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 .
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Introduction to RestKit Tutorial
30 mins
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:
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.
- Create a new file called Location, a subclass of NSObject.
- 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;
- 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;
- Open MasterViewController.m, add the following import at the top:
#import "Location.h"
- 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]];
- 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;
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.
@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 UILabel
s.
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.
- Create a new file called Stats, and make it a subclass of NSObject.
- In Stats.h, add the following properties:
@property (nonatomic, strong) NSNumber *checkins; @property (nonatomic, strong) NSNumber *tips; @property (nonatomic, strong) NSNumber *users;
- In Venue.h, add the following at the top, underneath the other class forward-declaration you added earlier:
@class Stats;
- Then add the following property:
@property (strong, nonatomic) Stats *stats;
- In MasterViewController.m, add the following import at the top:
#import "Stats.h"
- Then add the
Stats
mappings to the bottom of configureRestKitRKObjectMapping *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 NSObject
s. 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.