Parsing JSON on iOS with ASIHTTPRequest and SBJson

I know the title is a mouthful, but this tutorial is going to be chock-full-of information on how to make a URL request with ASIHTTPRequest (ASI for short) you’ll see how simple ASI makes it to make a URL Request on iOS. We’re going to download some JSON from COLOURlovers and then parse it with SBJson. Finally, I’m going to show you a few methods for caching our data for offline storage. I personally include these two frameworks in all my projects because they streamline necessary tasks of iOS development. Without further a-due, let’s get started.

Getting Started

note:“JSONAppViewController.h” and “JSONAppViewController.m” will refer to the header and implementation class files you wish to make your URL Request in

First things first. Open Xcode and create a new, Navigation-based iOS application. We’re not going to use Core data for storage, so you can go ahead and uncheck that box if it’s checked. Once you give your project any name you want and click ‘Create’, we’re ready too move on to the next step. Make sure you create a navigation-based iPhone-Only app only.

Now you’re going to need to download SBJson and ASIHTTPRequest. You can download ASIHTTPRequest (ASI for short) and SBJson here. Once you have those downloaded, go ahead and unzip the download and add all those the files to your project. You’ll also need to add the frameworks below to your project in order for ASI to work properly:

CFNetwork

SystemConfiguration

MobileCoreServices

CoreGraphics

libz1.2.3.dylib

note: If your app exclusively supports iOS 4 and up you don’t need to include libz1.2.3.dylib. If your app supports iOS 4 and you also need import libz1.2.5.dylib

Next, let’s not forget to import “ASIHTTPRequest.h” and “JSON.h” into “JSONAppViewController.m”. It would look like this (make sure you import these two files at the very top the file):

12

#import "ASIHTTPRequest.h"
#import "JSON.h"

Lastly, we need to become The request delegate for all of the URL Requests that we make. Becoming the ASIHTTPRequestDelegate will allow us to get
useful notifications such as requestFinished: and requestFailed: . In order to get such notifications we have to make “JSONAppViewController.h” conform the ASIHTTPRequest Delegate, here’s how we do that.

1234567

/* Go into the top of your .h file where you see @interface JSONAppViewController : UIViewController {
and right before the opening curly bracket put this: */

&lt;ASIHTTPRequestDelegate&gt;

/* Now the top of your JSONAppViewController.h should look something like this */
@interface JSONAppViewController : UIViewController &lt;ASIHTTPRequestDelegate&gt; {

note: the < and > should be replaced with the less than and greater than signs respectively when writing your code. WordPress is just messing things up.

That’s it! We’re done with the set up! Now on to the fun stuff.

URL Requests with ASIHTTPRequest (ASI)

I’m only going to be teaching you how to make asynchronous requests because it’s best practice. I recommend using asynchronous requests as much as possible. As they allow your app to execute multiple tasks at once and prevent your app from becoming unresponsive. Synchronous, requests on the other hand can make your app unresponsive quickly. The user interface of your application will quickly freeze up, which could lead to your app getting rejected by the App Store.

So let’s get started! First, ASI needs to know two things in order to make our request for us, the URL for the request and the who should become the request delegate.

Making a request

123456789101112

// The url to make the request to
NSURL *colorURL = [NSURL URLWithString:@"http://www.colourlovers.com/api/colors?format=json"];

Now we have made our request, told ASI where to make that request too, set our request’s delegate and fired off our request. However, we haven’t captured the output of our request yet, were going to need to do that in order to download the JSON we get as a response from COLOURlovers.

Capturing the result

The way to capture the output of our URL request is too implement the requestFinished: method in our “JSONAppViewController.m”. This method will only be called when our request finishes but only if our request has a delegate (which it does) Now let’s implement the requestFinished: method in our “JSONAppViewController.m”, this is what it would look like:

1234567891011

-(void) requestFinished: (ASIHTTPRequest *) request {

// [request responseString]; is how we capture textual output like HTML or JSON
// [request responseData]; is how we capture binary output like images
// Then to create an image from the response we might do something like
// UIImage *image = [[UIImage alloc] initWithData:[request responseData]];
NSString *theJSON = [request responseString];

// Now we have successfully captured the JSON ouptut of our request

}

Parsing the JSON

Our NSString instance “theJSON” contains some unparsed JSON which we got as a output of our URL Request. Let’s parse it now. Put this this code at the bottom of the requestFinished: method before the closing curly bracket:

/* Since I'm parsing JSON from an API, after it's parsed it'll come out in the form
of an NSMutableDictionary with NSMutableArrays inside it.
Just a side note...if the resulting JSON looks like this {"one","two","three"}
then after the JSON has been parsed it will come out as an NSMutableArray.
If the JSON looks like this [{id:1,two:"three"},{id:2, two:"four"}] the after it has
been parsed it will be in the form of an NSMutableDictionary
(just like the JSON most APIs output) */

/*If you take a look at http://www.colourlovers.com/api/colors?format=json in
your browser you'll notice that each color has a title, an id and a few other things
I'm in interested in the color's title. When SBJson parsed the JSON
it put all the color's titles in one NSMutableArray
this is how I extract the color titles from that NSMutableDictionary we just
got after we finished parsing our JSON */

/* We have two options available to parse our the json returned for each element returned by the
COLOURlovers API (For example, title, description, apiURL etc.). The first is valueForKey:
(which we are using to parse our color titles). And the second option is objectForKey: .

Use this general rule when deciding whether to use objectForKey: or valueForKey: .
If the data stored in the item is anything other than a simple string (e.g. NSMutableArray, NSMutableDictionary)
than use objectForKey: . However if the data stored in the item is a simple string use valueForKey: .

Example:

Here's a snippet of the JSON returned by COLOURlovers:

{
...
title: "One"
...
}

In this case the data stored in the item of interest (or title) is just a simple string so when to parse it we'll
use valueForKey:

Example 2:

Here's another snippet of the JSON returned by the COLOURlovers API

{
...
"rgb" : {
"red" : 0,
"green" : 0,
"blue" : 0
}
...
}

When the rgb item is parsed we're going to use objectForKey: because the data in that item is
something other than a simple string, its actually an NSMutableDictionary. When you parse the
values of "red","green" and "blue" the the type of the data in those items will be an NSNumber
because the numbers don't have quotes around around them.
*/

NSMutableArray *colorTitles = [jsonDictionary valueForKey:@"title"];

/*
If you want to use this array to persist across methods you might want to
declare "colorTitles" as an NSMutableArray in the "JSONAppViewController.h"
file and you might even want to create a property in the .h file
and synthesize that property in the .m file
extract other information such as the color's id or the number of views it
has the same way, it will most likely be an NSMutableArray */
*/

Caching our data for offline storage

We can’t always count on our users having an Internet connection. Some devices like the iPod Touch and some iPads depend solely on Wi-Fi for their Internet connection. We have to accept the reality that users won’t always be in a Wi-Fi hotspot or have an active Internet connection all the time. So what we have to do is save the data we got from our last URL request and put it in a cache for offline storage so we can access that data when users go offline.

Using NSUserDefaults as a Cache

First, I’m going to show you how to use NSUserDefaults as an offline cache. NSUserDefaults is a handy little class Apple made for storing small tid-bits of data (like the color titles we parsed above)

Writing to NSUserDefaults

Put this code at the bottom of the requestFinished: method (before the closing curly bracket).

note: For those of you who have tried this tutorial and couldn’t get the NSUserDefaults to save, put [titleStorage synchronize]; after this line – [titleStorage setObject:colorTitles forKey:@”COLOR_TITLES”]; . Synchnronizing the NSUserDefaults is a critical part of saving our changes to it. Sorry, I just realized I had forgotten the [titleStorage synchronize]; call.

12345678910111213

/* The quickest, and fastest way to store the color titles for offline storage
is to store them in an NSMutableArray in the NSUserDefaults. A class Apple made to store
quick bits of data in (just like our titles).
This is how we do that */

NSUserDefaults *titleStorage = [NSUserDefaults standardUserDefaults];

/* We could cache other data such as the colors id or the number of view in the same way.
You could also store the NSDictionary we got as a result of parsing our JSON
just like this in NSUSERDefaults */
[titleStorage setObject:colorTitles forKey:@"COLOR_TITLES"];
/* IMPORTANT! Without this step it won't save */
[titleStorage synchronize];

Reading from NSUserDefaults

Now we have captured a cache of our color titles in NSUserDefaults for offline storage. You can capture caches of other data such as a color’s id, the number of views the color has, and other similar pieces of data the same way. Now, let’s read the contents of the “COLOR_TITLES” key in NSUserDefaults.

12

// Just like this
NSMutableArray *colorsTitles = [titleStorage objectForKey:@"COLOR_TITLES"];

Using .plist Files as a Cache

The last thing I want to show you how to do is take that NSDictionary we got after we parsed our JSON and save it in a .plist file in the iPhone’s document directory, which is a little more complicated. So let’s me show you how to do that now.

Writing an NSDictionary or NSMutableDictionary to a .plist

What I’m going to do is create a helper method to help us find the path to the iPhone’s documents directory on-the-fly. First we have to declare the helper method in our .h file, so go into your “JSONAppViewController.h” file and write the following (make sure to put this method declaration at the end of “JSONAppViewController.h”:

1

-(NSString *) documentsPath;

Alright, now that we’ve declared our helper method in our .h file, let’s create the body of our method, put this just before the the dealloc method (It really doesn’t matter where you put this method, as long as its in “JSONAppViewController.m” after the viewDidLoad: method). This what our method is going to look like:

Now that we’ve finished creating our helper method, let’s put it to work. Now it’s easier to create .plist files from an NSDictionary or an NSMutableDictionary. Just put this code at the bottom of our requestFinished: method:

Checking For the .plist File’s Existence (on the iPhone Simulator)

That’s it, that’s all the code required to take an NSDictionary or NSMutableDictionary and write it to a .plist file. The iPhone Simulator has the wonderful ability of letting us see the files we put in our app’s documents directory. We’re going to use this feature to see if our .plist file was created successfully.

First, open Finder then press ⌘+⇧+G. Then type this:

1

~/Library/Application Support/iPhone Simulator

Now, look for the folder that has the same name as the version of iOS we made our app for and go inside of it (for example, I am running the iOS 5 beta 7 SDK so I would look for a folder named ‘5.0’). Then go to the ‘Applications’ folder. What you see here are folders with the bundle identifiers Xcode assigns your app when it’s created. What you have to do is find the folder that has your app’s bundle identifier (the best way to do this is trial and error). You’ll know when you’ve found the right folder when there is an item in that folder with name you gave your app in Xcode (for example, if called my project ‘JSONApp’ I would look for a file with the name ‘JSONApp’ . If I then found one then I would know that I’m in the right folder). Now go into the ‘Documents’ folder, if we did everything right, you should see a .plist file in there.

Reading a .plist file into an NSMutableDictionary

Last but not least, now that we have our data in a .plist file for offline storage, we need to read it back. We can do that just like this ( the data from a .plist file almost always will read into an NSDictionary or NSMutableDictionary):

Wrapping Up

That’s all folks! There are some more really interesting things that you can do with ASI, that I could cover in a future tutorial if you would like. Like using blocks to put a URL request and all of the code for it’s delegate methods in one selector, support for background tasks on iOS 4 and higher, using a queue for multiple network request at the same time, asynchronusly, getting the download progress of a URL Request into a UIProgressBar and much more over at the ASIHTTPRequest Documentation.

Problems?

We have a new Questions and Answers section so you can get help from the awesome community.

Ethan Kramer

Ethan is studying to be a computer science or web technology and design major in the United States. He make beautiful iOS, Web and Mac Apps, check out his website for some of his latest projects. he is always learning new tips & tricks about Objective-C and Web development. Although he is mainly a developer he loves to design too. He loves his freinds, life and Jesus and he always treats others as they would want to be treated. he is always open to learn new things!

scott d.

Excellent Tutorial!! It’s the best tutorial I’ve found yet for this!
One (probably stupid) question though. When I use your URL, it works as expected, and returns an array of colors, but what I need to do is return data from a mysql database table. I use this below in my php file:

So when I plug in my URL instead of yours, where your URL returned an array of colors, mine returns an array of true,true,true,true,true,etc…….. instead of actually returning the values from my database table.
I think one obvious error is that you return a string, and I’m putting my data into an array, but I think my problem happens before that, because with all request, response stuff aside, I get the array of ‘trues’ just by echoing out the statement in php through the terminal, bypassing obj-c altogether. Any ideas are greatly appreciated, and thanks so much for this tutorial!

http://forrst.me/ethankr Ethan Kramer

Can I see the URL which you are requesting the JSON from?

Jacco

@Ethan can you please post your solution about parsing the json in a different threath?

http://ek.alphaschildren.org/ Ethan Kramer

Just replace

123456789101112

// The url to make the request to
NSURL *colorURL = [NSURL URLWithString:@"http://www.colourlovers.com/api/colors?format=json"];

That will run the request on a seperate thread. So now if you wnat to display a loading indicator just make it appear before you start the request and then in the

1

requestFinished:

method, after the line that captures the json output (

1

NSString *theJSON = [request responseString];

) just make the loading indicator disappear.

Jonson10

The post is very nicely written and it contains many useful facts. I am happy to find your distinguished way of writing the post. Now you make it easy for me to understand and implement. Thanks for sharing with us. public liability insurance quote

when i log jsonDictionary, its just fine (i also receive the response)
but after that in NSMutableArray *colorTitles =[jsonDictionary objectForKey:@”title”]; it crashes and gives sigabart.

http://ek.alphaschildren.org/ Ethan Kramer

Can you please post your source code. Just copy and paste the code from the two files and put it on http://pastie.textmate.org so I can see where in your code the problem occurs

mandar

When i finish adding ASI (all the files), and write at the right before curly brackets,i immediately get the error
“cannot fid protocol declaration for ASIHTTPRequestDelegate”

Note: i have added all the files in my Xcode project.

http://ek.alphaschildren.org/ Ethan Kramer

Did you import “ASIHTTPReqest.h” in the .h and .m files of the the class you want to use ASI In?
Did you make your .h file the ASIHTTPRequestDelegate of the Request by putting right before the opening curly bracket of the @interface declaration in the .h file?
When making the request did you write [request setDelegate:self]; or request.delegate = self; before calling [request startAsynchronus]; ?

Mike

Thanks for a great tut. I’m gonna go ahead and ask you to stop using “go ahead” in every sentence. If you’ve ever spent a day watching and reading technical/instructional articles you hear the phrase “Go ahead and…” so many goddamn times it makes you crazy. There’s not need for it. So I’m gonna go ahead and leave it there. But please… don’t spoil such a great article with “Go ahead and…” it’s pointless and really freekin annoying!

http://ek.alphaschildren.org/ Ethan Kramer

Thanks for the constructive criticism.

naSh

Hi, I am trying to begin the tutorial, but I cannot since an error appears saying “libxml/HTMLparser.h file not found. Is there a way to fix it. Could you maybe post your project to begin with a working template with the resources? Thanks!

http://ek.alphaschildren.org/ Ethan Kramer

Make you’ve included libxml2.dylib in your project. The next step is to add “${SDKROOT}/usr/include/libxml2″ to your header search paths in target settings. I’m not at my computer right now, and Xcode is currently unavailable on my computer at the moment. For now refer to this post on Stack Overflow http://stackoverflow.com/questions/6012030/adding-libxml2-in-xcode and scroll down to the third answer (it’s the most detailed). If you still need help let me know, and I’ll post the tutorials Xcode project when I can.

http://fotechys.com x-fote-x

Nice Tuto,

But the line [request startAsynchronus], should be [request startAsynchronous];

If you can correct it, some new guys won’t get lost.

Thanks for the Tuto.

http://ek.alphaschildren.org/ Ethan Kramer

Your welcome. I’ll change it thanks.

http://ek.alphaschildren.org/ Ethan Kramer

@edward youre welcome!

edward

@Ethan
thnks about the info ive finally managed it to do parsing on a different thread.. it wont freeze anymore… I use NSOperation.. it makes multithreading (i think) a lot easier..

http://ek.alphaschildren.org/ Ethan Kramer

@Edward your welcome. Try running the method that downloads and parses your JSON on a different thread. You could ask@Dominic how to do that. That usually works for him.

edward

@Ethan i tried it but still it freezes during parsing..
i put my parsing and display data inside [request setCompletionBlock:^]
.. what did i do wrong.. thnx for the reply

http://ek.alphaschildren.org Ethan

@edward I would run an asynchronous request on blocks. Check out “running requests using blocks” in the ASI documentation using the link provided in the article

edward

@Grzegorz Adam Hankiewicz
can you show me how to prevent hanged app during parsing.. i also use ASIHTTPRequest and SBJson

http://ek.alphaschildren.org/ Ethan Kramer

Thank you everyone for your suggestions. @Luke thank you. I will update the post with that information as soon as possible.

http://www.57digital.co.uk Luke Dixon

Nice post, however you might want to explain how to setup the ASIHTTPRequestDelegate in the .h file. Some new iOS developers might not know how to set delegates up.

Here is an example for anyone that is wondering:

1

@interface SomeClass : UIViewController <ASIHTTPRequestDelegate>

http://www.terracoding.com Dominic

Yeh, what I tend to do its just make a synchronous request but put the whole thing within a thread, so both the request and the parsing is done in that thread.

Alternatively, I just create a delegate protocol etc.

http://elhaso.com/ Grzegorz Adam Hankiewicz

There seems to be a subtle behaviour problem with the code as it seems. Without seeing it all at once, it looks like you parse JSON inside the ASIHTTPRequest requestFinished delegate. The problem with that is that it runs on the main thread. You can check yourself looking at the ASIHTTPRequest source code, the requestFinished method calls reportFinished on the main thread, which then calls the delegate.

What this means is that even though your network query is asynchronous, the processing of the JSON will actually block on your main thread, and the user will get a “hanged app” if the parsing takes a long time.

To prevent this you can dispatch a GDC asynchronous block from your delegate or subclass ASIHTTPRequest::requestFinished to perform there the JSON parsing before calling [super requestFinished]. It’s handy then to store the parsed results in your subclass.

Submitted Articles, Images, content and comments remain the copyright of the original author(s). It is the responsibility of the owner to not break any NDA'S, or other copyright laws. iOS-Blog.co.uk accept no responsibility and such is not liable for any content that fails to adhear to these.