Connecting to a REST API in iOS

Post on 09-May-2015

16.676 views 7 download

description

This is a presentation I gave at the combined Tokyo Rubyist / iOS Event.

Transcript of Connecting to a REST API in iOS

Connecting an iOS App to a REST API

Matthew Gillingham

@gillygize / @TokyoiOSMeetup

The Basic Workflow

1.Use a networking library to connect to the web service.

2.Download the server response (probably JSON or XML).

3.Parse the response by transforming it into an NSDictionary or NSArray.

4.Turn the dictionary in a data model class.

5.Store the data locally (probably with Core Data)

Networking Libraries

•AFNetworking

•ASIHTTPRequest (the old standard, no longer maintained)

•MKNetworkKit

•NSURLConnection

•FSNetworking

•libcurl

AFNetworking

•Actively-maintained and widely used

•Simple interface

•Made with REST APIs in mind (using AFHTTPClient)

•Parses JSON automatically

[[AFHTTPClient sharedClient] getPath:@”path” parameters:nil success:^(AFHTTPRequestOperation *operation, id JSON) { // handle success } failure:^(AFHTTPRequestOperation *operation, NSError *error) { // handle failure }];

AFNetworkingGET /path

Return NSDataParse into

NSDictionary Server

Loading the Object

CBPost *post = [NSEntityDescription insertNewObjectForEntityForName:@"Post" inManagedObjectContext:self.managedObjectContext]; post.text = JSON[@”text”];

//...

[self.managedObjectContext save:&error];

•A System for Managing Data

•It creates an “abstract” set of entities describing your app’s data model

•It includes tools for persistence

•Using it is an intermediate-level topic and has a lot of features, so we can not cover it in depth.

Core Data

Core Data

•For these types of web service apps, Core Data is mainly used for persistence, like a cache.

•It allows the screens to have data, even when the app first launches or there is no network connection.

How It Works?• You have a model which contains “entities”

in an Object Graph

• These “entities” map to objects (NSManagedObjects)

• The objects are used and connected together in a “context” (NSManagedObjectContext)

• The context has an associated “persistent store coordinator” (NSPersistentStore). When the context is saved, the store coordinator writes to the disk.

Challenges

• You should not use NSManagedObjectContexts on more than one thread (thread confinement).

• Reading from a network is slow. Writing to the disk is slow. If possible, you do not want to do these things on the main thread.

• But you do want to use NSManagedObjects on the main thread for UI.

Challenges

•The main challenge of using Core Data has been figuring out how to create NSManagedObjects from a background and use them on various threads for displaying UI and saving to the disk.

Basic AnswerParent Parent ContexContex

tt

Child Child ContexContex

tt

PersistentPersistentStoreStore

CoordinatoCoordinatorr

Main QueuePrivate Queue

• Before iOS 5, there was a method which used NSNotifications.

• Since iOS 5, NSManagedObjectContexts can have both concurrency types (confinement, private, main) and parents. Children can pull the contents from their parent when they are created (but changes to the child do not propagate upwards automatically).

• RestKit, the tool we are using, uses an NSManagedObjectContext in a private queue to write to the disk. It has a child context with a main queue type to interact with the main queue. It uses (short-lived) contexts to create the NSManagedObjects from the JSON objects.

New Advances

•AFIncrementalStore

•RestKit

AFIncrementalStore

•Written by the author of AFNetworking

•Uses a recent, more obscure feature of Core Data called NSIncrementalStore.

•It is interesting because it allows you access the whole stack from Core Data.

•I am worried that it is too closely tied to Core Data to be practical.

RestKit

•More mature (but somewhat often rewritten)

•More or less a consolidation of the workflow I described above.

•Now uses AFNetworking under the hood.

•Maybe has too many features(?). There are multiple ways to do the same thing and it was not always clear which was preferred.

RestKit

1.Tell it about your Core Data model. It sets up you persistent store and contexts for you.

2.Tell it what your various REST endpoints are on the server.

3.Tell it how to take the JSON and map it to your Core Data entities.

RKManagedObjectStore

_managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:self.managedObjectModel]; [_managedObjectStore createPersistentStoreCoordinator];

NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"CommunityBoard.sqlite"]; NSError *error = nil; NSPersistentStore *persistentStore = [_managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error]; [_managedObjectStore createManagedObjectContexts];

RKEntityMapping

RKEntityMapping *communityResponseMapping = [RKEntityMapping mappingForEntityForName:@"Community" inManagedObjectStore:self.managedObjectStore]; communityResponseMapping.identificationAttributes = @[ @"communityId" ]; [communityResponseMapping addAttributeMappingsFromDictionary:@{ @"id": @"communityId", @"created_at": @"createdAt", @"post_count": @"postCount" }]; [communityResponseMapping addAttributeMappingsFromArray:@[@"name"]];

RKResponseDescriptor

RKResponseDescriptor *communityResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:communityResponseMapping pathPattern:[CBAPI communitiesPath] keyPath:@"communities" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]; [self addResponseDescriptor:communityResponseDescriptor];

Also, Authentication

•Setting up OAuth is not difficult. There are libraries, such as AFOAuth2Client, which we use.

•You will need an application id and secret bundled with your app.

•You should save the OAuth token you receive on the User’s Keychain.

•AFOAuthCredential (which comes with AFOAuth2Client) does that for you.

Example AFOAuth2Client *oauthClient = [AFOAuth2Client clientWithBaseURL:baseURL clientID:applicationID secret:secret];

[oauthClient authenticateUsingOAuthWithPath:@”/oauth/token” username:username password:password scope:nil success:^(AFOAuthCredential *credential){ [AFOAuthCredential storeCredential:credential withIdentifier:CBCredentialIdentifier]; } failure:^(NSError *error){ // Handle Error }];