Tutorial: JSON Over HTTP On The iPhone

By
On March 19, 2009

This tutorial provides a step-by-step howto for consuming JSON web services from an iPhone app.

When you’ve finished the tutorial you’ll have created a simple, back-of-a-fortune-cookie inspired, “Lucky Numbers” app with a solitary label whose content will be updated to show a list of lucky numbers fetched from a JSON-over-HTTP web service.

Source/Github

The code for this tutorial is available in GitHub. To see the source for the finished project, clone the repository:

  1. Open a terminal and change to the directory where you want the code
  2. Clone the repo by typing git clone git://github.com/dcgrigsby/luckynumbers.git

iPhone JSON Library

This tutorial uses Stig Brautaset’s JSON library (version 2.2), which provides functionality for parsing and generating JSON. We won’t be using the JSON generating functionality in this tutorial.

The library provides two methods for parsing JSON: (1) a high-level category that extends NSString to include JSON parsing and (2) a more granular, slighly lower level object-based parser. We’ll start simple and use the former; we’ll switch to the latter near the end of the tutorial.

  1. Download the disk image

We’ll include the relevant bits into our project in a later step.

Creating The Project

Launch Xcode and create a new View-Based iPhone application called LuckyNumbers:

  1. Create a new project using File > New Project… from Xcode’s menu
  2. Select View-Based Application from the iPhone OS > Application section, click Choose…
  3. Name the project as LuckyNumbers and click Save

Adding JSON Support To The Project

In order to use the JSON functionality we’ll need to add it to the project:

  1. Expand the LuckyNumbers project branch in the Groups & Files panel of the project
  2. Using Finder, locate the JSON_2.2.dmg file you downloaded earlier and mount it by double clicking its icon. A new finder window with the DMG’s contents will open
  3. Drag and drop the JSON directory from the DMG onto the Classes folder under the LuckyNumbers project icon in the Groups & Files panel in Xcode

We’ll test that the JSON is set up properly by parsing a JSON dictionary string and using the resulting NSDictionary as a datasource for a message we’ll write to the console from our app’s viewDidLoad. This is throw-away code just to test that everything’s wired up properly.

Use this code for LuckyNumbersViewController.m (located in the project’s Classes Xcode folder):

 

#import "LuckyNumbersViewController.h"
#import "JSON/JSON.h"

@implementation LuckyNumbersViewController

- (void)viewDidLoad {
	[super viewDidLoad];
	NSString *jsonString = [NSString stringWithString:@"{"foo": "bar"}"];
	NSDictionary *dictionary = [jsonString JSONValue];
	NSLog(@"Dictionary value for "foo" is "%@"", [dictionary objectForKey:@"foo"]);
}

- (void)dealloc {
    [super dealloc];
}

@end

 

Build and run the project. If the JSON SDK is configured properly you should see an entry in the console that says, Dictionary value for “foo” is “bar”.

Setting Up The (Spartan) UI

Our finished app will need a UILabel to display the lucky numbers that we’ll eventually grab using HTTP and JSON.

Use this code for LuckyNumbersViewController.h (located in the project’s Classes Xcode folder):

 

#import <UIKit/UIKit.h>

@interface LuckyNumbersViewController : UIViewController {
	IBOutlet UILabel *label;
}

@end

 

IBOutlet is a macro that tells the compiler that it needs to wire up this variable to a corresponding UILabel element added WYSIWYG in Interface Builder. In the next step, we’ll add that element and connect the two pieces:

Edit the LuckyNumbersViewController.xib file in Interface Builder:

  1. Expand the Resources folder under the LuckyNumbers project branch in the Groups & Files panel.
  2. Double-click the LuckyNumbersViewController.xib file

Make sure that the Library, Inspector and View windows are open/visible. If they are not:

  1. Show the Library window using Tools > Library from the menu
  2. Show the Inspector window using Tools > Inspector from the menu
  3. Show the View by clicking the View icon on the LuckyNumbersViewController.xib window

Add the label:

  1. Locate the Label component in the Library window and drag it onto the view
  2. In the View window use the label’s handles to enlarge it to about half the size of the view
  3. In the Inspector window under View Attributes (the left-most tab), set the label’s number of lines to 0.

Setting the label’s number of lines to zero configures the label dynamically size the text to fit within its bounds.

Connect the Interface Builder label the code’s label. While still in Interface Builder:

  1. Control-click on File’s Owner icon in the LuckyNumbersViewController.xib window
  2. In the resulting pop-up menu, click-and-hold (i.e., don’t unclick) on the right-justified circle in the locationLabel row of the Outlets section
  3. Drag the mouse to the Label in the View. A blue line will connect the two.

Confirm that the two have been connected. The pop-up menu should look like the image on the right.

If everything looks right, save the changes and close Interface Builder.

Fetching JSON Over HTTP

We’ll use Cocoa’s NSURLConnection to issue an HTTP request and retrieve the JSON data.

Cocoa provides both synchronous and asynchronous options for making HTTP requests. Synchronous requests run from the application’s main runloop cause the app to halt while it waits for a response. Asynchronous requests use callbacks to avoid blocking and are straightforward to use. We’ll use asynchronous requests.

First thing we need to do is update our view controller’s interface to include an NSMutableData to hold the response data. We declare this in the interface (and not inside a method) because the response comes back serially in pieces that we stitch together rather than in a complete unit.

Update LuckNumbersViewController.h. Changes are in bold:

 

#import <UIKit/UIKit.h>

@interface LuckyNumbersViewController : UIViewController {
	IBOutlet UILabel *label;
	NSMutableData *responseData;
}

 

To keep things simple, we’ll kick off the HTTP request from viewDidLoad.

Replace the contents of LuckyNumbersViewController.m with:


#import "LuckyNumbersViewController.h"
#import "JSON/JSON.h"

@implementation LuckyNumbersViewController

- (void)viewDidLoad {
	[super viewDidLoad];

	responseData = [[NSMutableData data] retain];
	NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.unpossible.com/misc/lucky_numbers.json"]];
	[[NSURLConnection alloc] initWithRequest:request delegate:self];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
	[responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
	[responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
	label.text = [NSString stringWithFormat:@"Connection failed: %@", [error description]];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
	[connection release];
}

- (void)dealloc {
    [super dealloc];
}

@end

This mostly boilerplate code initializes the responseData variable to be ready to hold the data and kicks off the connection in viewDidload; it gathers the pieces as they come in in didReceiveData; and the empty connectionDidFinishLoading stands ready to do something with the results.

Using The JSON Data

Next, we’ll flesh out the connectionDidFinishLoading method to make use of the JSON data retrieved in the last step.

Update the connectionDidFinishLoading method in LuckyNumbersViewController.m. Use this code:

 

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
	[connection release];

	NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
	[responseData release];

	NSArray *luckyNumbers = [responseString JSONValue];

	NSMutableString *text = [NSMutableString stringWithString:@"Lucky numbers:n"];

	for (int i = 0; i < [luckyNumbers count]; i++)
		[text appendFormat:@"%@n", [luckyNumbers objectAtIndex:i]];

	label.text =  text;
}

 

Our earlier throw-away test code created an NSDictionary. Here it creates an NSArray. The parser is very flexible and returns objects — including nested objects — that appropriately match JSON datatypes to Objective-C datatypes.

Better Error Handling

Thus far, we’ve been using the the convenient, high-level extensions to NSString method of parsing JSON. We’ve done so with good reason: it’s handy to simple send the JSONValue message to a string to accessed to the parsed JSON values.

Unfortunately, using this method makes helpful error handling difficult. If the JSON parser fails for any reason it simply returns a nil value. However, if you watch your console log when this happens, you’ll see messages describing precisely what caused the parser to fail.

It’d be nice to be able to pass those error details along to the user. To do so, we’ll switch to the second, object-oriented method, that the JSON SDK supports.

Update the connectionDidFinishLoading method in LuckyNumbersViewController.m. Use this code:

 

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
	[connection release];

	NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
	[responseData release];

	NSError *error;
	SBJSON *json = [[SBJSON new] autorelease];
	NSArray *luckyNumbers = [json objectWithString:responseString error:&error];
	[responseString release];

	if (luckyNumbers == nil)
		label.text = [NSString stringWithFormat:@"JSON parsing failed: %@", [error localizedDescription]];
	else {
		NSMutableString *text = [NSMutableString stringWithString:@"Lucky numbers:n"];

		for (int i = 0; i < [luckyNumbers count]; i++)
			[text appendFormat:@"%@n", [luckyNumbers objectAtIndex:i]];

		label.text =  text;
	}
}

 

Using this method gives us a pointer to the error object of the underlying JSON parser that we can use for more useful error handling.

Conclusion

The JSON SDK and Cocoa’s built-in support for HTTP make adding JSON web services to iPhone apps straightforward.

Update: Ryan Daigle points out that using ObjectiveResource makes consuming Ruby on Rails back-ended JSON (or, for that matter, XML) web services almost effortless.

Please leave a comment if you encounter any errors, mistakes or typos. Thanks!

0 responses to “Tutorial: JSON Over HTTP On The iPhone”

  1. alex says:

    This is an awesome article. I’ve been building similar projects against XML APIs, but SBJSON looks easier than XML parsing.

  2. Steve says:

    Awesome article on something I was just preparing to research! thanks!

  3. Billy Gray says:

    Good tip, sir. I prefer the OOP means myself.

  4. izo says:

    thanks for that article, i’m a starting iphone dev and it was a good start !

  5. Ryan Daigle says:

    Don’t forget that ObjectiveResource supports both XML and JSON deserialization as well. It provides an easy way to remotely send and receive object data.

  6. Robbie says:

    Thanks, this was a great tutorial to get started. Kinda what I was hoping to find in an iPhone SDK book I bought. Think we might need an “iPhone SDK for web developers” book with more examples like this.

    Cheers

  7. sonny says:

    Nice example. I am still curious about how to send image data wrapped in an JSON object to a remote server. Would you provide an example along those lines as an addition?

    thanks!

  8. Biggaz says:

    This is a great sample. Very easy to follow and well thought out. Saw ObjectiveResource here and went to try it out. Pretty disappointing … has the right buzzwords but in the end, despite trying to simplify it … in the end the documentation is so poorly done and the samples so purely organized that it seemed like a bad joke …..

  9. This is a great example and has been really helpful. So thanks.

    In the sample code for LuckyNumbersViewController.m, however, I think I spotted a memory leak in the viewDidLoad method:

    [[NSURLConnection alloc] initWithRequest:request delegate:self];

  10. Ashutosh Tamrakar says:

    Thanks Dan for this nice tutorial,
    Its running fine on iPhone SDK 2.2.1, but i am struggling to run this code on iPhone SDK 3.0, getting the following errors

    Precompiling LuckyNumbers_Prefix.pch (3 errors)
    In file included from /Users/Mac/Desktop/WorkSpace/JSONTestApp/LuckyNumbers/LuckyNumbers_Prefix.pch
    In file included from /var/folders/6j/6jTbl8c-HbG0gN9itOO-V++++TI/-Caches-/com.apple.Xcode.501/CompositeSDKs/iphonesimulator-iPhoneSimulator.platform3.0-gqibsjkkszjuandlmzobwzbemkyi/System/Library/Frameworks/UIKit.framework/Headers/UIKit.h
    error: syntax error before ‘AT_NAME’ token
    error: syntax error before ‘}’ token
    fatal error: method definition not in @implementation context

    The error points to file UILocalizedIndexedCollation.h which is included in UIKit.h.

    Code:
    UIKIT_EXTERN interface UILocalizedIndexedCollation : NSObject
    {
    package
    NSLocale *_locale;
    NSArray *_sectionTitles;
    NSArray *_sectionStartStrings;
    NSArray *_sectionIndexTitles;
    NSArray *_sectionIndexMapping;
    }

    I would request you to please have a look on this issue.

    Your help is highly appreciated.

    Thanks

  11. mike says:

    i have the same 3.0 compile error – but cant seem to resolve the problem.

  12. mike says:

    ok it seems adding the JSON code to your project resolves the code signing AND the AT_NAME compile error for SDK 3.0
    I just followed the instruction here (reversing what I did in option2 and did option1)
    https://code.google.com/p/json-framework/wiki/InstallationInstructions
    *took me a while to remove “Additional SDKs” to finally get rid of the AT_NAME compile error.

  13. Dan Grigsby says:

    Updated the tutorial to work with 3.0 to fix the problems noted by Mike and Ashutosh. Fixed the memory leak noted by Sam.

  14. shashank garg says:

    Hi Dan,
    I am currently working on an i phone application……in this application i have to display my current location on map and then using the bounds[southwest(latitude,longitude),northeast(latitude,longitude) as request parameters to the sever(where the data is stored in a jason format) display markers on the map of all the locations that lie within those bounds.

    rite now i have hardcoded the the my url in the NSURLRequest. can you help me with this.

    I have coded for displaying a map and the current location marker.
    i have also used your code to get jason data in (NSArray *luckyNumbers).

    i guess i’l have to make use of HTTP post and GET. please help me with this.

  15. Andy says:

    Thanks Dan, this example was clear and worked great, thanks for any changes you might have made for 3.0. I tried to use ObjectiveResource for my project but had errors and found insufficient documentation to help me get rolling. My needs are just two requests for JSON data at this time and I have that functionality in place now.

  16. Rudi Farkas says:

    Hi Dan
    Thank you for the tutorial – short and to the point.

    I would like to get to the HTTP headers that accompanied the response to a query sent via NSURLRequest.

    For example, query “https://twitter.com/statuses/friends_timeline.json” returns the JSON data, but the HTTP headers contain additional info of interest, notably X-RateLimit-Remaining — https://apiwiki.twitter.com/Rate-limiting.

    Is there a way to get this info?

  17. Sergio Campamá says:

    It’s a very good tutorial, but you could simplify all the connection trouble by doing:

    NSString *jsonPath = [[NSString alloc] initWithString:@”https://json.path.in/web”];
    NSString *jsonFile = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:jsonPath]];

    and then

    NSArray *jsonData = [[NSArray alloc] initWithArray:[jsonFile JSONValue]];
    or
    NSDictionary *jsonData = [[NSDictionary alloc] initWithDictionary:[jsonFile JSONValue]];

    depending on how the json is formated… i currently use this, so i know it works…

    initWithContentsOfURL simplifies all the connection trouble and setting delegates and stuff… the JSON library throws exceptions when the json file was badly loaded, so with a try catch you can manage errors…

  18. Dan Grigsby says:

    Sergio: that method blocks, halting the ui while you wait for the connection. The method I used does not freeze the ui.

  19. Sergio Campamá says:

    Ahh… never thought of that… as my app only displays the received data, its of no use for me to display the view before the json loads… anyway, i didnt know how to do asynchronous connections before, but i do now… thanks!

  20. Dan Grigsby says:

    Rudi:

    The headers for an HTTP connection are included in the NSHTTPURLResponse. The allHeaderFields message returns a dictionary of the headers.

    If you were doing a synchronous request it’s easy to populate an NSHTTPURLResponse:

    NSURLRequest *request = [NSURLRequest requestWithURL: url];
    NSHTTPURLResponse *response;
    [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: nil];

    With an asynchronous request you have to do a little more work. When connection:didReceiveResponse: is called, it is passed (as the second parameter) an NSURLResponse. You can cast it as an NSHTTPUrlResponse like so:

    – (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
    NSLog([[httpResponse allHeaderFields] description]);
    //…

  21. Dan Grigsby says:

    Turned my answer to Rudi’s question about accessing HTTP responses into a short blog post:

    https://www.mobileorchard.com/accessing-http-headers-from-an-nsurlrequest/

  22. ?AK?HE? says:

    ??????? ????? ?????? ???, ?? ? ?????? ??????? ?? ????????. ?????????? ??????????, ??? ??? ???????. 🙂

  23. fernando cañono says:

    thank you great tutorial!!

  24. orion elenzil says:

    short and sweet, thanks for a solid tutorial.

  25. Excellent tutorial. Thanks for such an excellent masterpiece.

  26. Philip Rousselle says:

    This is a great tutorial and very useful software. Could anyone point me to an example of exactly how they handled the copyright information provided on the App Store submission for an app that used this utility? I’m about to submit my first app and I don’t want to mess it up. Thanks.

  27. Thanks, the only tutorial I could find on the subject. Got it working in my app easily. Glad it is so easy to parse JSON on the iPhonel

  28. Chris Felix says:

    Here is a good article for those of you who are developing enterprise iPhone applications and use IBM WebSphere Process Server:

    https://www.ibm.com/developerworks/websphere/tutorials/1001_felix/index.html

  29. Vivek Tamrakar says:

    Thanks , nice stuff

  30. Casey says:

    I think you are leaking some memory. According to Apple’s doc for NSURLConnection, you should be releasing the connection in both the connectionDidFinishLoading and connectionDidFailWithError methods.
    [connection release];
    You only release it if the connection succeeds.

  31. Casey says:

    You also need to release responseData if the connection fails

  32. Casey says:

    responseString also should be released after its used…

  33. Anjani says:

    Thank You for tutorial. It was very nice for getting started.

  34. Ankur Patel says:

    It’s very simple and good for understanding JSON parsing tutorial for new bee like me.
    Thank you so much.

  35. ava says:

    Hi!

    Im trying to put a simple twitter search option (so no sign-in) in my iphone
    application, but don’t understand how to do so. All the resources seem to be for webbased applications.. Can you please help me? Thanks!