iOS Api Client: soluzioni a confronto

Post on 22-Apr-2015

847 views 0 download

description

Slides della sessione su iOS Api Client: soluzioni a confronto, tenuta da Massimo Oliviero e Francesco Sinopoli alla WhyMCA 2012. http://www.whymca.org/intervento/ios-api-client-soluzioni-confronto ----- Abbiamo sognato un giorno in cui REST diventerà lo standard per tutti i servizi web, le API saranno progettate e documentate come quelle di Twitter, tutti capiranno il significato di concetti come Risorsa, URI e HATEOAS e il mondo sarà per sempre riconoscente a Roy Fielding. Abbiamo sognato … Poi ci siamo svegliati e siamo andati al lavoro. Puntualmente abbiamo trovato il seguente scenario: applicazione critica, progetto in scadenza, API server inviolabili. Vi ricordate quella battuta in Apollo 13: come incastrare un tubo tondo in un boccaporto quadrato? In questa sessione mostreremo alcune pratiche, prodotte dalle nostre Lesson Learned, per realizzare un client iOS chiamato ad interagire con API remote ponendo l'accento sull'architettura software.

Transcript of iOS Api Client: soluzioni a confronto

iOS Api ClientSoluzioni a confronto

Massimo Oliviero - Cappery S.r.l.Francesco Sinopoli - Tiltap S.r.l.

Monday, May 28, 12

Chi siamo

Massimo Olivierohttp://www.massimooliviero.net - @maxoly

Co-founder & CEO di Cappery www.cappery.com

Co-founder di # pragma mark www.pragmamark.org

Monday, May 28, 12

Disclaimer

• Non è una sessione su REST

• Non è una sessione su sulle API Server

• Non vuole essere una sessione esauriente

Monday, May 28, 12

Agenda

• Architettura

• Framework custom

• Framework terze parti: REST Kit

Monday, May 28, 12

Scenari

Monday, May 28, 12

Scenari

• Scenario AServer Api non REST(ful)

Monday, May 28, 12

Scenari

• Scenario AServer Api non REST(ful)

• Scenario BServer API REST(ful)

Monday, May 28, 12

Soluzioni

Monday, May 28, 12

Soluzioni

A. Easy (w lo spaghetti code!)

Monday, May 28, 12

Soluzioni

A. Easy (w lo spaghetti code!)

B. Framework custom

Monday, May 28, 12

Soluzioni

A. Easy (w lo spaghetti code!)

B. Framework custom

C. Framework di terze parti

Monday, May 28, 12

Un nuovo progetto

Monday, May 28, 12

Un nuovo progetto

Monday, May 28, 12

Un nuovo progetto• App per la N.A.S.A.

Monday, May 28, 12

Un nuovo progetto• App per la N.A.S.A.

• Con tre view (Lista pianeti + Lista rover sul pianeta + dettaglio rover)

Monday, May 28, 12

Un nuovo progetto• App per la N.A.S.A.

• Con tre view (Lista pianeti + Lista rover sul pianeta + dettaglio rover)

• Il dettaglio deve mostrare alcuni parametri del rover (es. Opportunity su Marte)

Monday, May 28, 12

Un nuovo progetto• App per la N.A.S.A.

• Con tre view (Lista pianeti + Lista rover sul pianeta + dettaglio rover)

• Il dettaglio deve mostrare alcuni parametri del rover (es. Opportunity su Marte)

• Lettura dei dati tramite server API della NASA

Monday, May 28, 12

Un nuovo progetto• App per la N.A.S.A.

• Con tre view (Lista pianeti + Lista rover sul pianeta + dettaglio rover)

• Il dettaglio deve mostrare alcuni parametri del rover (es. Opportunity su Marte)

• Lettura dei dati tramite server API della NASA

• 10 gg/u

Monday, May 28, 12

Dominio

Planet Rover Cam1 n 1 n

Geo

1

1

Monday, May 28, 12

Server

• REQUESTGET /opportuniy_get_info.aspx?id=11A1&planet=mars

• RESPONSEcontent-type: text/html

Monday, May 28, 12

Server

• REQUESTGET /opportuniy_get_info.aspx?id=11A1&planet=mars

• RESPONSEcontent-type: text/html

Monday, May 28, 12

Non ci REST che piang!

Monday, May 28, 12

Non ci REST che piang!

Monday, May 28, 12

Non ci REST che piang!

Accettiamo la sfida!

Monday, May 28, 12

Soluzione Bsviluppiamo un framework custom

Monday, May 28, 12

Framework custom

Domande

• Cosa vuol dire sviluppare un framework custom?

• Quando sviluppare un framework custom?

• Quali sono i vantaggi?

• Quali sono gli svantaggi?

Monday, May 28, 12

Architetturaun approccio graduale

Monday, May 28, 12

Layers

Monday, May 28, 12

LayersView

Monday, May 28, 12

LayersView

Controller

Monday, May 28, 12

Layers

Network Layer

View

Controller

Monday, May 28, 12

Layers

Network Layer

View

Controller

Domain Model Layer

Monday, May 28, 12

Layers

Network Layer

View

Controller

Domain Model Layer

Data Layer

Monday, May 28, 12

Layers

Network Layer

View

Controller

Domain Model Layer

Data Layer

Business Layer

Monday, May 28, 12

Layers

Network Layer

View

Controller

Domain Model Layer

Data Layer

Business Layer

Monday, May 28, 12

Example Code

PMStarterKitPragma Mark Starter Kithttps://github.com/pragmamark/PMStarterKit

PMTouchPragma Mark iOS General Purpose Libraryhttps://github.com/pragmamark/PMTouch

Monday, May 28, 12

Network Layeril primo mattoncino della nostra architettura

Monday, May 28, 12

Network Layer

Cosa deve fare

• Gestire URL e risorse

• Gestire request e response

• Gestire proxy & authentication

• Gestire la cache (Cache-Control & ETag)

Monday, May 28, 12

Request

Monday, May 28, 12

Request

Request

Network Layer

Monday, May 28, 12

Request

Cosa deve fare

• Incapsulare una singola chiamata di rete ad una risorsa (URL) specifica

• Gestire i verbi HTTP (GET, POST, PUT, DELETE..)

• Gestire i parametri

Monday, May 28, 12

NSURLConnection- (void)viewDidLoad{ [super viewDidLoad]; static NSString *url = @"http://..../?p1=v1&p2=v2"; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; self.connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];}

.....

- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)data- (void)connection:(NSURLConnection *)theConnection didFailWithError:(NSError *)error- (void)connectionDidFinishLoading:(NSURLConnection *)theConnection- (void)connection:(NSURLConnection *)theConnection didReceiveResponse:(NSURLResponse *)response

Monday, May 28, 12

Monday, May 28, 12

NSURLConnection

Monday, May 28, 12

NSURLConnectionPro

Monday, May 28, 12

NSURLConnectionPro

• Nativo

Monday, May 28, 12

NSURLConnectionPro

• Nativo

• Documentato

Monday, May 28, 12

NSURLConnectionPro

• Nativo

• Documentato

• Flessibile

Monday, May 28, 12

NSURLConnectionPro

• Nativo

• Documentato

• Flessibile

Monday, May 28, 12

NSURLConnectionPro

• Nativo

• Documentato

• Flessibile

Monday, May 28, 12

NSURLConnectionPro

• Nativo

• Documentato

• Flessibile

Contro

Monday, May 28, 12

NSURLConnectionPro

• Nativo

• Documentato

• Flessibile

Contro

• Povero

Monday, May 28, 12

NSURLConnectionPro

• Nativo

• Documentato

• Flessibile

Contro

• Povero

• Verboso

Monday, May 28, 12

Alternative

• ASIHTTPRequesthttps://github.com/pokeb/asi-http-request/tree

• AFNetwork (by Gowalla)https://github.com/AFNetworking/AFNetworking

• MKNetworkKithttps://github.com/MugunthKumar/MKNetworkKit

Monday, May 28, 12

ASIHTTPRequest- (void)viewDidLoad{ [super viewDidLoad]; NSURL *url = [NSURL URLWithString:@"http://www.apple.com"]; ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:url]; self.request = request; [request release]; [self.request setDelegate:self]; [self.request startAsynchronous];}

- (void)requestStarted:(ASIHTTPRequest *)request{}

- (void)requestFinished:(ASIHTTPRequest *)request{}

- (void)requestFailed:(ASIHTTPRequest *)request{}

Monday, May 28, 12

AFNetworking

• HTTP Requests (cancelled, suspended / resumed)

• Authenticating requests with HTTP Basic credentials or an OAuth token

• Blocks

• feature-rich APIs

Monday, May 28, 12

AFNetworkigNSURL *url = [NSURL URLWithString:@"http://api.twitter.com/1/statuses/public_timeline.json"];

NSURLRequest *request = [NSURLRequest requestWithURL:url];

AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { NSLog(@"Public Timeline: %@", JSON);} failure:nil];[operation start];

Monday, May 28, 12

Response

Network Layer

Monday, May 28, 12

Response

Network Layer

Monday, May 28, 12

Response

Request

Network Layer

Monday, May 28, 12

Response

Request Response

Network Layer

Monday, May 28, 12

Response

Request Response

Network Layer

Monday, May 28, 12

Response

Request Response

Network Layer

Monday, May 28, 12

Response

Request Response

Network Layer

Monday, May 28, 12

Response

Cosa deve fare

• Incapsulare l’HTTP Response (Header, Status code etc.)

• Payload (HTTP Content body)

• Result (JSON > NSArray/NSDictionary etc.)

• Error

Monday, May 28, 12

Response@interface PMTResponse : NSObject{ PMTRequest *_request; NSString *_contentBody; NSError *_error; NSObject *_result;}

@property (nonatomic, retain, readonly) PMTRequest *request;@property (nonatomic, retain, readonly) NSString *contentBody;@property (nonatomic, retain, readonly) NSError *error;@property (nonatomic, retain, readonly) NSObject *result;

- (id)initWithRequest:(PMTRequest *)request;- (id)initWithRequest:(PMTRequest *)request result:(NSObject *)result;- (id)initWithRequest:(PMTRequest *)request result:(NSObject *)result error:(NSError *)error;

@end

Monday, May 28, 12

Parser

Cosa deve fare

• Trasformare stringhe o dati binari in strutture dati native (es. da JSon a NSArray).

Monday, May 28, 12

JSON Parser

Alcune librerie

• NSJSONSerialization

• JSONKit

• SBJson

Benckmarkshttps://github.com/samsoffes/json-benchmarks

Monday, May 28, 12

XML ParserAlcune librerie

• NSXMLParser (modalità SAX)

• libxml2 (C Library SAX e DOM)

• TBXML (DOM)

• TouchXML (NSXML DOM)

• KissXML (NSXML DOM)

• TinyXML (C Library DOM)

Monday, May 28, 12

Cache

Network Layer

Monday, May 28, 12

Cache

Network Layer

Monday, May 28, 12

Cache

Request

Network Layer

Monday, May 28, 12

Cache

Request Response

Network Layer

Monday, May 28, 12

Cache

Request Response Cache

Network Layer

Monday, May 28, 12

Cache

Request Response Cache

Network Layer

Monday, May 28, 12

Cache

Request Response Cache

Network Layer

Monday, May 28, 12

Cache

Cosa deve fare

• Gestire la direttiva cache-control

• Gestire la direttiva ETag

• Storage policy: per session o permanent

Monday, May 28, 12

Cache

• A cosa serveA gestire la cache

• Quando utilizzarlaQuando il server lo prevede e lo gestisce in modo efficente ed efficace con le direttive HTTP.

• Perché utilizzarlaPerché diminuisce il traffico di rete e di conseguenza aumenta la responsività dell’app.

Monday, May 28, 12

Client

Network Layer

Monday, May 28, 12

Client

Network Layer

Monday, May 28, 12

Client

Request

Network Layer

Monday, May 28, 12

Client

Request Response

Network Layer

Monday, May 28, 12

Client

Request Response Cache

Network Layer

Monday, May 28, 12

Client

Request Response Cache Client

Network Layer

Monday, May 28, 12

Client

Request Response Cache Client

Network Layer

Monday, May 28, 12

Client

Cosa deve fare

• Gestire ambienti diversi (produzione, sviluppo, stage, etc.)

• Aprire e chiudere tunnel ssh o vpn

• Gestire l’autenticazione

Monday, May 28, 12

Client

• A cosa serveAd incapsulare le request in ambienti specifici.

• Quando utilizzarloQuando si gestiscono ambienti diversi (stage, prod. etc.) o quando si utilizzano tunnel SSH o VPN.

• Perché utilizzarloPer isolare la gestione di queste specifiche funzionalità.

Monday, May 28, 12

Client@interface PMTClient : NSObject{ NSURL *_baseUrl; NSString *_username; NSString *_password;}

@property (nonatomic, retain, readonly) NSURL *baseUrl;@property (nonatomic, copy) NSString *username;@property (nonatomic, copy) NSString *password;

- (id)initWithBaseURL:(NSURL *)baseUrl;- (id)initWithBaseURL:(NSURL *)baseUrl username:(NSString *)username password:(NSString *)password;

- (PMTRequest *)createRequest:(NSString *)stringUrl delegate:(id<PMTRequestDelegate>)delegate;

Monday, May 28, 12

Network Manager

Network Layer

Monday, May 28, 12

Network Manager

Request

Network Layer

Monday, May 28, 12

Network Manager

Request Response

Network Layer

Monday, May 28, 12

Network Manager

Request Response Cache

Network Layer

Monday, May 28, 12

Network Manager

Request Response Cache Client

Network Layer

Monday, May 28, 12

Network Manager

Request Response Cache Client

Network Manager

Network Layer

Monday, May 28, 12

Network Manager

Cosa deve fare

• Creare ed eseguire le request tramite il client

• Gestire le code di request

• Gestire il suspend e il resume

Monday, May 28, 12

Network Manager• A cosa serve

A governare il network layer.

• Quando utilizzarloQuando si vuole disaccopiare e gestire attività complesse come le code, etc.

• Perché utilizzarloDisaccoppia il view controller dall’implementazione di rete e di conseguenza aumenta la manutenibilità del codice.

Monday, May 28, 12

Network Manager@interface PMTNetworkManager : NSObject{ SPKRequestQueue *_requestQueue;}

@property (nonatomic, retain, readonly) PMTRequestQueue *requestQueue;@property (nonatomic, assing) BOOL enableSuspendResume;

- (PMTRequest *)requestAdd:(NSString *)request params:(NSDictionary *)params- (PMTRequest *)requestAdd:(NSString *)request params:(NSDictionary *)params queue:(NSString *)queueName;

- (void)addQueue:(NSString *)queueName;

- (void)requestsStart;- (void)requestsStop;

@end

Monday, May 28, 12

Network Layer

Quando utilizzare un networking framework?

• SEMPRE, quando possibile evitare di utilizzare direttamente NSURLConnection. Non ha senso.

• Framework come ASIHTTPRequest o AFNetworking semplicano troppo la vita per non essere utilizzati. (Twitter e Facebook docet)

Monday, May 28, 12

Network Layer

Monday, May 28, 12

Network LayerIn conclusione

• HTTP Request & Response

• Tunneling SSH/VPN

• Ambienti (stage, produzione etc.)

• Cache (HTTP)

• Network Manager

Monday, May 28, 12

Network LayerIn conclusione

• HTTP Request & Response

• Tunneling SSH/VPN

• Ambienti (stage, produzione etc.)

• Cache (HTTP)

• Network Manager

Monday, May 28, 12

One more thing...

Monday, May 28, 12

One more thing...

Monday, May 28, 12

One more thing...

“Oltre che leggere l’app deve poter anche inviare dei comandi al rover!”

Monday, May 28, 12

Domain Model Layermodelliamo il nostro business

Monday, May 28, 12

Domain Model Layer

Cosa deve fare

• Gestire il dato (entità e relazioni)

• Gestire il comportamento

• Convertire il dato da una forma ad un’altra

Monday, May 28, 12

Model

Domain Model Layer

Monday, May 28, 12

Model

Domain Model Layer

Monday, May 28, 12

Model

Model

Domain Model Layer

Monday, May 28, 12

Model

Model

Domain Model Layer

Monday, May 28, 12

Model

Cosa deve fare

• Rappresentare l’informazione attraverso l’ausilio di una struttura dati nativa o custom

• Possibilmente oltre al dato dovrebbe incorporare anche il comportamento

Monday, May 28, 12

NSDictionary & NSArray

NSDictionary *element = [self.elements objectAtIndex:indexPath.row]; cell.textLabel.text = [element objectForKey:@"name"]; cell.detailTextLabel.text = [element objectForKey:@"description"];

Monday, May 28, 12

NSObject@interface PMSKPlanet : NSObject

@property (nonatomic, copy, readonly) NSString *name;@property (nonatomic, retain, readonly) PMSKGalaxy *galaxy;

- (id)initWithName:(NSString *)name galaxy:(PMSKGalaxy *)galaxy;

@end

@interface PMSKGalaxy : NSObject

@property (nonatomic, copy, readonly) NSString *name;@property (nonatomic, retain, readonly) NSArray *planets;

- (id)initWithName:(NSString *)name;

@end

Monday, May 28, 12

Mapper

Domain Model Layer

Monday, May 28, 12

Mapper

Domain Model Layer

Monday, May 28, 12

Mapper

Domain Model Layer

Model

Monday, May 28, 12

Mapper

Domain Model Layer

Model Mapper

Monday, May 28, 12

Mapper

Cosa deve fare

• Definire le regole di traformazione da una struttura dati ad un’altra (es. NSArray to NSObject)

• Gestire le regole e fornirle su richiesta attraverso un sistema centralizzato

Monday, May 28, 12

Object Mapper@interface PMTObjectMapper : NSObject

- (void)mapKey:(NSString *)mapKey toProperty:(NSString *)property;

+ (PMTObjectMapper *)mapperForClass:(Class)class;

@end

PMTObjectMapper *mapper = [PMTObjectMapper mapperForClass:[PMSKGalaxy class]];[mapper mapKey:@"planet_name" toProperty:@"name"];[mapper mapKey:@"solar_distance" toProperty:@"solarDistance"];

Monday, May 28, 12

Domain Model

Monday, May 28, 12

Domain Model

In conclusione

• Entità e relazioni

• Mappatura

Monday, May 28, 12

Domain Model

In conclusione

• Entità e relazioni

• Mappatura

Monday, May 28, 12

One more “little” thing

Monday, May 28, 12

One more “little” thing

Monday, May 28, 12

One more “little” thing

“L’applicazione deve funzionare anche OFFLINE”

Monday, May 28, 12

Data Layerpersistiamo i nostri dati

Monday, May 28, 12

Data Layer

Cosa deve fare

• Persistere le informazioni

• Recuperare le informazioni

Monday, May 28, 12

Store

Data Layer

Monday, May 28, 12

Store

Data Layer

Monday, May 28, 12

Store

Store

Data Layer

Monday, May 28, 12

Store

Store

Data Layer

Monday, May 28, 12

File system

• File systemNSCoding, NSKeyedArchiver, NSKeyedUnarchiver

• PlistNSDictionary, NSArray, NSFileManager

Monday, May 28, 12

Relational

• FMDB (Objective-C wrapper around SQLite)https://github.com/ccgus/fmdb

• CoreData (object graph & persistence framework)http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/coredata/cdprogrammingguide.html

Monday, May 28, 12

Manager

Data Layer

Monday, May 28, 12

Manager

Data Layer

Monday, May 28, 12

Manager

Store

Data Layer

Monday, May 28, 12

Manager

Store

Data Layer

Manager

Monday, May 28, 12

Manager

Cosa deve fare

• Fornire un’interfaccia di accesso ai dati persistiti

• Fornire strumenti di interrogazione

Monday, May 28, 12

Conclusionie quindi?

Monday, May 28, 12

Framework custom

Layer Quando

Network Layer SEMPRE

Domain Model Leggere e scrivere

Data Layer Offline

Monday, May 28, 12

Framework custom

Monday, May 28, 12

Framework custom

Vantaggi

Monday, May 28, 12

Framework custom

Vantaggi

• Know-how

Monday, May 28, 12

Framework custom

Vantaggi

• Know-how

• Modulare

Monday, May 28, 12

Framework custom

Vantaggi

• Know-how

• Modulare

• Flessibile (Business)

Monday, May 28, 12

Framework custom

Vantaggi

• Know-how

• Modulare

• Flessibile (Business)

Svantaggi

Monday, May 28, 12

Framework custom

Vantaggi

• Know-how

• Modulare

• Flessibile (Business)

Svantaggi

• Tempo (Costi)

Monday, May 28, 12

Framework custom

Vantaggi

• Know-how

• Modulare

• Flessibile (Business)

Svantaggi

• Tempo (Costi)

• Skill

Monday, May 28, 12

Framework custom

Vantaggi

• Know-how

• Modulare

• Flessibile (Business)

Svantaggi

• Tempo (Costi)

• Skill

• Declinato

Monday, May 28, 12

Soluzione C

API +

Dominio di Business =

Monday, May 28, 12

RestKit

Che cosa è ?Restkit è un framework Objective-C per iOS che ha lo scopo di facilitare l'interazione con i web-service.

Monday, May 28, 12

Che cosa fa ?

HTTP PROTOCOLREST SOAP XMLENCODE PARSER

CODE ACTIVE RECORDPATTERN MAPPING

CACHE STRATEGY SQL

RestKit

Monday, May 28, 12

Network Layer

“Ha la funzione di costruire e consegnare le richieste HTTP e processare le risposte che arrivano dal server remoto”

Obiettivo

Come raggiunge l’obiettivo?

RKClient RKRequest RKResponse

Attraverso tre attori principali

Monday, May 28, 12

Network Layer

RKClientHttp headers - Type Authentication

Base URL

RKClient

App

Monday, May 28, 12

Network Layer

RKClientHttp headers - Type Authentication

Base URL

RKClient Http headers - Type Authentication

Base URL

RKClient

App

Monday, May 28, 12

Network LayerRKClientRKRequestRKResponse

#import "AppDelegate.h"#import <RestKit/RestKit.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ RKClient *client = [RKClient clientWithBaseURL:@"https://api.pragma-mark.org/sampleQuiz"];...

[RKClient sharedClient]

Da qui in poi avremo disponibile

Monday, May 28, 12

Network Layer

@implementation MyViewController...

#pragma mark - View lifecycle

- (void)viewDidLoad{ [super viewDidLoad];

[[RKClient sharedClient] get:@"/collections/questions" delegate:self];}

#pragma mark - Restkit delegate

- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response { NSLog(@"Loaded payload: %@ with code %i", [response bodyAsString], [response statusCode]); }

-(void)request:(RKRequest*)request didFailLoadWithError:(NSError*)error{ NSLog(@"Error %@",[error localizedDescription]);}

#import <UIKit/UIKit.h>#import <RestKit/RestKit.h>

@interface MyViewController : UIViewController<RKRequestDelegate>{...

Una volta configurato un client possiamo inviare e processare richieste HTTP attraverso esso.

Esempio: invio richiesta e visualizzazione risposta

resource path

Monday, May 28, 12

Network Layer

?payload

Siamo in grado di comunicare con un

web service remoto, inviare

richieste e processare risposte,

Web Service

Monday, May 28, 12

Object Mapping

Quando ne abbiamo bisogno e che cosa è?

Punto di forza di RestKit

E’ un sistema che ci permette di trasformare le risposte JSON/XML in oggetti di dominio locali.

Salendo di astrazione: è un sistema che si occupa di trasformare i dati da un formato ad un altro.

Quando si ha necessità, non solo di scambiare dati, ma di rappresentare oggetti.

Monday, May 28, 12

Object Mapping

RKObjectManager RKObjectMapping

RKObjectMapper RKObjectMappingProvider

•Quando carichiamo un risorsa remota tramite un’istanza di RKObjectManager•Quando un oggetto locale è inviato al backend per essere processato

Le operazioni di mapping sono eseguite:

Monday, May 28, 12

Il protagonista principale di questo layer è RKObjectManager

“RKObjectManager è la classe che si occupa della trasformazione dei dati contenuti nel payload in oggetti”

Object Mapping

RKObjectManager

App RK

Clie

nt

Monday, May 28, 12

Object MappingEsempio: caricare oggetti remoti in oggetti locali

Obiettivo: vogliamo recuperare una collection di question dal web service

Monday, May 28, 12

Object MappingEsempio: caricare oggetti remoti in oggetti locali

Il web service ritorna qualcosa di simile a...{ “questions”: [{ "body" : "Che cosa si intende per scope creep?", "correctIdAnswer" : 1, "identifier" : 1},{ "body" : "Che differenza c’è tra lead e lag?", "correctIdAnswer" : 2, "identifier" : 2}]}

Monday, May 28, 12

Object MappingEsempio: caricare oggetti remoti in oggetti locali

@interface Question : NSObject

@property (nonatomic, copy) NSNumber* identifier;@property (nonatomic, copy) NSString* body;@property (nonatomic, copy) NSNumber* correctIdAnswer;

@end

...lato client, questi dati sono rappresentati da una classe

Monday, May 28, 12

Object Mapping

RKObjectManager* objectManager = [RKObjectManager objectManagerWithBaseURL:@"https://api.pragmamark.org/quiz"]; RKObjectMapping* questionMapping = [RKObjectMapping mappingForClass:[Question class]];[questionMapping mapKeyPath:@"identifier" toAttribute:@"identifier"];[questionMapping mapKeyPath:@"body" toAttribute:@"body"];[questionMapping mapKeyPath:@"correctIdAnswer" toAttribute:@"correctIdAnswer"]; [objectManager.mappingProvider setMapping:questionMapping forKeyPath:@"questions"];

Configuriamo RestKit Definiamo un mapping per

la classe Question

Istruiamo il mapping provider ad usare questionMapping se incontra un

@”questions” key path

Un esempio: caricare oggetti remoti in oggetti locali

Monday, May 28, 12

Object MappingUn esempio: caricare oggetti remoti in oggetti locali

[[RKObjectManager sharedManager] loadObjectsAtResourcePath:@”/questions" delegate:self];

Carichiamo gli oggetti

...i dati vengono recuperati, parserizzati e assegnati agli oggetti locali- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {! NSLog(@"Loaded statuses: %@", objects); }

La classe deve implementare RKObjectLoaderDelegate

Monday, May 28, 12

Object Mapping

Siamo in grado di comunicare con un

web service remoto, inviare

richieste e processare risposte

Caricare rappresentazioni di

oggetti remotinella nostra App e mapparli in oggeti

locali

Monday, May 28, 12

Object Mapping

JSONXML...

payloadServer

Inviare oggetti al server

(Serialization)

Monday, May 28, 12

Object Mapping

RKObjectSerializer

Local Domain Objects

IntermediateDictionary

(NSMutableDictionary)

Mapping Encoder[{ "body" : "Che cosa si intende per scope creep?",

"correctIdAnswer" : 1,

"identifier" : 1

},

{ "body" : "Che differenza c’è tra lead e lag?",

"correctIdAnswer" : 2,

"identifier" : 2

JSON(XML, URL Form Encode...)

BackendSystem

RKParser

attributi e relazioni

Serialization? Un’altra operazione di mapping.

Requ

est

Monday, May 28, 12

Object Mapping Un esempio: inviare oggetti locale al server remoto

[[RKObjectManager sharedManager].router routeClass:[Question class] toResourcePath:@"/questions" forMethod:RKRequestMethodPOST];

[[RKObjectManager sharedManager] setSerializationMIMEType:RKMIMETypeJSON];RKObjectMapping* questionSerializationMapping = [questionMapping inverseMapping];[[RKObjectManager sharedManager].mappingProvider setSerializationMapping:questionSerializationMapping forClass:[Question class]];

Configuriamo RestKit

Lo stesso mapping definito prima è

utilizzo per serializzare la classe

Istruiamo il mapping provider ad usare

questionSerializationMapping

Monday, May 28, 12

Object Mapping

// Create a new Question and POST it to the serverQuestion* question = [Question new];question.body = @"Cosa si intende per approccio agile ad un progetto?";question.identifier = [NSNumber numberWithInt: 3];

Creiamo l’oggetto

[[RKObjectManager sharedManager] postObject:question delegate:self];Inviamo la richiesta...

...recuperiamo la risposta dal server- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {! NSLog(@"Loaded statuses: %@", objects); }

RestKit sa che deve serializzare quando

esegue un POST o un PUT

Un esempio: inviare oggetti locale al server remoto

Monday, May 28, 12

RoutingOvvero, dove vivono gli oggetti?

RKObjectRouter

RestKit offre un sistema di routing capace di generare resource path per un oggetto.

RKObjectRouter registra un mapping tra una classe del dominio e un resource path per uno specifico metodo HTTP.

Per interagire con un web service è necessario sapere dove gli oggetti risiedono.

RoutingSystem

/questions/123

/questions/654/questions/789

Monday, May 28, 12

RoutingOvvero, dove vivono gli oggetti?

[[RKObjectManager sharedManager].router routeClass:[Question class] toResourcePath:@"/questions" forMethod:RKRequestMethodPOST];

POST

[[RKObjectManager sharedManager].router routeClass:[Question class] toResourcePath:@"/questions/(idpk)" forMethod:RKRequestMethodPUT];

PUT

[[RKObjectManager sharedManager].router routeClass:[Question class] toResourcePath:@"/questions/(idpk)" forMethod:RKRequestMethodDELETE];

DELETE

GET[[RKObjectManager sharedManager].router routeClass:[Question class] toResourcePath:@"/questions/(idpk)" forMethod:RKRequestMethodGET];

Monday, May 28, 12

Routing

[[RKObjectManager sharedManager].router routeClass:[Question class] toResourcePath:@"/questions" forMethod:RKRequestMethodPOST];

[[RKObjectManager sharedManager].router routeClass:[Question class] toResourcePath:@"/questions/(identifier)"];

Inizializziamo il nostro route...

Definiamo default route che

sarà usato per tutti gli HTTP

verbs (GET, POST, PUT e DELETE)

registriamo uno specifico

route per il verbo POST

Un esempio: inviare un oggetto al server

Monday, May 28, 12

Routing

// Nel setup dell’App abbiamo già definito i mapping per questa classe Question* question = [Question new]; question.body = @"Cosa si intende per approccio agile ad un progetto?";

[[RKObjectManager sharedManager] postObject:question delegate:self];

POST

Un esempio: inviare oggetti locale al server remoto

RKObjectRouter è quindi utilizzato per generare resource path quando utilizziamo getObject, deleteObject, postObject e putObject.

“/questions”

Monday, May 28, 12

Routing

Question *question = [Question new]; question.body = @"Che cosa si intende per scope creep?"; question.identifier = [NSNumber numberWithInt: 3];

[[RKObjectManager sharedManager] putObject:question delegate:self];

PUT

Un esempio: inviare oggetti locale al server remoto

RKObjectRouter è quindi utilizzato per generare resource path quando utilizziamo getObject, deleteObject, postObject e putObject.

“/questions/3”

Monday, May 28, 12

Routing

Question *question = [Question new];question.identifier = [NSNumber numberWithInt: 3];

[[RKObjectManager sharedManager] deleteObject:question delegate:self];

DELETE

Un esempio: inviare oggetti locale al server remoto

RKObjectRouter è quindi utilizzato per generare resource path quando utilizziamo getObject, deleteObject, postObject e putObject.

“/questions/3”

Monday, May 28, 12

Offline :|Core Data Integration

A questo punto, cosa siamo in grado di fare?

Interagire con il web service remoto

Rappresentare le risorse remote in

locale

Modificare e inviare oggetti

locali al backend

Monday, May 28, 12

Offline :|Core Data Integration

Assenza di connettività?

Monday, May 28, 12

Offline :)Core Data Integration

Assenza di connettività?Persistiamo i dati in locale con Core Data

Monday, May 28, 12

OfflineCore Data Integration

RKManagedObjectStore RKManagedObjectMapping

RKObjectManager* objectManager = [RKObjectManager objectManagerWithBaseURL:@”http://pragmamark.org]; objectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:@”Quiz.sqlite];

Setup RestKit

Vediamo come fare passo dopo passo.

Indicare lo store1

Monday, May 28, 12

Mapping

Vediamo come fare passo dopo passo.

Utilizzare RKManagedObjectMapping2

Questo permette al Mapper di discriminare tra oggetti nuovi, aggiornati o

eliminati

3

OfflineCore Data Integration

RKManagedObjectMapping* questionMapping = [RKManagedObjectMapping mappingForClass:[Question class]]; questionMapping.primaryKeyAttribute = @"identifier"; [questionMapping mapKeyPath:@"identifier" toAttribute:@"identifier"];[questionMapping mapKeyPath:@"body" toAttribute:@"body"];[questionMapping mapKeyPath:@"correctIdAnswer" toAttribute:@"correctIdAnswer"]; [objectManager.mappingProvider setMapping:questionMapping forKeyPath:@"questions"];

Monday, May 28, 12

OfflineCore Data Integration

Il modello persistente deve

ereditare da NSManagedObject

4

@interface Question : NSManagedObject

@property (nonatomic, copy) NSNumber* identifier;@property (nonatomic, copy) NSString* body;@property (nonatomic, copy) NSNumber* correctIdAnswer;

@end

Monday, May 28, 12

@implementation Question

@synthesize identifier;@synthesize body;@synthesize correctIdAnswer;

@end

@implementation Question

@dynamic identifier;@dynamic body;@dynamic correctIdAnswer;

@end @dynamic vs @synthesize

5

OfflineCore Data Integration

Monday, May 28, 12

2012-05-20 12:03:20.921 WhyMCA[1060:fb03] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Cannot initialize an RKManagedObjectMapping without an entity. Maybe you want RKObjectMapping instead?'

Data Model Data Model resource6

OfflineCore Data Integration

Monday, May 28, 12

Ora che succede?

Local Domains Objectsmapping

{questions: [

{ "body" : "Che cosa si intende per scope creep?",

"correctIdAnswer" : 1,

"identifier" : 1

},

{ "body" : "Che differenza c’è tra lead e lag?",

"correctIdAnswer" : 2,

"identifier" : 2

}]

}

JSON(XML, Form URL Encode...)

Transientobjects

Persistentobjects

OfflineCore Data Integration

Monday, May 28, 12

[[RKObjectManager sharedManager] loadObjectsAtResourcePath:@”/questions" delegate:self];

Dopo aver fatto richieste al server...

...i dati sono recuperati, parserizzati e assegnati agli oggetti locali- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {! NSLog(@"Loaded questions: %@", objects); }

OfflineCore Data Integration

Un esempio: chiedere i dati al server

Monday, May 28, 12

2012-05-20 12:29:00.757 WhyMCA [1499:fb03] Loaded questions: ( "<Question: 0x6b921b0> (entity: Question; id: 0x6e704e0 <x-coredata://62C52FE3-86E9-4FD0-9BA8-FAE16839477E/Question/p1> ; data: <fault>)", "<Question: 0x6b8f090> (entity: Question; id: 0x6e707a0 <x-coredata://62C52FE3-86E9-4FD0-9BA8-FAE16839477E/Question/p5> ; data: <fault>)", }

2012-05-13 16:01:03.478 WhyMCA[9935:fb03] Loaded questions: ( "<Question: 0x83a1bd0>", "<Question: 0x83a57d0>")

OfflineCore Data Integration

Un esempio: chiedere i dati al server

Monday, May 28, 12

if ([[RKObjectManager sharedManager] isOnline]) { [[RKObjectManager sharedManager] loadObjectsAtResourcePath:@"/questions" delegate:self];}else { NSArray *questions = [Question allObjects];}

Impostiamo la nostra tecnica

Alternative?

•Implementare di RKManagedObjectCache protocol•Utilizzare RKRequestCache •Database seeding

Un esempio: chiedere i dati al server

OfflineCore Data Integration

Monday, May 28, 12

In practiceRestKit Mapping & CoreData

“Non sempre le risposte del web service sono come ce le aspettiamo”(Anonimo)

Team Mobile Team Server-side

Monday, May 28, 12

•Aggiungiamo l’entità Answer

•Question e Answer sono in relazione molti a molti

•L’identificativo di Question è in un oggetto nidificato

•Nell’elenco delle question manca la key Path @”questions”

Elaboriamo un pò il nostro model

In practiceRestKit Mapping & CoreData

Monday, May 28, 12

RestKit Mapping & CoreDataIn practice

{ “questions”: [ { "identifier" : 1 "body" : "Che cosa si intende per scope creep?", "correctIdAnswer" : 1, }, { "identifier" : 2 "body" : "Che differenza c’è tra lead e lag?", "correctIdAnswer" : 2, }]}

{ [ {"_id": {        "$identifier": "4faf69fee4b0895a3ed9b1ff"    },    "correctIdAnswer": 1,    "body": "Quale è il significato del termine lead?",    "answers": [        {            "identifier": "3",            "body": "risposta B"        },        {            "identifier": "1",            "body": "risposta A"        }    ] }, {...

Dove è la key @“questions” ?Come identifichiamo il contenuto?

Monday, May 28, 12

@interface Answer : NSManagedObject

@property (nonatomic, copy) NSString* identifier;@property (nonatomic, copy) NSString* body;@property (nonatomic, retain) NSSet* questions;

@end

In practiceRestKit Mapping & CoreData

@interface Question : NSManagedObject

@property (nonatomic, copy) NSString* identifier;@property (nonatomic, copy) NSString* body;@property (nonatomic, copy) NSNumber* correctIdAnswer;@property (nonatomic, retain) NSSet* answers;

@end

Monday, May 28, 12

In practiceRestKit Mapping & CoreData

//MappingRKManagedObjectMapping* questionMapping = [RKManagedObjectMapping mappingForClass:[Question class]];questionMapping.primaryKeyAttribute = @"identifier";[questionMapping mapKeyPath:@"_id.$identifier" toAttribute:@"identifier"];[questionMapping mapKeyPath:@"body" toAttribute:@"body"];[questionMapping mapKeyPath:@"correctIdAnswer" toAttribute:@"correctIdAnswer"];

RKManagedObjectMapping* answerMapping = [RKManagedObjectMapping mappingForClass:[Answer class]]; [answerMapping setPrimaryKeyAttribute:@"identifier"];[answerMapping mapKeyPath:@"identifier" toAttribute:@"identifier"];[answerMapping mapKeyPath:@"body" toAttribute:@"body"];

[questionMapping mapKeyPath:@"answers" toRelationship:@"answers" withMapping:answerMapping];[objectManager.mappingProvider addObjectMapping:questionMapping];

Monday, May 28, 12

In practiceRestKit Mapping & CoreData

Effettuiamo la richiesta al server...RKObjectMapping *questionMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[Question class]];

[[RKObjectManager sharedManager] loadObjectsAtResourcePath:@”/questions" objectMapping:questionMapping delegate:self];

Monday, May 28, 12

[[RKObjectManager sharedManager] postObject:question delegate:self block:^(RKObjectLoader* loader) { loader.objectMapping = [RKObjectMapping mappingForClass:[Question class] block:^(RKObjectMapping* mapping) { [mapping mapAttributes:@"identifier", @"body", nil]; }]; }];

In practiceRestKit Mapping & CoreData

Possiamo costruire un mapping dinamicamente...

Monday, May 28, 12

Le codeInviare richieste e processare risposte: i retroscena.

RKResponse

RKClient

RKRequest

Payload

Monday, May 28, 12

Le codeInviare richieste e processare risposte: i retroscena.

RKResponse

RKClient

RKRequest

Payload

RKRequestQueue

Monday, May 28, 12

//[[RKClient sharedClient].requestQueue addRequest:loader];

Cosa succede quando inviamo una richiesta con RKRequest?[RKClient sharedClient].requestQueue

[[RKClient sharedClient].requestQueue cancelRequestsWithDelegate:delegate];

•rilevare connettività•limitare le richieste concorrenti•eliminare richieste per un determinato delegato

RKRequestQueue è utilizzato anche per:

Le codeInviare richieste e processare risposte: i retroscena.

Monday, May 28, 12

Possiamo usarle per:

•Effettuare il download di un’entità “complessa”. Ad esempio: ipotizziamo una Guida, entità del dominio, rappresentata da un grafo di oggetti contenente le sue proprietà e le sue relazioni ma che fa riferimento a diversi asset (audio, fotografie, tiles per le mappa offline) attraverso percorsi esterni.

•Raggruppare le chiamate per la paginazione, per la ricerca, per il workflow di acquisto, etc.. e utilizzare la sharedQueue per soddisfare la user action.

Le codeInviare richieste e processare risposte: i retroscena.

Monday, May 28, 12

Le codeInviare richieste e processare risposte: i retroscena.

RKRequestQueue *queue = [RKRequestQueue requestQueueWithName:nameQueue];

queue.concurrentRequestsLimit = 5;

Creare una coda...

Monday, May 28, 12

Le codeInviare richieste e processare risposte: i retroscena.

NSString *resourcePath = [NSString stringWithFormat:@"%@%@/%i",kAPIAddress,kAPI_AUDIO_GET,[identifier intValue]]; RKRequest *request = [RKRequest requestWithURL:[NSURL URLWithString:resourcePath] delegate:self];request.authenticationType = RKRequestAuthenticationTypeHTTPBasic;request.username = [RKClient sharedClient].username;request.password = [RKClient sharedClient].password; [queue addRequest:request][queue start];

Aggiungere richieste...

Monday, May 28, 12

Le codeInviare richieste e processare risposte: i retroscena.

//verifica prima se esiste la queueBOOL existsQueue = [RKRequestQueue requestQueueExistsWithName:nameQueue]; if (existsQueue) { RKRequestQueue *queue = [RKRequestQueue requestQueueWithName:nameQueue]; NSLog(@"Elimino richieste in coda %i",[queue count]); [queue cancelAllRequests]; NSLog(@"Richieste in coda %i",[queue count]);}

Svuotare la coda...

Monday, May 28, 12

Conclusionie quindi?

Monday, May 28, 12

“Buon senso”

Monday, May 28, 12