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 3 of 4 of this article. Click here to view the first page.

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:

And also add the following property:

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.

  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;
    
@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;
@class Location;
@property (nonatomic, strong) Location *location;
#import "Location.h"
// 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]];
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]];
    
@property (nonatomic, strong) NSNumber *checkins;
@property (nonatomic, strong) NSNumber *tips;
@property (nonatomic, strong) NSNumber *users;
@class Stats;
@property (strong, nonatomic) Stats *stats;
#import "Stats.h"
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.

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.