Introduction to RestKit Tutorial

Scott

Need coffee now? You're gonna write an app for that!

Update 02/24/2014: Updated RestKit tutorial for new version of the RestKit library.

There are a lot of web services that you can connect to to get interesting and useful data.

For example, Twitter has a web service you can use to list and send Tweets, and Foursquare has a web service you can connect to that allows you to retrieve a list of restaurants near your current location.

If you want to use these APIs, you can connect to them directly with NSURLRequest or a wrapper library like AFNetworking. However, there’s an easier way for many of these APIs – use RestKit!

RestKit is a popular and easy-to-use framework that saves you from having to write much of the boring code you usually have to when working with web service APIs, such as parsing JSON or mapping responses to objects.

In this RestKit tutorial, we’ll try it out with by making a simple app that uses the FourSquare API to list the coffee shops nearby – because we all love and need our coffee! :]

This tutorial is for any level of iOS developer, from beginner to advanced. So read on and get ready for a RESTful caffeine buzz! If you are not sure about networking in general on iOS yet, the I suggest checking out some of the other tutorials on this site

Getting Started

To start, we’re going to create a Master-View application in Xcode. If you’re already familiar with how to do this and how it works, feel free to glance through this quickly and move to the next section – but I’m going to include some more details for the beginners.

Start up Xcode and create a new project with the iOS\Application\Master-Detail Application template.

Enter CoffeeKit for the product name, set device family to iPhone (leave “Use Core Data” checkbox unchecked):

Project settings for CoffeeKit app

Click Next and choose a location to save your project.

The Master-Disciple, er, wait… Master-Detail template is a fully-functioning app, so build and run.

You should see a blank screen with Edit and Add (+) buttons. Click the (+) button to add some table entries to the Master screen. Tapping one of these entries will show a Detail screen:

You can see how this format is perfect for lists of data that contain different levels of information. Open MasterViewController.h. You will see something like this:

#import <UIKit/UIKit.h>
 
@interface MasterViewController : UITableViewController
 
@end

MasterViewController is a UITableViewController, which manages a UITableView and conforms to two protocols: UITableViewDelegate and UITableViewDataSource. UITableViewDataSource handles the data model needs for the UITableView, and UITableViewDelegate handles the appearance needs of the UITableView (such as managing selections; configuring section headers and footers; deleting and reordering of cells).

Look at some key methods in MasterViewController.m:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    return 1; 
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{
    return _objects.count; 
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath 
{ 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
 
    NSDate *object = [_objects objectAtIndex:indexPath.row];
    cell.textLabel.text = [object description];
    return cell; 
}

The first two methods, numberOfSectionsInTableView: and tableView:numberOfRowsInSection: are as their names imply. You use them to tell the table how many sections it has and how many rows are in each section.

You specify the data displayed in each cell using tableView:cellForRowAtIndexPath:.

For this tutorial, you’ll only be using the Master View Controller to display a list of nearby coffee shops. To keep things simple, the Detail View Controller will not be used. So remove the following:

  • prepareForSegue:sender: from MasterViewController.m
  • The #import for DetailViewController.h at the top of MasterViewController.m.
  • The files DetailViewController.h and DetailViewController.m. (Make sure you choose Move to trash – you really don’t need these any more!)

Next, delete “Detail View Controller – Detail” from Main.storyboard. Yes that’s right, the entire view controller! You can find it here:

RESTing Easy

OK now on to the fun stuff – RestKit!

The RestKit GitHub project describes RestKit as the following:

RestKit is a modern Objective-C framework for implementing RESTful web services clients on iOS and Mac OS X. It provides a powerful object mapping engine that seamlessly integrates with Core Data and a simple set of networking primitives for mapping HTTP requests and responses built on top of AFNetworking. It has an elegant, carefully designed set of APIs that make accessing and modeling RESTful resources feel almost magical.

…like unicorns.

RestKit has three main components:

  1. Network – RestKit now uses AFNetworking v1.3.3 for the network layer. RestKit maintainers are working on updating to AFNetworking 2.0.
  2. Object Mapping – Provides a simple API for turning remote JSON/XML responses into local objects.
  3. Core Data – Provides additional support on top of the object mapper for mapping remote resources to persisted local objects. (No Core Data for this tutorial)

In other words, that’s all a lot of code that you don’t have to write! ;]

The hardest thing about working with RestKit is the installation and setup. There are two setup options: CocoaPods (with automatic configuration, i.e. the easy way) or Git submodule (with manual configuration, the harder way). I recommend using CocoaPods.

  1. The first option is using CocoaPods: [for more on CocoaPods, see Introduction to CocoaPods.]

    Open up Terminal and enter the following commands:

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

    Install RestKit into your project:

    $ pod install

    Close your CoffeeKit.xcodeproj and reopen CoffeeKit.xcworkspace.

  2. The second option is as a Git submodule:

    Open up Terminal and enter the following commands:

    $ cd /path/to/CoffeeKit
    $ git init 
    $ git submodule add git://github.com/RestKit/RestKit.git RestKit
    $ git submodule update --init --recursive

    Next, add RestKit to your project. In a Finder window, open the RestKit folder inside your CoffeeKit project folder. Drag RestKit.xcodeproj into the Project Navigator in Xcode, like this:

    Now that you’ve told your project RestKit is there, it’s time for the most detailed step: configuring Restkit so you can use it properly. You’ll both configure settings and add some frameworks to your build configuration.

    1. Click on the top item in the Project Navigator pane and select the CoffeeKit target.
    2. Click the Build Settings filter for Other Linker Flags in the search box. Click the value column to the right of Other Linker Flags, add -ObjC and hit return.

      other_linker_flag

    3. Now clear the search box text and input Header Search Paths. Double click the value column to the right of Header Search Paths, add "$(BUILT_PRODUCTS_DIR)/../../Headers" to the list (be sure to include the quotes) and hit return. Then click outside the drop down to return to the build settings.

    4. Now click on the Build Phases tab, click on the disclosure triangle for Target Dependencies, and tap the Add (+) button. Select RestKit and click Add.
    5. Now click on the Link Binary With Libraries disclosure triangle and tap the Add (+) button. Select libRestKit.a and click Add.
    6. You also need to add some required Apple frameworks. Click the Add (+) button again and select the following: (⌘-click for multi-select)

      • CFNetwork.framework
      • CoreData.framework
      • MobileCoreServices.framework
      • Security.framework
      • SystemConfiguration.framework

      Click Add one last time!

    7. Next you need to update the precompiled header file to avoid build warnings from AFNetworking. Open CoffeeKit-Prefix.pch (it’ll be under Supporting Files). Replace the contents with the following:
      #ifndef __IPHONE_5_0
      #warning "This project uses features only available in iOS SDK 5.0 and later."
      #endif
       
      #ifdef __OBJC__
          #import <UIKit/UIKit.h>
          #import <Foundation/Foundation.h>
          #import <SystemConfiguration/SystemConfiguration.h>
          #import <MobileCoreServices/MobileCoreServices.h>
      #endif

For the final step in the setup, verify your RestKit installation and configuration. Open AppDelegate.m, and add the following:

#import <RestKit/RestKit.h>

Build and run. If it builds without error, RestKit is set up correctly. Feel the force!

Connecting with Foursquare

Now that you have RestKit working, you can use it to connect to Foursquare’s web services to get venue data for your app.

You will be using Foursquare’s Search Venues API to search for coffee shops near your current location.

Don’t worry about reading that whole document right now, just know we’ll be using this basic query (feel free to click it to try it out!):

This will return JSON-formatted data of coffee-related venues located near Apple Headquarters (latitude: 37.33 and longitude: -122.03). Foursquare’s identifier for venues categorized as “Coffee Shop” is
“categoryId=4bf58dd8d48988d1e0931735”. And to clarify for our Dutch-speaking friends, at FourSquare, the “Coffee Shop” category is for venues that specialize in selling brewed beverages prepared from the roasted and ground seeds of several species of an evergreen shrub of the genus Coffea.

A typical JSON response to the basic query 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/coffeeshop_",
                            "suffix": ".png"
                        },
                        "id": "4bf58dd8d48988d1e0931735",
                        "name": "Coffee Shop",
                        "pluralName": "Coffee Shops",
                        "primary": true,
                        "shortName": "Coffee Shop"
                    }
                ],
                "contact": {
                    "formattedPhone": "(408) 446-9000",
                    "phone": "4084469000",
                    "twitter": "philzcoffee"
                },
                "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": "Philz Coffee",
                "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/coffeeshop_",
                            "suffix": ".png"
                        },
                        "id": "4bf58dd8d48988d1e0931735",
                        "name": "Coffee Shop",
                        "pluralName": "Coffee Shops",
                        "primary": true,
                        "shortName": "Coffee Shop"
                    }
                ],
                "contact": {
                    "formattedPhone": "(650) 321-2161",
                    "phone": "6503212161",
                    "twitter": "philz_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": "Philz Coffee",
                "referralId": "v-1390061483",
                "specials": {
                    "count": 0,
                    "items": []
                },
                "stats": {
                    "checkinsCount": 14168,
                    "tipCount": 118,
                    "usersCount": 4044
                },
                "verified": true
            }
        ]
    }
}

Foursquare provides free access to their web services, as long as you register your app using the OAuth Consumer Registration page, so make sure you do that before going any further.

First, go to this page on Foursquare. You should see a form similar to this:

Enter a name, CoffeeKit and a random download URL and redirect URI. These can be dummy pages on your website. Your app will be a mobile connection, and won’t actually make use of these. Check out Foursquare’s User Authentication page for more information.

Click Save Changes.

You’ll then be given a Client ID and a Client Secret that you can use for Foursquare API calls.

You need to add these to your source code so that RestKit can authenticate itself when making any Foursquare API calls. Add the following to the top of MasterViewController.m, right below the #import lines:

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

Remember that you must replace the dummy values above with the actual client ID and secret you receive from Foursquare.

Time to Code, It Is Surely

You have all the pieces in place to build your app. All you need to do now is add the code!

You will use two of the main components of RestKit: Network and Object Mapping. For Network, you define the base URL for Foursquare’s API, (https://api.foursquare.com) and send/receive your messages. For Object Mapping, you create a data model that you will map to the returned JSON values.

The JSON output above lists two venues. So, use that output to define a Venue data model class.

  1. Select File/New/File… (or right-click in the Project Navigator then click New File…, or ⌘-N).

  2. Select iOS\Cocoa Touch\Objective-C class and click Next.

  3. Enter Venue for class name, NSObject for subclass of and click Next.

  4. Choose a location and click Create.

The only JSON venue data you are adding for now is the venue name. So, open Venue.h, and add a property for name:

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

You will add more in a bit, when needed.

Next, open MasterViewController.m. Then add the following imports at the top of the file:

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

And modify viewDidLoad to:

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

Then add the following method (one of the two you added a call to in viewDidLoad above):

- (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];
}

Here you define the base URL for Foursquare’s API. All requests will be appended to this URL. This is nice if you make requests to several endpoints at the same service. Using this base URL, you create a AFHTTPClient object, and pass that client to create an RKObjectManager. RKObjectManager is the primary interface for interacting with RESTful services.

The RKObjectMapping class defines the mapping between a JSON attribute and your data model’s attribute. addAttributeMappingsFromArray is a shortcut method to use when the JSON and your data model share the same keys, which is “name” in your case. You will add other mappings later in this tutorial.

Next, you create an RKResponseDescriptor, which describes an object mapping that is applicable to an HTTP response. pathPattern matches against URLs for which the mapping should be used. This is appended to the base URL. keyPath is a subset of the parsed JSON response data for which the mapping should be used. Looking at the JSON sample data above, you see that venues is inside of the response object. So, keyPath:@"response.venues" tells RestKit where to find the venue objects.

Now add the following method:

- (void)loadVenues
{
    NSString *latLon = @"37.33,-122.03"; // approximate latLon of The Mothership (a.k.a Apple headquarters)
    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(@"What do you mean by 'there is no coffee?': %@", error);
                                 }];
}

This creates and sends a request to, and receives a response from Foursquare.

The getObjectsAtPath:parameters:success:failure: method is being used to fetch the objects. This is just doing a normal HTTP request, using AFNetworking, to Foursquare’s API with path /v2/venues/search. When the response comes back, it uses the response descriptor mapping you set up earlier in configureRestKit to map the response to Venue objects.

You’ll notice that there is a problem building at the moment though. This is because the success block sets the value of the venues property. But this doesn’t exist. To fix this, open MasterViewController.m, then change the following code at the top:

@interface MasterViewController () {
    NSMutableArray *_objects;
}
@end
 
@implementation MasterViewController

to this:

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

Also, in the same file, change:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
 
    NSDate *object = [_objects objectAtIndex:indexPath.row];
    cell.textLabel.text = [object description];
    return cell;
}

to:

- (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;
}

Also, still in MasterViewController.m, change the return statement in tableView:numberOfRowsInSection: to:

return _venues.count;

Finally, remove the tableView:commitEditingStyle: and insertNewObject: methods from MasterViewController.m, since the data is no longer editable. (Plus, Xcode throws a few compilation errors if you don’t remove/fix those methods :])

Build and run. You should see something similar to this:

You’ll notice some of the places on this list aren’t actually coffee houses, such as Kaiser Permanente and Nokia. I blame Foursquare for that! :P

Stylin’ the Cells

We want to make the cells look a bit nicer. Open Main.storyboard and find the master view controller. Click on the single “prototype cell”. You should see something like this:

One of the options in the image above is the table view cell’s Style. The default is Basic. It provides a left-aligned text label in the cell. The other provided options are:

  • Right Detail
  • Left Detail   
  • Subtitle       

These other options make it possible to show a detail of your choice in the Master view. For example, if you were to use this app to find nearby coffee shops, it would be nice to see how far away each shop is from your present location. Nobody wants to go too far for a coffee fix!

Change the style to Right Detail.

In the JSON data above, the location field provides the distance:

"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"
}

So that RestKit can retrieve this, create a Location data model and provide the mapping. Here are the steps:

  1. Create a new file called Location, a subclass of NSObject.
  2. In Location.h, add the following properties:
    @property (nonatomic, strong) NSString *address;
    @property (nonatomic, strong) NSString *city;
    @property (nonatomic, strong) NSString *country;
    @property (nonatomic, strong) NSString *crossStreet;
    @property (nonatomic, strong) NSString *postalCode;
    @property (nonatomic, strong) NSString *state;
    @property (nonatomic, strong) NSNumber *distance;
    @property (nonatomic, strong) NSNumber *lat;
    @property (nonatomic, strong) NSNumber *lng;
  3. Now in Venue.h, add the following at the top, under the imports:
    @class Location;

    And also add the following property:

    @property (nonatomic, strong) Location *location;
  4. Open MasterViewController.m, add the following import at the top:
    #import "Location.h"
  5. Now, still in the same file, add the Location mappings at the bottom of configureRestKit
    // define location object mapping
    RKObjectMapping *locationMapping = [RKObjectMapping mappingForClass:[Location class]];
    [locationMapping addAttributeMappingsFromArray:@[@"address", @"city", @"country", @"crossStreet", @"postalCode", @"state", @"distance", @"lat", @"lng"]];
     
    // define relationship mapping
    [venueMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"location" toKeyPath:@"location" withMapping:locationMapping]];
  6. This is similar to the Venue mapping you did earlier, except for addPropertyMapping:. It informs your venueMapping instance to use locationMapping for its location property.

  7. Change the contents of tableView:cellForRowAtIndexPath: to:
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
     
    Venue *venue = _venues[indexPath.row];
    cell.textLabel.text = venue.name;
    cell.detailTextLabel.text = [NSString stringWithFormat:@"%.0fm", venue.location.distance.floatValue];
     
    return cell;

Build and run. Now you should see something like this:

The ‘m’ stands for meters, not miles. Also remember that we’re still using the latitude/longitude for Apple HQ – if you want you might want to replace it with your own lat/lng coordinates. For more info on how to do that, check out this tutorial.

Customizing TableViewCells

Let’s wrap things up by displaying the Foursquare checkins in our table view cell as well. To do this, we’ll need to make a custom table view cell.

Open Main.storyboard. Change the table view cell style to Custom. The default labels will disappear. In the Size Inspector, change the Row Height to Custom and from 44 to 64.

Set the Row Height property of the table view itself to 64 as well.

Add 3 UILabels: name, distance and checkins. Make it look like this:

Since the cell is now set to Custom style, you cannot use UITableViewCell‘s textLabel and detailTextLabel properties to add text to the labels. This means that in order to refer to the individual labels, you need to create a subclass of UITableViewCell with custom labels for venue name, distance, and check-ins.

Add a new file to the project, with the Objective-C class template. Name it VenueCell and make it a subclass of UITableViewCell.

Replace VenueCell.h contents with the following:

#import <UIKit/UIKit.h>
 
@interface VenueCell : UITableViewCell
 
@property (nonatomic, weak) IBOutlet UILabel *nameLabel;
@property (nonatomic, weak) IBOutlet UILabel *distanceLabel;
@property (nonatomic, weak) IBOutlet UILabel *checkinsLabel;
 
@end

Replace VenueCell.m contents with the following:

#import "VenueCell.h"
 
@implementation VenueCell
 
@end

As you can see, this class doesn’t do much: it just adds properties for nameLabel, distanceLabel and checkinsLabel.

Next, open Main.storyboard again. Then, select the prototype cell and set its class to VenueCell in the Identity Inspector. Also, in the Attributes Inspector, change Identifier to VenueCell.

These items don’t need to match, but it is good to match them for consistency and clarity.

Now connect the outlets to the labels in VenueCell. In the Connections Inspector, connect the outlets: nameLabel, distanceLabel and checkinsLabel to their respective UILabels.

Open MasterViewController.m. Add an import for VenueCell at the top:

#import "VenueCell.h"

Still in MasterViewController.m, replace tableView:cellForRowAtIndex: with the following:

VenueCell *cell = [tableView dequeueReusableCellWithIdentifier:@"VenueCell" forIndexPath:indexPath];
 
Venue *venue = _venues[indexPath.row];
cell.nameLabel.text = venue.name;
cell.distanceLabel.text = [NSString stringWithFormat:@"%.0fm", venue.location.distance.floatValue];
cell.checkinsLabel.text = [NSString stringWithFormat:@"%d checkins", venue.stats.checkins.intValue];
 
return cell;

This won’t work if you try to build and run, because Venue does not have a stats property… yet. Just as with location data, Foursquare provides venue stats in the JSON data:

"stats": {
    "checkinsCount": 3790,
    "tipCount": 40,
    "usersCount": 1460
},

And just as for the location data, you need to create a Stats data model and provide the mapping for RestKit.

  1. Create a new file called Stats, and make it a subclass of NSObject.
  2. In Stats.h, add the following properties:
    @property (nonatomic, strong) NSNumber *checkins;
    @property (nonatomic, strong) NSNumber *tips;
    @property (nonatomic, strong) NSNumber *users;
  3. In Venue.h, add the following at the top, underneath the other class forward-declaration you added earlier:
    @class Stats;
  4. Then add the following property:
    @property (strong, nonatomic) Stats *stats;
  5. In MasterViewController.m, add the following import at the top:
    #import "Stats.h"
  6. Then add the Stats mappings to the bottom of configureRestKit
    RKObjectMapping *statsMapping = [RKObjectMapping mappingForClass:[Stats class]];
    [statsMapping addAttributeMappingsFromDictionary:@{@"checkinsCount": @"checkins", @"tipsCount": @"tips", @"usersCount": @"users"}];
     
    [venueMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"stats" toKeyPath:@"stats" withMapping:statsMapping]];

For statsMapping, this time you’re changing things by using addAttributeMappingsFromDictionary: instead of addAttributeMappingsFromArray:. The Stats properties are defined checkins, tips, and users, while the JSON fields are checkinsCount, tipsCount and usersCount. This method therefore provides the mapping from the JSON key to the object’s property name.

Using addAttributeMappingsFromDictionary: comes in real handy when the JSON response fields are labeled with Objective-C keywords like id and description. This is because id is a keyword in Objective-C so you cannot use it as a property name. Similarly, description is already a method on all NSObjects. So you wouldn’t want to override that by creating a property with the same name!

Build and run, and you should see something like this:

Wow, who knew that Kaiser Permanente had an on-call Barista. ;]

Note: If you have any crashes at this point, double-check to make sure that you set the Identifier for the cell to “VenueCell” in Main.storyboard. That’s the most likely culprit.

Where to Go From Here?

Here is a example project with all of the code from the above tutorial.

Here is list of what you have done:

  • An introduction to UITableView using the Master-Detail Application template.
  • RestKit installation and configuration for your app.
  • Foursquare configuration for your app and an introduction to their Venues API.
  • Created data models for your app.
  • Setup RestKit mappings so that it can request, receive and parse the data into your data model.
  • Created a custom table view cell to display the Foursquare data.

Here is what you did not do:

  • Setup and use NSURLConnections or NSURLConnectionDelegates.
  • Parse JSON or XML data.
  • Map the JSON or XML responses to your own objects.

Because RestKit handled all of this for you! Not bad, eh?

From here, feel free set up Foursquare user authentication to gain full access to Foursquare’s API and extend this example with even more features, or find other web services that you can use RestKit to provide data for your app.

I hope you enjoyed this tutorial, and if you have any questions on it or RestKit in general, please join the forum discussion below!


This is a post by iOS Tutorial Team Member Scott McAlister, an iOS developer at Flat World Knowledge.

Scott McAlister

iOS developer at Flat World Education creating competency-based systems for higher education. Also founder at 4 Arrows Media. Spare time is spent with wife and 4 kids, scouts, backpacking and Tenkara.

User Comments

23 Comments

[ 1 , 2 ]
  • I have also been waiting for the update to this tutorial for quite some time, thank you very much!
    ritec
  • redwolf wrote:Two questions:
    - If my JSON response includes an array of objects (such as I get an order with a list of products) how do you map this in RestKit particularly when the collection of products has objects you would also like RestKit to convert to their ObjectiveC object representations?
    - Can RestKit go the other way, in other words can it convert the ObjectiveC object back to JSON and send it to the server?


    - That's what this tutorial did. It requested an array of venues. Look at the loadVenues method.

    - Yes, RestKit can convert objects to JSON and send to a server. This tutorial did not cover this. Look at https://github.com/RestKit/RestKit/#pos ... -an-object. It should give you enough information to start that process.
    scott4arrows
  • Thanks for awesome tutorial.
    How do i get whole response in NSDictionary, Here we are doing "keyPath:@"response.venues"", but how about if i need a NSString that is out side of the venue array.
    saleemsangi
  • Never Mind. I got it working.
    saleemsangi
  • Hi,

    great tutorial. I also did a version with Swift instead of Objective-C, but the venues response doesn't return any object... is there because of some compatibility problems with RestKit and Swift?

    Here are my two functions:

    Code: Select all

    func configureRestKit() {
            // initialize AFNetworking HTTPClient
            let baseURL = NSURL.URLWithString("https://api.foursquare.com")
            let client = AFHTTPClient(baseURL: baseURL)
           
            // initialize RestKit
            let objectManager = RKObjectManager(HTTPClient: client)
           
            // setup object mappings
            let venueMapping = RKObjectMapping(forClass: Venue.self)
            venueMapping.addAttributeMappingsFromArray(["name"])
           
            // register mappings with the provider using a response descriptor
            let responseDescriptor = RKResponseDescriptor(mapping: venueMapping, method: RKRequestMethod.GET, pathPattern: "/v2/venues/search", keyPath: "response.venues", statusCodes: NSIndexSet(index: 200))
           
            objectManager.addResponseDescriptor(responseDescriptor)
        }
       
        func loadVenues() {
            let latLon = "37.33,-122.03" // approximate latLon of The Mothership (a.k.a Apple headquarters)
            let queryParams = [
                "ll": latLon,
                "client_id": kCLIENTID,
                "client_secret": kCLIENTSECRET,
                "categoryId": "4bf58dd8d48988d1e0931735",
                "v" : "20140617"
            ]
           
            RKObjectManager.sharedManager().getObjectsAtPath("/v2/venues/search", parameters: queryParams,
                success:{ operation, mappingResult in
                    self.venues = mappingResult.array()
                    self.tableView.reloadData()
                },
                failure:{ operation, error in
                    NSLog("What do you mean by 'there is no coffee?': \(error!.localizedDescription)")
                }
            )
        }



    The response I got is:
    Code: Select all
    (200 OK / 0 objects)


    What did I miss?

    Thanks a lot.
    ema78
  • Is the Venue class a sub-class of NSObject in your Swift implementation?
    kelvinlg
  • I using a Git submodule and i get this error : 'RKValueTransformers.h' not found ....... ????
    KingAdrian
  • Hello guys,

    Thanks for the tutorial. I used it in my application. I have one question about AKObjectManager. In its sharedManager method I have the following

    // Access Token
    NSUserDefaults* userData = [NSUserDefaults standardUserDefaults];
    NSLog(@"Authorization field set to %@", [userData objectForKey:@"accessToken"]);
    if ([userData objectForKey:@"accessToken"]) {
    // Not too sure if this is being taken into account for the other class that inherits
    [sharedManager.HTTPClient setDefaultHeader:@"Authorization" value:[userData objectForKey:@"accessToken"]];
    }

    In order to make sure the access token passed in the API calls will always be the one I want. I thought it will be refreshed every time in the children classes by having this in their parent class but I found out that it wasn't the case. As a result I amended a child shredManager method to be like :

    + (instancetype)sharedManager {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    sharedManager = [super sharedManager];
    });

    // Access Token
    NSUserDefaults* userData = [NSUserDefaults standardUserDefaults];
    NSLog(@"Authorization field set to %@", [userData objectForKey:@"accessToken"]);
    if ([userData objectForKey:@"accessToken"]) {
    // Not too sure if this is being taken into account for the other class that inherits
    [sharedManager.HTTPClient setDefaultHeader:@"Authorization" value:[userData objectForKey:@"accessToken"]];
    }

    return sharedManager;
    }

    It now works but I would appreciate your help to understand why it is the case

    Many thanks
    kintso
[ 1 , 2 ]

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Vote for Our Next Tutorial!

Every week, we alternate between Gaming and Non-Gaming tutorial votes. This week: Non-Gaming!

    Loading ... Loading ...

Last week's winner: How to Make a Simple 2D Game with Metal.

Suggest a Tutorial - Past Results

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in October: Xcode 6 Tips and Tricks!

Sign Up - October

Our Books

Our Team

Tutorial Team

... 53 total!

Update Team

  • Zouhair Mahieddine

... 14 total!

Editorial Team

... 22 total!

Code Team

  • Orta Therox

... 3 total!

Subject Matter Experts

  • Richard Casey

... 4 total!