restkit - from zero to hero
DESCRIPTION
This talk explains what RestKit is and how it can help you build applications that sync with REST services. The code for this talk is available at https://github.com/peterfriese/RestKitFromzeroToHeroTRANSCRIPT
RestKitFrom Zero to Hero
Peter Friese, Zühlke Engineering
What’s that?!
What we will cover today
1
2
3
Challenges
How can RestKit help?
Demos!
Challenges...
Flaky Connectivity
Different Data Formats
Offline Data Access
2
How can RestKit help?
RestKit Features
Integrated HTTP StackPluggable ParserObject MappingCore Data IntegrationUI Integration
Integrated HTTP Stack
Base URLsCustom HeadersNetwork IndicatorPerforming RequestsBackground ProcessingAuthentication
✔
✔
✔
✔
✔
✔
Request caching✔
Base URL
// create clientRKClient *client = [RKClient clientWithBaseURL:@"http://github.org"];
Custom Headers
// send this field with each request[client setValue:[[[UIDevice currentDevice] identifierForVendor] UUIDString]
forHTTPHeaderField:@"X-UDID"];
Network Indicator
client.requestQueue.showsNetworkActivityIndicatorWhenBusy = YES;
Network Indicator
client.requestQueue.showsNetworkActivityIndicatorWhenBusy = YES;
Perform Requests
- (IBAction)forkYou:(id)sender { [[RKClient sharedClient]
get:@"https://github.com/fluidicon.png" delegate:self];
}
- (void)request:(RKRequest *)request didLoadResponse:(RKResponse *)response
{ if ([response isSuccessful]) { UIImage *image =
[UIImage imageWithData:[response body]]; self.imageView.image = image; }}
Requests with Blocks
- (IBAction)forkYouWithBlocks{ self.imageView.image = nil; [[RKClient sharedClient] get:@"http://github.com/fluidicon.png" usingBlock:^(RKRequest *request) { [request setOnDidLoadResponse:^(RKResponse *response) { if (response.isSuccessful) { UIImage *image = [UIImage imageWithData:response.body]; self.imageView.image = image; } }]; }];}
Blocks FTW
Background Processing[request setBackgroundPolicy:RKRequestBackgroundPolicyContinue];
// do nothingRKRequestBackgroundPolicyNone// cancel requestRKRequestBackgroundPolicyCancel
// continue until extra time expiresRKRequestBackgroundPolicyContinue// requeue upon app restart RKRequestBackgroundPolicyRequeue
Authentication[clientsetAuthenticationType:RKRequestAuthenticationTypeHTTPBasic];
// disable authentication
RKRequestAuthenticationTypeNone
// NSURLConnection auto negotiation
RKRequestAuthenticationTypeHTTP
// HTTP Basic Auth
RKRequestAuthenticationTypeHTTPBasic
// OAuth 1.0
RKRequestAuthenticationTypeOAuth
// OAuth 2.0
RKRequestAuthenticationTypeOAuth2
Request cachingclient.cachePolicy = RKRequestCachePolicyLoadIfOffline | RKRequestCachePolicyTimeout;
// don’t use request cacheRKRequestCachePolicyNone// use cache if offlineRKRequestCachePolicyLoadIfOffline // in case of an errorRKRequestCachePolicyLoadOnError// use ETagsRKRequestCachePolicyEtag// if we’ve got data storedRKRequestCachePolicyEnabled// in case of a timeoutRKRequestCachePolicyTimeout
Integrated HTTP StackPluggable Parser Object Mapping
JSON(new and cool)
XML(legacy)
Integrated HTTP StackPluggable Parser Object Mapping
JSON(new and cool)
XML(legacy)
Deprecated
Integrated HTTP StackPluggable Parser Object Mapping
Simple Object MappingMapping RelationshipsInverse MappingsSerialization Mappings
✔
✔
✔
✔
Integrated HTTP StackPluggable Parser Object Mapping
Integrated HTTP StackPluggable Parser Object Mapping
#->No Hashmaps
Integrated HTTP StackPluggable Parser Object Mapping
@interface GithubUser : NSObject
@property (strong, nonatomic) NSNumber *id;
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *location;
@property (strong, nonatomic) NSString *followers;
@property (strong, nonatomic) NSString *email;
@property (strong, nonatomic) NSString *following;
@end
->
Integrated HTTP StackPluggable Parser Object Mapping
@interface GithubUser : NSObject
@property (strong, nonatomic) NSNumber *id;
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *location;
@property (strong, nonatomic) NSString *followers;
@property (strong, nonatomic) NSString *email;
@property (strong, nonatomic) NSString *following;
@end
->
Integrated HTTP StackPluggable Parser Object Mapping
@interface GithubUser : NSObject
@property (strong, nonatomic) NSNumber *id;
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *location;
@property (strong, nonatomic) NSString *followers;
@property (strong, nonatomic) NSString *email;
@property (strong, nonatomic) NSString *following;
@end
{id: 232107,name: "Peter Friese",location: "Hamburg",followers: 42,email: “[email protected],following: 36,
}
Integrated HTTP StackPluggable Parser Object Mapping
@interface GithubUser : NSObject
@property (strong, nonatomic) NSNumber *id;
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *location;
@property (strong, nonatomic) NSString *followers;
@property (strong, nonatomic) NSString *email;
@property (strong, nonatomic) NSString *following;
@end
{id: 232107,name: "Peter Friese",location: "Hamburg",followers: 42,email: “[email protected],following: 36,
}
RKObjectMapping *objectMapping = [RKObjectMapping mappingForClass:[GithubUser class]];
Integrated HTTP StackPluggable Parser Object Mapping
@interface GithubUser : NSObject
@property (strong, nonatomic) NSNumber *id;
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *location;
@property (strong, nonatomic) NSString *followers;
@property (strong, nonatomic) NSString *email;
@property (strong, nonatomic) NSString *following;
@end
{id: 232107,name: "Peter Friese",location: "Hamburg",followers: 42,email: “[email protected],following: 36,
}
RKObjectMapping *objectMapping = [RKObjectMapping mappingForClass:[GithubUser class]];
register mapping
for a class
Integrated HTTP StackPluggable Parser Object Mapping
@interface GithubUser : NSObject
@property (strong, nonatomic) NSNumber *id;
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *location;
@property (strong, nonatomic) NSString *followers;
@property (strong, nonatomic) NSString *email;
@property (strong, nonatomic) NSString *following;
@end
{id: 232107,name: "Peter Friese",location: "Hamburg",followers: 42,email: “[email protected],following: 36,
}
RKObjectMapping *objectMapping = [RKObjectMapping mappingForClass:[GithubUser class]];
register mapping
for a class
[objectMapping mapKeyPath:@"id" toAttribute:@"id"];[objectMapping mapKeyPath:@"name" toAttribute:@"name"];
Integrated HTTP StackPluggable Parser Object Mapping
@interface GithubUser : NSObject
@property (strong, nonatomic) NSNumber *id;
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *location;
@property (strong, nonatomic) NSString *followers;
@property (strong, nonatomic) NSString *email;
@property (strong, nonatomic) NSString *following;
@end
{id: 232107,name: "Peter Friese",location: "Hamburg",followers: 42,email: “[email protected],following: 36,
}
RKObjectMapping *objectMapping = [RKObjectMapping mappingForClass:[GithubUser class]];
register mapping
for a class
[objectMapping mapKeyPath:@"id" toAttribute:@"id"];[objectMapping mapKeyPath:@"name" toAttribute:@"name"]; configure mapping (KVC)
Integrated HTTP StackPluggable Parser GETting Objects
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:
usingBlock:^(RKObjectLoader *loader) {
}];
Integrated HTTP StackPluggable Parser GETting Objects
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[NSString stringWithFormat:@"/users/%@", self.userName]
usingBlock:^(RKObjectLoader *loader) {
[loader setObjectMapping:self.mapping];
}];
Integrated HTTP StackPluggable Parser GETting Objects
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[NSString stringWithFormat:@"/users/%@", self.userName]
usingBlock:^(RKObjectLoader *loader) {
[loader setObjectMapping:self.mapping];
}];
resource path
mapping, as defined previously
Integrated HTTP StackPluggable Parser GETting Objects
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[NSString stringWithFormat:@"/users/%@", self.userName]
usingBlock:^(RKObjectLoader *loader) {
[loader setObjectMapping:self.mapping];[loader setOnDidLoadObject:^(id object) {
}];[loader setOnDidFailWithError:^(NSError *error) {
}];}];
Integrated HTTP StackPluggable Parser GETting Objects
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[NSString stringWithFormat:@"/users/%@", self.userName]
usingBlock:^(RKObjectLoader *loader) {
[loader setObjectMapping:self.mapping];[loader setOnDidLoadObject:^(id object) {
}];[loader setOnDidFailWithError:^(NSError *error) {
}];}];
happy case :-)
sad unhappy case :-(
Integrated HTTP StackPluggable Parser GETting Objects
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[NSString stringWithFormat:@"/users/%@", self.userName]
usingBlock:^(RKObjectLoader *loader) {
[loader setObjectMapping:self.mapping];[loader setOnDidLoadObject:^(id object) {[self.root bindToObject:object];[self.quickDialogTableView reloadData];
}];[loader setOnDidFailWithError:^(NSError *error) {
}];}];
sad unhappy case :-(
Integrated HTTP StackPluggable Parser GETting Objects
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[NSString stringWithFormat:@"/users/%@", self.userName]
usingBlock:^(RKObjectLoader *loader) {[SVProgressHUD showWithStatus:@"Loading..."];[loader setObjectMapping:self.mapping];[loader setOnDidLoadObject:^(id object) {[self.root bindToObject:object];[self.quickDialogTableView reloadData];[SVProgressHUD dismiss];
}];[loader setOnDidFailWithError:^(NSError *error) {[SVProgressHUD showErrorWithStatus:@"Problem loading user"];
}];}];
Integrated HTTP StackPluggable Parser GETting Objects
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[NSString stringWithFormat:@"/users/%@", self.userName]
usingBlock:^(RKObjectLoader *loader) {[SVProgressHUD showWithStatus:@"Loading..."];[loader setObjectMapping:self.mapping];[loader setOnDidLoadObject:^(id object) {[self.root bindToObject:object];[self.quickDialogTableView reloadData];[SVProgressHUD dismiss];
}];[loader setOnDidFailWithError:^(NSError *error) {[SVProgressHUD showErrorWithStatus:@"Problem loading user"];
}];}];
heads up!
Integrated HTTP StackPluggable Parser GETting Objects
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[NSString stringWithFormat:@"/users/%@", self.userName]
usingBlock:^(RKObjectLoader *loader) {[SVProgressHUD showWithStatus:@"Loading..."];[loader setObjectMapping:self.mapping];[loader setOnDidLoadObject:^(id object) {[self.root bindToObject:object];[self.quickDialogTableView reloadData];[SVProgressHUD dismiss];
}];[loader setOnDidFailWithError:^(NSError *error) {[SVProgressHUD showErrorWithStatus:@"Problem loading user"];
}];}];
Integrated HTTP StackPluggable Parser Mapping Relationships
->
RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[GithubUser class]];
RKObjectMapping *issueMapping = [RKObjectMapping mappingForClass:[GithubIssue class]];
[issueMapping mapKeyPath:@"user" toRelationship:@"user" withMapping:userMapping];
[ { "number": 1347, "title": "Found a bug", "user": { "login": "octocat",
@interface GithubIssue : NSObject
@property NSNumber *number;@property NSString *title;@property GithubUser *user;
Integrated HTTP StackPluggable Parser Mapping Relationships
->
RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[GithubUser class]];
RKObjectMapping *issueMapping = [RKObjectMapping mappingForClass:[GithubIssue class]];
[issueMapping mapKeyPath:@"user" toRelationship:@"user" withMapping:userMapping];
[ { "number": 1347, "title": "Found a bug", "user": { "login": "octocat",
@interface GithubIssue : NSObject
@property NSNumber *number;@property NSString *title;@property GithubUser *user;
Integrated HTTP StackPluggable Parser POSTing Objects
->
[[objectManager mappingProvider] setObjectMapping:issueMapping forKeyPath:@""];
RKObjectMapping *issueSerializationMapping = [issueMapping inverseMapping];
[[[RKObjectManager sharedManager] mappingProvider]setSerializationMapping:issueSerializationMapping forClass:[GithubIssue class]];
[[[RKObjectManager sharedManager] router]routeClass:[GithubIssue class] toResourcePath:@"/repos/:repouser/:repo/issues"forMethod:RKRequestMethodPOST ];
[[[RKObjectManager sharedManager] router] routeClass:[GithubIssue class] toResourcePath:@"/repos/:repouser/:repo/issues/:number"];
Integrated HTTP StackPluggable Parser POSTing Objects
->
GithubIssue *issue = [[GithubIssue alloc] init];
issue.repouser = repouser;issue.repo = repo;
[[RKObjectManager sharedManager] postObject:issue usingBlock:^(RKObjectLoader *loader){loader.onDidLoadResponse = ^(RKResponse *response) {[self dismissViewControllerAnimated:YES completion:nil];
}}];
1: Create new object
2: ProvideInfos for RestKit router:
3: POST object:
Integrated HTTP StackPluggable ParserObject Mapping Core Data Integration
Offline Data Access
Remember?
Integrated HTTP StackPluggable ParserObject Mapping Core Data Integration
Change Mapped Objects
Add a Core Data ModelRegister a Managed Object Store
Adjust Object Mappings
Adjust Object CreationFetch Data from DB / Backend
Integrated HTTP StackPluggable ParserObject Mapping Change Mapped Objects
@interface GithubUser : NSManagedObject
@interface GithubUser : NSObject
@synthesize id;@synthesize login;@synthesize name;@synthesize company;@synthesize location;@synthesize blog;@synthesize following;@synthesize followers;@synthesize email;
@dynamic id;@dynamic login;@dynamic name;@dynamic company;@dynamic location;@dynamic blog;@dynamic following;@dynamic followers;@dynamic email;
Header Header
Module Module
Integrated HTTP StackPluggable ParserObject Mapping Add a Core Data Model
Integrated HTTP StackPluggable ParserObject Mapping Add a Core Data Model
@interface GithubUser : NSObject
Integrated HTTP StackPluggable ParserObject Mapping Add a Core Data Model
Keep in mind:Assign respective
classes to managed objects!
@interface GithubUser : NSObject
Integrated HTTP StackPluggable ParserObject Mapping Register a Managed Object Store
// set up object managerRKObjectManager *objectManager = [RKObjectManager objectManagerWithBaseURL:@"https://api.github.com"];
// set up backing data storeobjectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:@"github.sqlite"];
Integrated HTTP StackPluggable ParserObject Mapping Adjust Object Mappings
RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[GithubUser class]];
RKManagedObjectMapping *userMapping = [RKManagedObjectMapping mappingForClass:[GithubUser class] inManagedObjectStore:objectStore];
Integrated HTTP StackPluggable ParserObject Mapping Adjust Object CreationGithubIssue *issue = [[GithubIssue alloc] init];
GithubIssue *issue = [GithubIssue object];
+ (id)object {
id object = [[self alloc]
initWithEntity:[self entity]
insertIntoManagedObjectContext:
[NSManagedObjectContext contextForCurrentThr
ead]];
return [object autorelease];
}
Integrated HTTP StackPluggable ParserObject Mapping Fetch Data from DB / Backend
if ([[RKObjectManager sharedManager] isOnline]) {[self fetchDataFromRemote];
}else {[self fetchDataFromDataStore];
}
Online of offline?
- (void)fetchDataFromDataStore {NSFetchRequest *request = [[[RKObjectManager sharedManager] mappingProvider] fetchRequestForResourcePath:self.resourcePath];
self.repos = [GithubRepo objectsWithFetchRequest:request];[self.tableView reloadData];
}
Offline - Fetch from DB
Integrated HTTP StackPluggable ParserObject Mapping Fetch Data from DB / Backend
- (void)fetchDataFromRemote {[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[self resourcePath] usingBlock:^(RKObjectLoader *loader) {[loader setOnDidLoadObjects:^(NSArray *objects) {self.repos = objects;[self.tableView reloadData];
}];}];
}
Online - Fetch from Backend
Integrated HTTP StackPluggable ParserObject Mapping Fetch Data from DB / Backend
- (void)reachabilityChanged:(NSNotification*)notification {RKReachabilityObserver* observer = (RKReachabilityObserver*)[notification object];
if ([observer isNetworkReachable]) {if (![self.view isHidden]) {[self fetchDataFromRemote];
}} else {if (![self.view isHidden]) {[self fetchDataFromDataStore];
}}
}
Reconnect after offline
Integrated HTTP StackPluggable ParserObject Mapping Sync Managerhttps://github.com/RestKit/RestKit/pull/573
Integrated HTTP StackPluggable ParserObject Mapping Seeding your DB
[RKManagedObjectSeedergenerateSeedDatabaseWithObjectManager:objectManager fromFiles:@"repos.json", nil];
RKManagedObjectStore *objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:@"github.sqlite" usingSeedDatabaseName:@"seed_db.sqlite" managedObjectModel:nildelegate:nil];
Generate seed
Import seed
Integrated HTTP StackPluggable ParserObject Mapping Putting it All Together
Integrated HTTP StackPluggable ParserObject Mapping Putting it All Together
Wait a minute - can we do this simpler?
Integrated HTTP StackPluggable ParserObject Mapping RestKit UI
Static TablesNetworked TableForms
✔
✔
✔
Integrated HTTP StackPluggable ParserObject Mapping RestKit UI - Networked Table
self.tableController = [[RKObjectManager sharedManager] fetchedResultsTableControllerForTableViewController:self];
self.tableController.resourcePath = [self resourcePath];
Integrated HTTP StackPluggable ParserObject Mapping RestKit UI - Networked Table
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"createdAt" ascending:NO];
self.tableController.sortDescriptors = [NSArray arrayWithObject:descriptor];
Sorting
self.tableController = [[RKObjectManager sharedManager] fetchedResultsTableControllerForTableViewController:self];
self.tableController.resourcePath = [self resourcePath];
Integrated HTTP StackPluggable ParserObject Mapping RestKit UI - Networked Table
self.tableController.autoRefreshFromNetwork = YES;self.tableController.pullToRefreshEnabled = YES;
Other nice stuff
self.tableController = [[RKObjectManager sharedManager] fetchedResultsTableControllerForTableViewController:self];
self.tableController.resourcePath = [self resourcePath];
Integrated HTTP StackPluggable ParserObject Mapping RestKit UI - Cell Mapping
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
cellMapping.style = UITableViewCellStyleValue1;[cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"];
[cellMappingmapKeyPath:@"openIssues" toAttribute:@"detailTextLabel.text"];
[cellMappingsetAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
[tableController mapObjectsWithClass:[GithubRepo class] toTableCellsWithMapping:cellMapping];
Thanks!
Remember that little spot
on the first slide?
http://www.slideshare.net/peterfriese
Awesome Unicorn Coloring Slide
Anyway, Thanks!
Peter [email protected]
Zühlke Engineering GmbH
Am Sandtorkai 6620457 Hamburg
+49 151 108 604 72
Available for consulting,projects, discussing all things mobile - and frosty beverages!
CouchDB Mobile - from Couch to 5K in 1 Hour