iOS Programming · · 14 min read

How To Fetch and Parse JSON Using iOS SDK

How To Fetch and Parse JSON Using iOS SDK

Editor’s Note: This week, Ziad Tamim will show you how to fetch and parse JSON. In this tutorial, we will be focusing on adding JSON support to your iOS apps by demonstrating how to create a simple app using Meetup API. The tutorial may be a bit complicated than other tutorials we covered before. You’re expected to have basic knowledge of JSON and design patterns.

Enter the JSON programming tutorial.

First, what’s JSON? JSON (short for JavaScript Object Notation) is a text-based, lightweight and easy way for storing and exchanging data. It’s commonly used for representing structural data and data interchange in client-server applications, serving as an alternative to XML. A lot of the services we use everyday have JSON-based APIs. Most of the iOS apps including Twitter, Facebook and Flick send data to their backend web services in JSON format.

json intro tutorial

As an example, a JSON representation of a Movie object may look like this

{
    "title": "The Amazing Spider-man",
    "release_date": "03/07/2012",
    "director": "Marc Webb",
    "cast": [
        {
            "name": "Andrew Garfield",
            "character": "Peter Parker"
        },
        {
            "name": "Emma Stone",
            "character": "Gwen Stacy"
        },
        {
            "name": "Rhys Ifans",
            "character": "Dr. Curt Connors"
        }
    ]
}

As you can see, the JSON data is human-readable and easier to parse than XML. If you still have no idea about JSON, check out JSON Guide to learn more about it.

Since the release of iOS 5, the iOS SDK makes it easy to fetch and parse JSON data. In this tutorial, we’ll demonstrate how to use the built-in API to query a JSON-based API from Meetup and deal with the returned data appropriately.

Take a Look at Our App

Before we move onto the code, let’s take a look at what we’ll build.

In this tutorial, we’ll create a simple app called BrowseMeetup that consumes Meetup’s public API. If you haven’t heard of Meetup, it is the world’s largest network of local groups. You’re free to use Meetup to organize a local group or find one of the thousands already meeting up face-to-face. Like other social networks, it provides open API for accessing its data from your own apps.

The BrowseMeetup app will consume the web service of Meetup to look up for groups nearby. The app will get the current location and then automatically load the nearby Meetup groups.

BrowseMeetup Demo App

Note: This app utilizes Core Location framework. If you don’t have any idea about it, check out our tutorial about How To Get the User Location in iPhone App.

Getting Started

It’s time to set up the project to manage the BrowseMeetup application. Launch Xcode and create a new project based on the Master-Detail Application iOS app template. In the project options, you’ll need to check the Storyboard and Automatic Reference Counting boxes. This app will provide only iPhone Views, so choose iPhone for the device family and save the project. Delete the DetailsViewController and in the storyboard, design the user interface similar to the one below:

BrowseMeetup Storyboard

Storyboards of BrowseMeetup App

The focus of this tutorial is on JSON fetching and parsing. So to save your time from setting up the project, you can download this project template to start with. The template already pre-built the user interface and implemented the Core Location for you.

Tip: If you have no idea about table view and wonder how the navigation bar works, check out our tutorials in the free iOS course.

Working with Meetup APIs

Before you can use Meetup APIs, first create a new account on Meetup. Start by creating a new account by going to APIs Doc and clicking on the button “Request to join this Meetup group”, then fill the necessary information and hit “Sign Up”, then go step by step until its done.

Screen Shot 2013-08-26 at 10.06.27 PM

We’ll use one of the Meetup APIs (i.e. https://api.meetup.com/2/groups) for fetching the Meetup groups held at a certain location. The call allows developers to specify the location by using latitude and longitude. You can test the call by using the API console.

Here is a sample JSON response for this request (https://api.meetup.com/2/groups?&sign=true&lat=51.509980&lon=-0.133700&page=1):

{
"results": [
{
"lon": -0.10000000149011612,
"visibility": "public",
"organizer": {
"name": "William Brown",
"member_id": 3817216
},
"link": "http://www.meetup.com/french-32/",
"state": "17",
"join_mode": "approval",
"who": "LFM members",
"country": "GB",
"city": "London",
"id": 63974,
"category": {
"id": 16,
"name": "language/ethnic identity",
"shortname": "language"
},
"topics": [
{
"id": 185,
"urlkey": "french",
"name": "French Language"
},
{
"id": 264,
"urlkey": "friends",
"name": "Friends"
},
{
"id": 3304,
"urlkey": "expatfrench",
"name": "Expat French"
}
],
"timezone": "Europe/London",
"group_photo": {
"photo_link": "http://photos3.meetupstatic.com/photos/event/7/4/a/b/600_929867.jpeg",
"highres_link": "http://photos3.meetupstatic.com/photos/event/7/4/a/b/highres_929867.jpeg",
"thumb_link": "http://photos3.meetupstatic.com/photos/event/7/4/a/b/thumb_929867.jpeg",
"photo_id": 929867
},
"created": 1034097734000,
"description": "The London French Meetup is the biggest group of French speakers of all ages and nationalities in London. We hold regular events to meet up, talk in French and share interests in French culture whilst having a good time.
We have two main events per month where we have the whole of the upstairs of a pub.
In addition, we organise other regular events such as outings to: restaurants, trendy bars, french films, live music, sports related activities, outdoor events and more...
The organising team is made of volunteers from different nationalities and ages. Our members are made up of: 1/3 French nationals, 1/3 British nationals and 1/3 other nationalities and francophone countries. If you have any ideas or suggestions for events or would like to help please let us know.
A bientôt.
LFM Team.

", "name": "London French Meetup", "rating": 4.37, "urlname": "french-32", "lat": 51.52000045776367, "members": 4889 } ], "meta": { "lon": -0.1337, "count": 1, "signed_url": "http://api.meetup.com/2/groups?radius=25.0&order=id&desc=false&offset=0&format=json&lat=51.50998&page=1&fields=&lon=-0.1337&sig_id=109020062&sig=4532ed8f987f940748ebfba0f483a26f756dcba3", "link": "http://www.meetup.com/2/groups", "next": "http://www.meetup.com/2/groups?radius=25.0&order=id&format=json&lat=51.50998&page=1&desc=false&offset=1&fields=&sign=true&lon=-0.1337", "total_count": 4501, "url": "http://www.meetup.com/2/groups?radius=25.0&order=id&format=json&lat=51.50998&page=1&desc=false&offset=0&fields=&sign=true&lon=-0.1337", "id": "", "title": "Meetup Groups v2", "updated": 1377876449000, "description": """", "method": "Groups", "lat": 51.50998 } }

Application Design and How It Works

As mentioned, Meetup API provides a method for requesting groups in a certain location. The response data will be sent in JSON format. We’ll need an object that can retrieve the data and construct our domain object from the encoded data. Below is an overview of the application design showing how the classes are structured and how they work together to get the meetup groups:

BrowseMeetup - Design Overview

BrowseMeetup – Design Overview

It may be a bit complicated for some of you. But let me give you a brief walkthrough. We create a MeetupManager, in which it will request for Meetup groups for a particular location. Here the MeetupManager acts as a facade. If you haven’t heard of the Facade design pattern, you can think of it as a coordinator of other classes. The Facade tries to provide a simplified interface for the view controller and shields it from the underlying implementation.

The MeetupCommunicator class is used for communicating with the Meetup API. Once Meetup responds with the JSON-formatted response, we’ll pass it to GroupBuilder that constructs the Group object.

The MasterViewController uses Core Location to figure out the current location and informs the MeetupManager to get the Meetup groups for that location. The MeetupManager coordinates with other classes to retrieve the groups. Once the groups are retrieved, it communicates with the MasterViewController via the delegate and pass the groups found. The MasterViewController then presents the groups in the table view.

Creating JSON Data Model

We are going to start by implementing the model layer. The Group class represents the group information in the BrowseMeetup app and is used to store the group information returned by Meetup. Here is a sample JSON response of a Group object:

{
     lon: -71.12999725341797,
     visibility: "public",
     organizer: {
          name: "Emma",
          member_id: 2161382
     },
     link: "http://www.meetup.com/bloggers/",
     state: "MA",
     join_mode: "closed",
     who: "Bloggers",
     country: "US",
     city: "Cambridge",
     id: 21458,
    category: {
          id: 34,
          name: "tech",
          shortname: "tech"
     },
     topics: [
          {
               id: 198,
               urlkey: "blog",
               name: "Blog"
          },
          {
               id: 772,
               urlkey: "writers",
               name: "Writers"
          }
     ],
     timezone: "US/Eastern",
     group_photo: {
     photo_link: "http://photos3.meetupstatic.com/photos/event/6/4/9/5/600_25749.jpeg",
     highres_link: "http://photos3.meetupstatic.com/photos/event/6/4/9/5/highres_25749.jpeg",
     thumb_link: "http://photos3.meetupstatic.com/photos/event/6/4/9/5/thumb_25749.jpeg",
     photo_id: 25749
     },
     created: 1034097731000,
     description: "This is a group for people that are interested in blogging and meeting others who are interested in blogging. Topics discussed range from blog content to blog software. All interest levels are welcome.",
     name: "The Greater Boston Area Weblogger Meetup Group",
     rating: 3.33,
     urlname: "bloggers",
     lat: 42.38999938964844,
     members: 119
}

The above JSON response represents a single Meetup Group. We’ll not use all the group data returned. But simply use the “name”, “description”, “who”, “country” and “city” fields. These fields are just good enough for presenting in the table view of our app. Now create a new file using the Objective-C class template. Name it Group, make it a subclass of NSObject and add the following code in the header file:

@interface Group : NSObject
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *description;
@property (strong, nonatomic) NSString *who;
@property (strong, nonatomic) NSString *country;
@property (strong, nonatomic) NSString *city;
@end

These properties are the information that we will use in our app to achieve the final result depicted earlier.

Fetching JSON Data Using Meetup API

First create a new file using Objective-C protocol template and name it as MeetupCommunicatorDelegate. Fill it with the below code:

@protocol MeetupCommunicatorDelegate 
- (void)receivedGroupsJSON:(NSData *)objectNotation;
- (void)fetchingGroupsFailedWithError:(NSError *)error;
@end

The MeetupCommunicator class is responsible for the communication with the Meetup APIs and fetching the JSON data. It relies on the delegate of MeetupCommunicatorDelegate to handle the parsing of JSON data. The communicator has no idea how the JSON data is handled. Its focus is only on creating connection to the Meetup APIs and fetching the raw JSON result.

With the delegate created, create another class file and name it as MeetupCommunicator. Open the header file and put in the below code:

#import 

@protocol MeetupCommunicatorDelegate;

@interface MeetupCommunicator : NSObject
@property (weak, nonatomic) id delegate;

- (void)searchGroupsAtCoordinate:(CLLocationCoordinate2D)coordinate;
@end

We are creating a property to keep track of the communicator delegate, then defining a method for searching Meetup groups in a certain location. Next, open the MeetupCommunicator.m and place the code below:

#import "MeetupCommunicator.h"
#import "MeetupCommunicatorDelegate.h"

#define API_KEY @"1f5718c16a7fb3a5452f45193232"
#define PAGE_COUNT 20

@implementation MeetupCommunicator

- (void)searchGroupsAtCoordinate:(CLLocationCoordinate2D)coordinate
{
    NSString *urlAsString = [NSString stringWithFormat:@"https://api.meetup.com/2/groups?lat=%f&lon=%f&page=%d&key=%@", coordinate.latitude, coordinate.longitude, PAGE_COUNT, API_KEY];
    NSURL *url = [[NSURL alloc] initWithString:urlAsString];
    NSLog(@"%@", urlAsString);
    
    [NSURLConnection sendAsynchronousRequest:[[NSURLRequest alloc] initWithURL:url] queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        
        if (error) {
            [self.delegate fetchingGroupsFailedWithError:error];
        } else {
            [self.delegate receivedGroupsJSON:data];
        }
    }];
}

@end

The Meetup API needs a key to work. If you’re registered an account, you need to get one by going to API Key page. Simply click on the lock icon beside the textfield to reveal the key. Copy the text inside it and replace the value of the macro API_KEY with your own API key.

As mentioned in the earlier section, we use the following Meetup API for searching group in a specific location. The API accepts the location in the form of latitude and longitude. Here is a sample Meetup URL to connect:

https://api.meetup.com/2/groups?lat=51.509980&lon=-0.133700&page=20&key=1f5718c16a7fb3a5452f45193232

In the implementation of the method, we first construct the Meetup API URL with the specified latitude, longitude, the number of groups and the API key. In order not to blocking the UI, we load the data for a URL request asynchronously by using the “sendAsynchronousRequest:” method of NSURLConnection. Finally, when the JSON data is retrieved, it passes the data to the delegate for further processing.

Parsing JSON Data and Building Group Objects

When MeetupManager receives the data in JSON format, we use the class method of GroupBuilder to convert the data into Group objects. Create a new file with Objective-C class template and name it as GroupBuilder. Open the header file and paste the following code:

#import 

@interface GroupBuilder : NSObject

+ (NSArray *)groupsFromJSON:(NSData *)objectNotation error:(NSError **)error;

@end

Next, open “GroupBuilder.m” and implement the method with the following code:

#import "GroupBuilder.h"
#import "Group.h"

@implementation GroupBuilder
+ (NSArray *)groupsFromJSON:(NSData *)objectNotation error:(NSError **)error
{
    NSError *localError = nil;
    NSDictionary *parsedObject = [NSJSONSerialization JSONObjectWithData:objectNotation options:0 error:&localError];
    
    if (localError != nil) {
        *error = localError;
        return nil;
    }
    
    NSMutableArray *groups = [[NSMutableArray alloc] init];
    
    NSArray *results = [parsedObject valueForKey:@"results"];
    NSLog(@"Count %d", results.count);
    
    for (NSDictionary *groupDic in results) {
        Group *group = [[Group alloc] init];
        
        for (NSString *key in groupDic) {
            if ([group respondsToSelector:NSSelectorFromString(key)]) {
                [group setValue:[groupDic valueForKey:key] forKey:key];
            }
        }
        
        [groups addObject:group];
    }
    
    return groups;
}
@end

The “groupsFromJSON:” method is designed to convert raw JSON data into an array of Group objects. Since the release of iOS 5, the iOS SDK comes with a class called NSJSONSerialization for parsing JSON data. Developers can use the class to convert JSON to Foundation objects or convert Foundation objects back to JSON.

When reading JSON data using NSJSONSerialization, all the keyed lists are automatically turned into NSDictionary objects. For array, it’s converted into NSArray instances. Any strings encountered along with the names of named items in keyed lists are converted into NSString, while purely numeric strings are converted into NSNumber objects. Lastly, any values of null are represented using NSNull.

Referring the sample response we showed you in earlier section, the Meetup API returns a JSON response with two main parts – results and meta. We’ll just need the “results” part. The code is very straightforward. We loop through the results and look into each NSDictionary inside it. We then create a Group object and fill it with the necessary information, then adding it to the mutable array.

Putting the Pieces Together with MeetupManager

Now you should know how to work with JSON, parse the data and convert it into objects. Next, we’ll implement the MeetupManager which serves as a coordinator of the underlying classes.

First, create a new file with the Objective-C protocol template and name it as MeetupManagerDelegate. Add the code below in the MeetupManagerDelegate.h:

@protocol MeetupManagerDelegate
- (void)didReceiveGroups:(NSArray *)groups;
- (void)fetchingGroupsFailedWithError:(NSError *)error;
@end

This delegate declares two methods and will be called by MeetupManager when the groups become available. The first method is called when the list of groups retrieved from Meetup is parsed, while the second method is invoked when an error occurred. The MeetupManagerDelegate will be implemented by MasterViewController that we’ll discuss in later section.

Next, create a new file with the Objective-C class template and name it MeetupManager. Then open the header file and add the code below:

#import 
#import 

#import "MeetupManagerDelegate.h"
#import "MeetupCommunicatorDelegate.h"

@class MeetupCommunicator;

@interface MeetupManager : NSObject
@property (strong, nonatomic) MeetupCommunicator *communicator;
@property (weak, nonatomic) id delegate;

- (void)fetchGroupsAtCoordinate:(CLLocationCoordinate2D)coordinate;
@end

As said, the MeetupManager acts as a façade. The app’s controller can work with the model object (i.e. Group) created without knowing any details about the network connection, JSON fetching/parsing or how the group objects are created. What the controller just needs to know is to use the “fetchGroupsAtCoordinate:” method for fetching Meetup groups.

We set up a property to keep an instance of our communicator which we will cover later and the other to keep track of the MeetupManagerDelegate. The “fetchGroupsAtCoordinate:” method will be used by the controller to fetch groups.

Next, open MeetupManager.m and place this code:

#import "GroupBuilder.h"
#import "MeetupCommunicator.h"

@implementation MeetupManager
- (void)fetchGroupsAtCoordinate:(CLLocationCoordinate2D)coordinate
{
    [self.communicator searchGroupsAtCoordinate:coordinate];
}

#pragma mark - MeetupCommunicatorDelegate

- (void)receivedGroupsJSON:(NSData *)objectNotation
{
    NSError *error = nil;
    NSArray *groups = [GroupBuilder groupsFromJSON:objectNotation error:&error];
    
    if (error != nil) {
        [self.delegate fetchingGroupsFailedWithError:error];
        
    } else {
        [self.delegate didReceiveGroups:groups];
    }
}

- (void)fetchingGroupsFailedWithError:(NSError *)error
{
    [self.delegate fetchingGroupsFailedWithError:error];
}

Here we implement the method fetchGroupsAtCoordinate:coordinate to fetch the groups in a certain area using the searchGroupsAtCoordinate:coordinate method of the communicator. We also implement the methods of MeetupCommunicatorDelegate for handling the JSON-formatted results as retrieved by the communicator.

The code inside the first method of the protocol receivedGroupsJSON:objectNotation uses the class method of GroupBuilder to convert the JSON result into Group objects and then informs its delegate with the Group objects. If there is any problem while processing the request, we invoke the other method (i.e. fetchingGroupsFailedWithError:) of the delegate to notify the controller that a problem is occurred.

Displaying the List of Groups

The first thing to do is hooking all these classes together so the MeetupManager can work. Open the MasterViewController.m, import the required header files, update the interface and declare the MeetupManager instance as follows:

#import "Group.h"
#import "MeetupManager.h"
#import "MeetupCommunicator.h"

@interface MasterViewController ()  {
    NSArray *_groups;
    MeetupManager *_manager;
}

Later we’ll implement the methods as defined in MeetupManagerDelegate. But first let us instantiate the MeetupManager in the method of viewDidLoad:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    _manager = [[MeetupManager alloc] init];
    _manager.communicator = [[MeetupCommunicator alloc] init];
    _manager.communicator.delegate = _manager;
    _manager.delegate = self;
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(startFetchingGroups:)
                                                 name:@"kCLAuthorizationStatusAuthorized"
                                               object:nil];
}

We are instantiating a new manager, then fill its communicator property with an new instance, and finally making the current view controller to keep track of any changes. The observer here captures the response of the user when the alert shows up to enable location services to call the method startFetchingGroups: to start fetching groups from server.

Next, open the “MasterViewController.m” and put the below to start fetching:

- (void)startFetchingGroups:(NSNotification *)notification
{
    [_manager fetchGroupsAtCoordinate:self.locationManager.location.coordinate];
}

As the view controller should conform to the MeetupManagerDelegate protocol. Implement the methods as follows:

- (void)didReceiveGroups:(NSArray *)groups
{
    _groups = groups;
    [self.tableView reloadData];
}

- (void)fetchingGroupsFailedWithError:(NSError *)error
{
    NSLog(@"Error %@; %@", error, [error localizedDescription]);
}

The “didReceiveGroups:” method will be called when the Meetup groups is grabbed from the Meetup server with an array instance returned. Here we simply reload the table view to display the new data.

Lastly, set up the methods of the table view by pasting the code below:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _groups.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    DetailCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    Group *group = _groups[indexPath.row];
    [cell.nameLabel setText:group.name];
    [cell.whoLabel setText:group.who];
    [cell.locationLabel setText:[NSString stringWithFormat:@"%@, %@", group.city, group.country]];
    [cell.descriptionLabel setText:group.description];
    
    return cell;
}

The above code is very straightforward. We retrieve the groups from the array and fill the table cell with the group information.

Compile and Run

Now it’s time to test the app. Compile and run it in the Simulator (or a real iOS device if you’ve registered the iOS developer program). Setup the location of the Simulator to London, UK. You should get the Meetup groups similar to the below screenshot:

BrowseMeetup London

Tip: If you have no idea about testing location in iPhone Simulator, check out the Core Location tutorial.

Summary

We have covered in this tutorial many aspects of programming by adopting the right design pattern to work with JSON data. You should now have a hands-on experience on fetching and parsing JSON data.

The tutorial may be a bit difficult if you’re new to design pattern. However, this shouldn’t scare you away from using JSON in your app. The iOS SDK makes it so easy for your apps to work with JSON. In summary, you simply create a URL connection, fetch the JSON data and parse it by using the built-in NSJSONSerialization class.

You can find various free APIs (such as Kiva and TMDb) on the Internet. As an exercise, try to pick another API and develop another app for it. Say, you can develop a simple app and display a list of the most recent fundraising loans using Kiva API.

For your complete reference, you can download the full source code of the Xcode project here.

As always, please leave us comment and share your thought about the tutorial.

Read next