Wednesday, May 08, 2013

JSON parsing in iOS, using the Tiny Tiny RSS API

I saw a recent question posted to reddit a few days ago. It was around the idea that there aren't any good tutorials on how to take JSON data and parse it in iOS. Since I'm a newbie to iOS, I decided that it might be a good problem to solve. Before we start, take a look at https://gist.github.com/kgdesouz/5542543/.

How the heck do you connect to a JSON API that is remote (in iOS)?

Using curl, its easy. Using iOS, there's a few more steps. First we need to put our data into some sort of dictionary (well, at least that's what I wanted to do). Inside the viewDidLoad method, you can just use NSMutableDictionary. This data will need to be serialized afterwards in order to prep it to be sent to the API.

    NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithDictionary:@{
                                       @"op" : @"login", @"user" : login, @"password" : password
                                       }];
    [self fetchData:dictionary];
    [self getCategories:_sid];
Then we need to serialize the data. The magic is done in the fetchData method. Actually, all the heavy lifting is done the the fetchData method, let's break it down a little.
- (void)fetchData:(NSMutableDictionary *)dictionary
{
    NSError *error = nil;
    NSData *jsonData = [NSJSONSerialization
                        dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:&error];
    
    
    if ([jsonData length] > 0 && error == nil){
        NSLog(@"Successfully serialized the dictionary into data."); NSString *jsonString =
        [[NSString alloc] initWithData:jsonData
                              encoding:NSUTF8StringEncoding];
        NSLog(@"JSON String = %@", jsonString);
Now that we have a serialized object, we can now get it ready to be sent.
        // create the connection and send it to the server
        NSString *requestString = kTinyTinyRSSURL;
        
        NSURL *url = [NSURL URLWithString:requestString];
        
        NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
        [req setHTTPMethod:@"POST"];
        
        NSString *dataString = jsonString;
        
        NSData *requestData = [NSData dataWithBytes:[dataString UTF8String] length:[dataString length]];
        
        [req setHTTPBody:requestData];
        
        NSURLResponse *theResponse = NULL;
        NSError *theError = NULL;
        NSData *theResponseData = [NSURLConnection sendSynchronousRequest:req returningResponse:&theResponse error:&theError];

The money here is theResponseData, it allows you to POST the data to the server. Once you have sent the data, you will get back a response, so you need to deserialize that back into something useful. I'm putting it into a NSArray.
 
        //deserialize the response into something usable
        NSDictionary* json = [NSJSONSerialization
                              JSONObjectWithData:theResponseData //1
                              options:kNilOptions
                              error:&error];
        
        NSArray* validResponse = [json objectForKey:@"content"]; //2
        NSLog(@"content: %@", validResponse); //3

That's basically it. fetchData is reusable, so you can now use the same method to make other JSON queries. As per the gist, anyone should be able to use the code to connect to a JSON API.

Note: I mashed together a lot of this code from:
A few more thoughts about this little piece of code:
  • My full code snip can be found here: https://gist.github.com/kgdesouz/5542543/
  • The code was meant to be quick and dirty. I didn't really follow the MVC, since really I didn't need the view or the controller, just the model. I wanted everything to go to stdout (to keep things simple). I basically just looked at some tutorials and tried to integrate it. 
  • There's no GUI component required. Without too much effort, anyone should be able to use the code for any JSON calls, with a little tweaking.
  • There is a lot of clean up required. I'm still playing with unit testing in this language. Specifically, JSON unit testing. Its a little different since I need to figure out how to reset the JSON. I will probably create a JSON mock object to do this. 
  • I totally didn't use getter and setter methods here. I know I should, but read the first bullet point; quick and dirty.