Introduction to RestKit Tutorial

Learn how to obtain data from Foursquare’s venues API and display it in a table in this RestKit tutorial. By .

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

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