TSM - RestKit for iOS

Mihai Fischer - mihaifischer.com

At this moment there is a variety of web services to which you can hook up to obtain useful data. For example we have the Twitter API for tweets, we have the Instagram API for images or in the particular case of this article, the Foursquare API for venues and locations. Of course you could connect to these, using the out of the box NSURLRequest or a library like AFNetworking, but you would have to write a lot of boiler plate code for parsing and mapping the response JSON. Here comes into role RestKit to help you with some of these things, and I"ll show you just how in the next few lines.

What is RestKit?

RestKit is an Objective-C framework which helps you connect to any REST type web services. In rough lines, it combines an easy to use HTTP request/response API and a strong mapping system. The most outstanding feature of RestKit is the fact the it allows the developer to think more of how to develop his data model and worry less for how to send requests, parse JSON and to map the result to native objects.

What"s in the package?

RestKit contains a HTTP client based on NSURLConnection and provides a library with convenience methods for MIME type inspection or status codes. For example sending data collected from a form is just as simple as creating a dictionary with parameters. It also has support for big files, like video.

Framework level support for switching between servers and environments (ex: development, production). RestKit uses base URL and resource paths instead of whole URLs to aid this.

A see mapping system. This is shown through a model kind of layer which maps parsed JSON into native objective-C objects. This mapping is done through key-value coding. It also provides Apple Code Data framework integration. This allows RestKit to persist any object loaded from a server into a local data base, used for caching or just as the main data base use to sync with the Cloud.

Data base seeding. Used together with Core Data, it is capable to populate a local data base, thing which allows you to have a ready to use data base when the user downloads the app from the App Store.

Integration with Rails. RestKit was first built as an Objective-C response to Active Resource, but Rails being the general case for an iOS backend, it was somehow normal for RestKit to have support for this.

Example

Let"s start with a simple app containing a list populated with venues provided by the Foursquare service. We"ll split it up into two main view controllers : an UITableViewController for the list and an UIViewController for the detailed view of the venues. I won"t get into the details, because it"s not the subject of the article, but i will go on with the methods of getting RestKit into your project. there two main ways to achieve this : Cocoapods or Git submodule. Keeping things simple and I will use CocoaPods, which resumes to a few lines in the terminal:

$ sudo gem install cocoapods
$ pod setup
$ cd /calea/catre/ProiectulVostruCuBaruriApropiate
$ touch Podfile
$ [edit] Podfile (using your preferred editor; vim, nano, etc)
platform :ios, "5.0"
pod "RestKit", "~> 0.20.0"
$ pod install

Before using the API, you have to register the app. More details can be found here.

You"ll need a client ID and client secret, which you can add as macros to the project.

#define kCLIENTID @»Your Foursquare Client ID»
#define kCLIENTSECRET @»Your Foursquare Client Secret»

After all the setup is done, you can the out the service, and notice the response JSON will look something like this:

{
    "meta": {
        "code": 200
    },
    "notifications": [
        {
            "item": {
                "unreadCount": 3
            },
            "type": "notificationTray"
        }
    ],
    "response": {
        "confident": true,
        "neighborhoods": [],
        «venues»: [
            {
                "categories": [
                    {
                        "icon": {
                            "prefix": "https://ss1.4sqi.net/img/categories_v2/food/bar_",
                            "suffix": ".png"
                        },
                        "id": "4bf58dd8d48988d1e0931735",
                        "name": "Bar",
                        "pluralName": "Bars",
                        "primary": true,
                        "shortName": "Bar"
                    }
                ],
                "contact": {
                    "formattedPhone": "(408) 446-9000",
                    «phone»: "4084469000",
                    "twitter": "jackslongdrinks"
                },
                "hereNow": {
                    "count": 0,
                    "groups": []
                },
                "id": "51630409498eedc7dd88e60b",
                "location": {
                    "address": "20686 Stevens Creek Blvd",
                    "cc": "US",
                    "city": "Cupertino",
                    "country": "United States",
                    "crossStreet": "De Anza Blvd",
                    "distance": 936,
                    "lat": 37.32246179607897,
                    "lng": -122.03470838696346,
                    "postalCode": "95014",
                    "state": "CA"
                },
                "name": "Jacks Cocktails",
                "referralId": "v-1390061483",
                "specials": {
                    "count": 0,
                    "items": []
                },
                "stats": {
                    "checkinsCount": 3790,
                    "tipCount": 40,
                    "usersCount": 1460
                },
                "verified": true
            },
            {
                "categories": [
                    {
                        "icon": {
                            "prefix": "https://ss1.4sqi.net/img/categories_v2/food/bar_",
                            "suffix": ".png"
                        },
                        "id": "4bf58dd8d48988d1e0931735",
                        "name": "Bar",
                        "pluralName": "Bars",
                        "primary": true,
                        "shortName": "Bar"
                    }
                ],
                "contact": {
                    "formattedPhone": "(650) 321-2161",
                    «phone»: "6503212161",
                    "twitter": "downtown_coffee"
                },
                "hereNow": {
                    "count": 0,
                    "groups": []
                },
                "id": "4dd1580eb3adb047f5024231",
                "location": {
                    "address": "101 Forest Ave",
                    "cc": "US",
                    "city": "Palo Alto",
                    "country": "United States",
                    "crossStreet": "at Alma St.",
                    "distance": 17063,
                    "lat": 37.442086282055726,
                    "lng": -122.16159119091502,
                    "postalCode": "94301",
                    "state": "CA"
                },
                "name": "Downtown Coffee",
                "referralId": "v-1390061483",
                "specials": {
                    "count": 0,
                    "items": []
                },
                "stats": {
                    "checkinsCount": 14168,
                    "tipCount": 118,
                    "usersCount": 4044
                },
                "verified": true
            }
        ]
    }
}

Code explanations

Now that we pilled up all our pieces, and took a look at the JSON, we can go on with building the app. For the sake of simplicity I will talk only about 2 big parts of RestKit: Network and Object Mapping. We"ll define a base URL for the Foursquare API (https://api.foursquare.com) . Afterwords we define a model which will map the JSON Values returned by the service. Now that we know how the response look, we can create a Venue class, with only one property (name) for keeping things simple.

@interface Venue : NSObject
@property (nonatomic, strong) NSString *name;
@end

Then we import it together with RestKit in the main view controller.

#import 
#import "Locatie.h"

Then we add a configuration method, called from one of the lifecycle iOS methods

- (void)viewDidLoad
{
    [super viewDidLoad];
 
    [self configureRestKit];
    [self loadVenues];
}

Implement them:

- (void)configureRestKit
{
    // initialize AFNetworking HTTPClient
    NSURL *baseURL = [NSURL URLWithString:@»https://api.foursquare.com»];
    AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
 
    // initialize RestKit
    RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
 
    // setup object mappings
    RKObjectMapping *venueMapping = [RKObjectMapping mappingForClass:[Venue class]];
    [venueMapping addAttributeMappingsFromArray:@[@"name"]];
 
    // register mappings with the provider using a response descriptor
    RKResponseDescriptor *responseDescriptor = 
        [RKResponseDescriptor responseDescriptorWithMapping:venueMapping 
                                                     method:RKRequestMethodGET 
                                                pathPattern:@"/v2/venues/search" 
                                                    keyPath:@»response.venues» 
                                                statusCodes:[NSIndexSet indexSetWithIndex:200]];
 
    [objectManager addResponseDescriptor:responseDescriptor];
}

We define the base URL we talked about earlier. Every request will linked to this URL.

The RKObjectManager class defines the mapping between a JSON attribute and the model corresponding attribute. addAttributeMappingsFromArray is a shortcut method in the case the JSON and the model have the same keys.

- (void)loadVenues
{
    NSString *latLon = @"37.33,-122.03"; // aici putem sa trecem locatia noastra curenta
    NSString *clientID = kCLIENTID;
    NSString *clientSecret = kCLIENTSECRET;
 
    NSDictionary *queryParams = @{@"ll" : latLon,
                                  @"client_id" : clientID,
                                  @"client_secret" : clientSecret,
                                  @"categoryId" : @"4bf58dd8d48988d1e0931735",
                                  @"v" : @"20140118"};
 
    [[RKObjectManager sharedManager] getObjectsAtPath:@"/v2/venues/search"
                      parameters:queryParams
                         success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                                     _venues = mappingResult.array;
                                     [self.tableView reloadData];
                                 }
                         failure:^(RKObjectRequestOperation *operation, NSError *error) {
                                     NSLog(@"Din pacate nu sunt baruri in zona…": %@", error);
                                 }];
}


@interface MasterViewController ()
 
@property (nonatomic, strong) NSArray *venues;
 
@end
 


@implementation MasterViewController

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
 
    Venue *venue = _venues[indexPath.row];
    cell.textLabel.text = venue.name;
 
    return cell;
}

Done. It"s a really useful library which helps you a lot in concentrating on the important things, like the model, instead of waiting time on parsing a JSON. The end result should be a functional list of pubs and bars in your area.