Gestire i dati: download e salvataggio
L’importanza dei dati La quasi totalità delle applicazioni hala necessità di gestire varie funzionalità relative alle informazioni: Scaricare informazioni da un server tramite HTTP Inviare informazioni ad un server Memorizzare i dati in maniera persistente Recuperare i dati memorizzati
Utilizzare il protocollo HTTP
Preparare la richiesta Il protocollo HTTP si basa su una sequenza di richieste/risposte, pertanto è indispensabile, per avviare una comunicazione con il server, inizializzare ed inviare una richiesta. Le principali informazioni sono: Url della pagina Metodo utilizzato (Post, Get, Put, ecc.) Eventuali parametri Header HTTP (tipo di informazioni inviate, formati accettati, ecc.)
Preparare la richiesta Body : Il contenuto vero e proprio della richiesta che può essere di vari tipi: Una semplice stringa Un oggetto di tipo JSON Una serie di coppie chiave/valore Un’immagine Un MultipartFormData : un body suddiviso in più parti che consente di inviare informazioni di tipo differente in un’unica richiesta
NSMutableURLRequest In Swift, la classe corrispondente ad una richiesta HTTP è NSMutableURLRequest. Tale classe ci permette di inizializzare le proprietà appena indicate: Url : Solitamente passato come parametro al costruttore Metodo: A tale scopo esiste la proprietà HTTPMethod che deve essere valorizzata con una stringa corrispondente al nome di un metodo HTTP (esempio: request.HTTPMethod = "POST”) Parametri : in caso di GET sono impostati direttamente nell’URL, altrimenti è necessario utilizzare il body
NSMutableURLRequest Header HTTP: La classe espone il metodo addValue(value: String, forHTTPHeaderField: String) che accetta due stringhe; la prima è il valore e la seconda la chiave Body : Per impostare il Body della richiesta è presente la proprietà HTTPBody, di tipo NSData. In base al tipo di richiesta esistono vari metodi per inizializzare tale property.
Inviare la richiesta : NSURLSession Una volta impostata la richiesta, è necessario inviarla al server. La classe delegata allo svolgimento di tale compito è NSURLSession. Per inviare una richiesta sono richiesti i seguenti passaggi: Ottenere un’istanza di tipo Session : La classe NSURLSession espone un metodo statico, denominato sharedSession, che restituisce una sessione condivisa all’interno del device. In tal modo, all’interno del dispositivo, è possibile condividere cache e cookies.
Inviare la richiesta : NSURLSession Invocare il metodo dataTaskWithRequest della session. Questo metodo accetta due parametri: request : la richiesta definita precedentemente. completionHandler : la funzione che sarà invocata alla ricezione della risposta. Tale metodo non invia direttamente la richiesta, ma restituisce un oggetto di tipo task che sarà responsabile di effettuarne l’invio asincrono. Invocare il metodo resume() sul task, per richiedere l’invio effettivo della richiesta.
CompletionHandler Per gestire la richiesta è necessario utilizzare la funzione passata come secondo parametro al metodo dataTaskWithRequest. Tale funzione restituisce void e fornisce in ingresso 3 parametri: data : Un oggetto di tipo NSData che rappresenta il contenuto vero e proprio della risposta response : Un NSURLResponse che fornisce informazioni sugli header HTTP della risposta. error : fornisce informazioni su un eventuale errore riscontrato durante la comunicazione. Se non si sono verificati errori è nil.
Response data Il parametro di maggior interesse è sicuramente data. Per gestire le informazioni in esso contenuto è indispensabile conoscere il formato e la struttura del messaggio di risposta. Se ad esempio si volesse ottenere il messaggio di risposta come stringa, è sufficiente l’istruzione: NSString(data: data, encoding: NSUTF8StringEncoding)
Ottenere una risposta in JSON Uno dei formati più utilizzati nella comunicazione mobile- server è sicuramente il formato JSON. Per gestire comunicazioni che utilizzano tale sintassi, esiste un’apposita classe denominata NSJSONSerialization, che si occupa di serializzare/deserializzare oggetti in JSON. Tale classe espone un metodo statico che, dato un oggetto di tipo NSData contenente un JSON, lo converte in un NSDictionary.
JSONObjectWithData Questo metodo accetta 3 parametri in ingresso: L’oggetto di tipo NSData da convertire Un oggetto di tipo NSJSONReadingOptions, che permette di indicare come gestire alcuni aspetti della conversione (principalmente se gli oggetti restituiti devono essere mutable). Un’istanza di NSError, passata per riferimento. Si tratta di un parametro di tipo out, utilizzato per indicare se si sono verificati errori di lettura.
Aggiornare l’interfaccia Dato che il processo di comunicazione è gestito in un thread che non ha accesso all’interfaccia, per modificare le view, è necessario richiederne l’aggiornamento all’interface thread. Per farlo è necessario usare il metodo dispatch_async(). Il compito di questo metodo è quello di richiedere ad un differente thread l’esecuzione di un blocco di codice. Il blocco deve essere inserito in un’apposita coda.
dispatch_async Il metodo accetta due parametri: La coda in cui inserire il blocco. Per ottenere quella del main thread è sufficiente utilizzare dispatch_get_main_queue(). Il blocco di codice da eseguire.
Persistenza dei dati
Gestione i file In iOS è possibile salvare le informazioni nella memoria persistente del dispositivo, all’interno di file. Per farlo è necessario: Ottenere il percorso di una cartella all’interno della quale l’utente ha permessi di scrittura Ottenere il percorso del file all’interno del quale salvare i dati Effettuare l’operazione di lettura/scrittura sul file.
Gestione dei file Esistono varie alternative per salvare le informazioni; una delle più utilizzate, per la sua flessibilità, è quella che fa uso di NSKeyedArchiver/Unarchiver, che sono due classi responsabili del salvataggio/recupero delle informazioni dal disco. Per utilizzare questo metodo è necessario che gli oggetti salvati siano di tipo NSObject e aderiscano al protocollo NSCoding.
NSCoding Il protocollo NSCoding permette di archiviare/recuperare oggetti dalla memoria, grazie al supporto di un oggetto di tipo NSCoder, responsabile del salvataggio delle singole proprietà dell’oggetto. Per rispettare il protocollo NSCoding è necessario ridefinire i seguenti metodi: encodeWithCoder(coder: NSCoder) : invocato quando l’oggetto sta per essere salvato su disco. init(coder: NSCoder) : invocato quando l’oggetto viene recuperato dalla memoria persistente.
encodeWithCoder Essendo un metodo ereditato da objective-C, è necessario anteporre alla signature del metodo la public func encodeWithCoder(coder: NSCoder) All’interno di questo metodo è necessario codificare le singole proprietà dell’oggetto, associando ognuna ad una chiave. In tal modo sarà possibile recuperarle in fase di decodifica delle informazioni. Per farlo, il principale metodi esposto dalla classe NSCoder, è: coder.encodeObject(object: AnyObject, forKey:String)
init Nel momento in cui l’oggetto viene recuperato dalla memoria è invocato l’overload del metodo init che accetta un NSCoder come parametro. La sintassi public required init(coder decoder: NSCoder) Questo costruttore permette di recuperare le informazioni salvate in precedenza, grazie all’associazione chiave/valore e di inizializzare le proprietà dell’oggetto. Il metodo per farlo è: decoder.decodeObjectForKey(key: String) che restituisce un oggetto di tipo AnyObject. E’ pertanto obbligatorio forzare il downcast al tipo corretto.
Ottenere il percorso della cartella Per salvare le informazioni abbiamo bisogno di accedere ad una cartella in cui l’utente ha il permesso di scrittura. Per recuperare un elenco di cartelle che soddisfino specifiche condizioni esiste il metodo NSSearchPathForDirectoriesInDomains, che accetta i seguenti parametri: directory : oggetto di tipo NSSearchPathDirectory, che specifica di che tipo di cartella abbiamo bisogno. Esistono innumerevoli valori possibili; per salvare informazioni, è consigliabile l’utilizzo della DocumentDirectory
Ottenere il percorso della cartella domainMask : di tipo NSSearchPathDomainMask, specifica all’interno di quale root Folder effettuare la ricerca. Specificando UserDomainMask, verranno cercate solamente le cartelle dell’utente. expandTilde : valore booleane usato per indicare se è necessario gestire opportunamente il caso in cui il percorso contenga il simbolo tilde ( ~ ). Questo metodo restituirà un array di stringhe, ognuna delle quali rappresenta il path di una cartella. Per il nostro obiettivo, accederemo a quella in posizione 0. Per il percorso del file, è sufficiente concatenare, al path della cartella, il simbolo / seguito dal nome del file
Salvataggio delle informazioni Una volta ottenuto il percorso del file, è possibile procedere con il salvataggio dei dati. Per farlo utilizziamo la classe NSKeyedArchiver, responsabile della gestione del salvataggio di informazioni persistenti. E’ sufficiente invocare il seguente metodo statico: NSKeyedArchiver.archiveRootObject(object, toFile: path) passando come parametri l’oggetto da salvare ed il percorso del file.
Recupero delle informazioni. Per il recupero delle informazioni è consigliato effettuare un passaggio in più, cioè verificare che il file esista. L’istruzione è la seguente: NSFileManager.defaultManager().fileExistsAtPath(path) alla quale passiamo in ingresso il percorso del file e restituisce un boolean che indica se tale file esiste o meno. Infine, per il recupero delle informazioni, la classe NSKeyedUnarchiver espone il metodo: unarchiveObjectWithFile(path) che restituirà l’oggetto salvato in precedenza.