API for iOS - Part 2

In our last post we saw how to configure a Rails environment, create and locally serve a very basic API. You should now be able insert and persist new objects (we called them Products) in the local database via the very simple interface Rails created for you and query them as JSON from http://localhost:3000/products.json.

I inserted a few objects and this is the JSON I can retrieve from my API:

[
    {
        "created_at": "2012-12-28T12:02:08Z",
        "id": 1,
        "name": "Book",
        "updated_at": "2012-12-28T12:02:08Z"
    },
    {
        "created_at": "2012-12-28T12:02:16Z",
        "id": 2,
        "name": "Movie",
        "updated_at": "2012-12-28T12:02:16Z"
    },
    {
        "created_at": "2012-12-28T12:02:32Z",
        "id": 3,
        "name": "iPhone",
        "updated_at": "2012-12-28T12:02:32Z"
    }
]
Since each object has a property called name, basically a simple string, we want to build an iOS app that retrieves and parses the JSON, and then displays the results in a table view. Pretty basic stuff, but at the heart of much more complex projects.

Starting a new Xcode project.

Open Xcode and start a new project and choose the single Single View Application template.
http://cocoahunter-blog.s3.amazonaws.com/ios-api/api1.png

Then type your app name, and select Use Storyboards and Use Automatic Reference Counting.
http://cocoahunter-blog.s3.amazonaws.com/ios-api/api2.png

In the summary view of your project, under targets, make your app’s Deployment Target to be iOS 6.0 compatible or greater.

Installing dependencies using CocoaPods

To connect to our RESTful API we’re going to use RestKit an Objective-C framework for iOS that aims to simplify the interaction with RESTful web services. To manage our dependencies I suggest you use CocoaPods. I’m going to assume you already have CocoaPods installed on your system to continue with this tutorial (otherwise, just go to here and follow the instructions on how to install it). Now navigate or cd into you app directory and create a new file named Podfile. Open it and write the following:

platform :ios, '5.0'  
pod 'RestKit'  

Then from the command line run

pod install  

You shoul now have in you project dir a file with a .xcworkspace extension. From now on you should always work on this file when developing your app.

Customizing the storyboard

Open your workspace and navigate to your MainStoryboard.storyboard file. Delete the view controller in the storyboard and replace it with a Table View Controller (drag it on the storyboard from the Object Library). You can now delete (move to trash) the two ViewController.h / ViewController.m files from the Project Navigator in the left panel.
http://cocoahunter-blog.s3.amazonaws.com/ios-api/api3.png

Create (Command-N) a new Objective-C document and add it to your project.
http://cocoahunter-blog.s3.amazonaws.com/ios-api/api4.png

Name it MyTableViewController and make it inherit from the UITableViewController class.
http://cocoahunter-blog.s3.amazonaws.com/ios-api/api5.png

Back to the Storyboard you can now set the Class of your TableViewController to be a MyTableViewController in the Identity inspector.
http://cocoahunter-blog.s3.amazonaws.com/ios-api/api6.png

You should also set the identifier of the cell to Cell so that we can dequeue the cell of the TableView from our code later on.
http://cocoahunter-blog.s3.amazonaws.com/ios-api/api7.png

Creating the model

Create a new Objective-C (Command-N) document and make it inherit from NSObject class. I’ll simply call it Product. This is how Product.h looks like:

#import <Foundation/Foundation.h>
@interface Product : NSObject

@property (strong, nonatomic) NSString *name;
@property (nonatomic) int product_id;

@end

Product.m doesn’t need to be modified because with the latest release of Apple’s developer tools properties don’t need to be synthesized anymore. So we’re just declaring two properties in the header file, a name and an id.

Setting up the RestKit stack

Now we’re going to init the RestKit stack in order to be able to connect and retrieve data from our API.
In MyTableViewController.h we need to declare an Array where we will store the incoming data:

@property (nonatomic, retain) NSMutableArray *productsArray;

In MyTableViewController.m, import the header files for RestKit and Product:

#import <RestKit/RestKit.h>
#import "Product.h"

Then, inside the viewDidLoad declare this method call:

[self initRestKitStack];

Now let’s implement initRestKitStack method:

-(void)initRestKitStack
{
    // Set debug logging level. Set to 'RKLogLevelTrace' to see JSON payload
    RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);

    // Set the base URL of our API
    NSURL *baseURL = [NSURL URLWithString:@"http://localhost:3000"];
    RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:baseURL];

    // Enable Activity Indicator Spinner
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;

    // Map the JSON payload to our Product model with a dictionary
    RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[Product class]];
    [mapping addAttributeMappingsFromDictionary:@{
        @"name": @"name",
        @"id": @"product_id"}];

    RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping
                        pathPattern:@"/products.json"
                        keyPath:nil
                        statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

    [objectManager addResponseDescriptor:responseDescriptor];

    // Load the object model via RestKit
    [objectManager getObjectsAtPath:@"/products.json" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
        // Store the results of our query into our mutable array
        _productsArray = [NSMutableArray arrayWithArray:[mappingResult array]];
        // Update table data after the fetch has finished
        [self.tableView reloadData];
    } failure:^(RKObjectRequestOperation *operation, NSError *error) {
    }];
}

The code is commented inline, so you shouldn’t have any problems understanding it. RestKit is a pretty massive framework and provides a lot of functionality, so if you think it could be of any help to you, it’d be better to read the documentation.

Now let’s implement the table view methods:

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [_productsArray count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    cell.textLabel.text = [(Product *)[_productsArray objectAtIndex:indexPath.row] name];

    return cell;
}

Build and run

Now you should start your rails API server. Just cd from the command line into the appropriate directory and run rails s. If you build and run you iOS app you should now be seing the Table View populated with the data from your API, something like the following:

{% img center http://cocoahunter-blog.s3.amazonaws.com/ios-api/api8.png 200%}

Conclusion

Of course these are only humble beginnings, there’d still be much to say about REST APIs. The simple app we built is lacking a lot of features (the most important perhaps is data persistence on the device), but we’re on the good track. In one of my future posts I’ll talk about integration of RestKit with CoreData.