Show Menu

Looking for an iOS Developer?

Submit your 30 day Job Listing for FREE

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.

UPDATE: 12/1/2016
There is a new, Swift version update to this article. Check out the Swift 2 Parse Json Tutorial

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

So go ahead and do that now. If you need help, head on over to the ASIHTTPRequest Set Up Page.

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):

			

			#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.



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

<ASIHTTPRequestDelegate>

/* Now the top of your JSONAppViewController.h should look something like this */
@interface JSONAppViewController : UIViewController <ASIHTTPRequestDelegate> { 


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

			

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

			//The actual request
			ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:colorURL];

			// Becoming the request delegate
			//To get callbacks like requestFinished: or requestFailed:
			[request setDelegate:self];

			// Fire off the request
			[request startAsynchronous];

			

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:

			

			-(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:

			

			// Alloc and initialize our JSON parser
			SBJsonParser *parser = [[SBJsonParser alloc] init];

			/* 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) */

			// Actually parsing the JSON
			NSMutableDictionary *jsonDictionary = [parser objectWithString:theJSON error:nil];

			/*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.

			

			/* 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.

			

			// 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”:

			

			-(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:

			

			-(NSString *) documentsPath{
			    NSArray *paths =
			    NSSearchPathForDirectoriesInDomains(
			                                        NSDocumentDirectory, NSUserDomainMask, YES);
			    NSString *documentsDir = [paths objectAtIndex:0];
			    return documentsDir;
			}

			

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:

			

			//Setting the location to write to .plist file to
			NSString *filename = [[self documentsPath] stringByAppendingString:@"/YOUR_FILENAME.plist"];
			    NSLog(@"Writing plist to %@",filename);

			//Our NSMutableDictionary from earlier
			[jsonDictionary writeToFile:filename atomically:YES]; 

			

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:

~/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):

			

			    NSString *fn = [[self documentsPath] stringByAppendingString:@"/YOUR_FILENAME.plist"];
			    NSMutableDictionary *d = [NSMutableDictionary dictionaryWithContentsOfFile:fn];

			

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.

having issues?

We have a Questions and Answer section where you can ask your iOS Development questions to thousands of iOS Developers.

Ask Question

FREE Download!

Get your FREE Swift 2 Cheat Sheet and quick reference guide PDF download when you sign up to SwiftMonthly


Sharing is caring

If you enjoyed this tutorial, please help us and others by sharing using one of the social media buttons below.


Written by:

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!

Comments

There are currently: 28 Responses to “Parsing JSON on iOS with ASIHTTPRequest and SBJson”. Leave your comment!

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:

$stmt = $this->db->prepare( ‘SELECT * FROM offenderEntries WHERE device_id=?’);

$stmt->bind_param(“s”,$device_id);
$stmt->execute();

$results = array();

while ($entry = $stmt->fetch()) {
$results[] = $entry;
}

sendResponse(200, json_encode($results));

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!


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


    Just replace


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

    //The actual request
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:colorURL];

    // Becoming the request delegate
    //To get callbacks like requestFinished: or requestFailed:
    [request setDelegate:self];

    // Fire off the request
    [request startAsynchronous];

    with


    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

    NSURL *colorURL = [NSURL URLWithString:@"http://www.colourlovers.com/api/colors?format=json"];

    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:colorURL];

    [request setDelegate:self];

    [request startAsynchronous];

    });

    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 requestFinished: method, after the line that captures the json output (NSString *theJSON = [request responseString];) just make the loading indicator disappear.


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.



    I found the issue. On line 70 change NSMutableArray *colorTitles = [jsonDictionary objectForKey:@"title"]; to NSMutableArray *colorTitles = [jsonObject valueForKey:@"title"];


Hello,
When i exactly follow your sample code along with frameworks being added,
i get following error:
012-04-09 20:39:06.441 TabBarTP[15498:f803] -[__NSArrayM objectForKey:]: unrecognized selector sent to instance 0x685ef80
2012-04-09 20:39:06.442 TabBarTP[15498:f803] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[__NSArrayM objectForKey:]: unrecognized selector sent to instance 0x685ef80’
*** First throw call stack:
(0x1a6e022 0x1bffcd6 0x1a6fcbd 0x19d4ed0 0x19d4cb2 0x2d77 0x1a6fe42 0x2516b 0x1a6fe42 0x11f29df 0x1a4294f 0x19a5b43 0x19a5424 0x19a4d84 0x19a4c9b 0x1ec57d8 0x1ec588a 0x8cc626 0x23d2 0x2345)
terminate called throwing an exception(lldb)

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.


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.


    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]; ?


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!


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!


    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.


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.


@edward youre welcome!


@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..


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


@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


@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


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


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


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:


@interface SomeClass : UIViewController


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.


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.

You may also find JSONKit to be faster than SBJSON, though YMMV.