Download - 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
Francesco SinopoliTILTAP
Superpartes Innovation Campus | H-FARM
http://it.linkedin.com/in/francescosinopoli
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
• Mac OS X & iPhone
• Synchronous & asynchronous requests
• Basic, Digest e NTLM authentication
• Cache control and ETag support
• Throttling bandwidth
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