Wednesday 26 September 2012

Fetch and parse JSON in iOS6


When you have finished this tutorial, you will know how to:
  • Asynchronously load a remote JSON feed from Internet
  • Easily handle invalid JSON data
  • Convert JSON into Objective-C objects
  • Use the JSON data in your UI
Let’s start!

Prepare the Xcode project and UI

Create a new Xcode project (using XCode 4.5 or newer) and select the Single View iOS project template. Make sure “Use Automatic Reference Counting” is checked.
From the project file list on the left side select MainStoryboard.storyboard. Drop in a UILabel and resize it to take most of the screen, like so:
While the label is selected, open up the properties inspector on the right and for the “Lines” property enter “0″. That will make the text you fill in the label wrap onto multiple lines.
Now open up ViewController.m and replace the empty @interface declaration of the top of the file with:
@interface ViewController ()
{
    IBOutlet UILabel* label;
}
@end
Now open up back MainStoryboard.storyboard and connect the outlet you just declared. Hold the “ctrl” key on your keyboard and drag with the mouse as on the schema below. Choose label from the menu that pops up when you release the mouse button:
OK, now your label is connected to your ViewController class, so you can go on with writing the Objective-C for your project.

Fetching JSON feed from Kiva.org

As in the original JSON tutorial on Touch Code Magazine in this tutorial you are going to fetch a JSON feed from Kiva.org – an organization that provides micro-funding to entrepreneurs in the 3rd world countries.
The JSON feed with the latest loans that need funding you can find at:http://api.kivaws.org/v1/loans/search.json?status=fundraising. You are going to load that feed and show human readable information about the latest loan.
The structure of the JSON feed you can examine on the screenshot below (taken in Charles– the single most valuable tool if you’re working on an app, which communicates via JSON, XML, or something similar)
So the JSON response’s top object is a dictionary, which has two keys “paging”(1) and “loans”(2). You will use the “loans” array – it contains a list of loan objects (3), and you can see the structure of a single loan object (4) – it has keys like name, country, use, sector, loan_amount, etc.

Working in the background

To do the heavy work network communication without blocking your app’s UI, you’ll need to execute the networking code in the background. Open up ViewController.m and at the end of the viewDidLoad method add:
//1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //code executed in the background
    //2
    NSData* kivaData = [NSData dataWithContentsOfURL:
                        [NSURL URLWithString:@"http://api.kivaws.org/v1/loans/search.json?status=fundraising"]
                        ];
    //3
    NSDictionary* json = nil;
    if (kivaData) {
      json = [NSJSONSerialization
      JSONObjectWithData:kivaData
      options:kNilOptions
      error:nil];
    }
 
    //4
    dispatch_async(dispatch_get_main_queue(), ^{
        //code executed on the main queue
        //5
        [self updateUIWithDictionary: json];
    });
 
});
I’ll go step by step to explain the code above:
  1. First you use dispatch_async to execute a block of code asynchronously in the background
  2. Inside the block you fetch the contents of the Kiva JSON API URL – you just create a new NSData object by calling dataWithContentsOfURL:
  3. Next you call NSJSONSerialization to convert the piece of data into objects (you don’t provide an error parameter – you’ll do error handling in a bit)
  4. Then you need to update the UI with the new data, therefore you need switch to execution on the main tread- another dispatch_async switches to the main thread
  5. Finally, on the main thread you call updateUIWithDictionary: method of the ViewController class (you will implement it in a second)
Did you notice that you actually already fetched and parsed the JSON feed? Yes, since iOS5 it’s actually that easy to grab data from a web server :)
But wait – in a second you will use the new iOS6 features to dig through the result with just few keystrokes!

Updating the app UI

Now let’s implement the updateUIWithDictionary: method. It takes one parameter in – you expect an NSDictionary, but it might be also nil if JSON feed is corrupt or unreachable. Paste in the initial code for the method:
@try {
  label.text = [NSString stringWithFormat:
    @"%@ from %@ needs %@ %@\nYou can help by contributing as little as 25$!",
    json[@"loans"][0][@"name"],
    json[@"loans"][0][@"location"][@"country"],
    json[@"loans"][0][@"loan_amount"],
    json[@"loans"][0][@"use"],
    nil];
}
The code is just one line – you create a new string with some placeholders, and you fill in the placeholders with text from the fetched loan data object. You wrap this operation in a try block – this way if the fetched data object doesn’t have the structure you expect, the program will fall into the catch block and you can do some error handling there (you add the catch block in a second).
Note how easy it is in iOS6 to drill through nested data objects. For example:
json[@"loans"][0][@"location"][@"country"]
Gets the “loans” key in the “json” dictionary, then casts it to an array and gets the very first element. This first element is than casted to a dictionary and you get the “location” key out of it and the value of this key is casted to a dictionary and you fetch the object of the “country” key.
Isn’t it actually easier to understand what’s going on when you see the code, than to have it spelled out in plain English :) ?
So by executing this line you set the text of the label to something like the one in the screenshot:
However you are not done just yet. At the end of the viewDidLoad method you need to also handle errors, add this code after the closing bracket of the catch block:
    @catch (NSException *exception) {
        [[[UIAlertView alloc] initWithTitle:@"Error"
                                    message:@"Could not parse the JSON feed."
                                   delegate:nil
                          cancelButtonTitle:@"Close"
                          otherButtonTitles: nil] show];
        NSLog(@"Exception: %@", exception);
    }
If there was any exception thrown while you were digging through the data you just show an alert to the user and dump the exception description in the console – that should suffice for our demo project!

And … that’s it! You know now how to fetch data from a remote server, parse incoming JSON and use it in your app.

Happy iCoding.

No comments:

Post a Comment