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

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


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


1
2
3
4
5
6
7
/* 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


1
2
3
4
5
6
7
8
9
10
11
12
            // 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:


1
2
3
4
5
6
7
8
9
10
11
            -(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:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
            // 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.


1
2
3
4
5
6
7
8
9
10
11
12
13
            /* 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.


1
2
            // 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:


1
2
3
4
5
6
7
            -(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:


1
2
3
4
5
6
            //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:


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


1
2
                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.

Problems?

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

Ask a Question

Enjoyed this post?

Subscribe to our RSS Feed or Follow us on twitter to keep up to date with the latest from iOS-Blog. Remember, Sharing is caring so please click one of the following options:

Tags: , , ,