IOS CloudKit Corso di Programmazione di Sistemi Mobile Mattia Borrillo Matr: 0000656937 Dire che un’app è cloud based significa che alcuni dati utilizzati nella stessa vengono salvati in server online come quelli iCloud. Apple, per permetterti di salvare dati ed informazioni sui propri server, ti mette a disposizione un framework chiamato CloudKit. La bellezza di affidarsi ad un servizio come quello offerto da iCloud sta nella semplicità di implementazione e nella sicurezza che ne derivano i dati in esso inseriti. Molti pensano che il Cloud è qualcosa di poco affidabile, ma non è assolutamente vero, uno dei vantaggi del cloud è che esendo un sistema distribuito,i dati sono anche difficili da recuperare da parte di eventuali attacchi informatici. Il CloudKit è lo strumento, offerto agli sviluppatori, per interfacciare i dati della propria applicazione ad uno spazio dedicato all’interno dei server Apple. I dati, inseriti nel cloud, possono essere condivisi con gli altri utenti oppure mantenuti privati. Tre motivi per affidarsi ad iCloud tramite il CloudKit. • Semplicità di utilizzo : Per avere il permesso di utilizzare il CloudKit, e quindi permettere alla tua applicazione di conservare dati sui server iCloud, è necessario registrarsi al Developer Program. Questo farà si che Apple configuri in automatico uno spazio dedicato alla conservazione delle informazioni delle tue applicazioni. Dal lato dell’utilizzatore della tua applicazioni, il CloudKit evita la creazione di fastidiosi moduli di registrazione dato che vengono utilizzati di default le credenziali dell’account iCloud che ogni utente Apple possiede. In pratica un utente che utilizza un’app con CloudKit è già di default loggato nello spazio cloud dedicato. • Sicurezza : tutte le informazioni vengono salvate sui server Apple, quindi utilizzare il CloudKit è sinonimo di sicurezza. In più i dati non possono essere trattati se non nei limiti imposti dal regolamento Apple e dalle norme vigenti in maniera di tutela della privacy Americana. Non è da sottovalutare questo aspetto, molte compagnie online non ti impongono di trattare e tutelare i dati degli utilizzatori della tua applicazione e per questo, molti potrebbero sbandierare i tuoi dati a società terze come quelle pubblicitarie. • Costi : Apple ti offre una quantità di storage gratuito che non può neanche lontanamente essere paragonato a quello che gli altri servizi mettono sul mercato. Abilitare le funzionalità del CloudKit. Prima di cominciare bisogna modificare il Bundle Identifier dell'applicazione e assicurarsi di essere loggato con l’account sviluppatore. Per evitare la nascita di errori futuri bisogna utilizzare un Identifier unico. Ad esempio utilizzare la seguente sintassi: • it.tuoDominio.nomeApp • tuoDominio.nomeApp Se non hai un dominio puoi mettere anche il tuo nome, l’importante è che sia seguito dalla dot notation e dal nome della tua applicazione. Per poter interfacciare l'applicazione con iCloud, vai nel menu Capabilities e poi passa su On la voce iCloud: adesso che la tua app è pronta a connettersi ai server iCloud, serve anche uno spazio in cui poter conservare le informazioni pubbliche e private dei tuoi utenti. Lo spazio di memorizzazione prende il nome di Container e per abilitarlo devi spuntare la casella CloudKit nel menù apparso una volt posizionato lo switch sull'ON. Il Container della tua applicazione ha un identificativo che è dato dall’unione della parola iCloud seguita dal tuo Bundle Identifier e deve obbligatoriamente trovarsi in questa forma, quindi non modificarlo. CloudKit DashBoard La tua app è pronta ad utilizzare il CloudKit e a connettersi con i server iCloud. Per poter accedere all’aria riservata alla gestione dei dati, premi il bottone “CloudKit Dashboard” oppure collegati dal seguente link: https://icloud.developer.apple.com/dashboard/. Nel Container puoi inserire 3 tipi di “oggetti”, definiti e gestiti nell’area “SCHEMA“, chiamati: Record Types, Security Roles e Subscription Types. Per poter salvare dati sul cloud è sufficiente l'utilizzo dei Record Types. Il principio di funzionamento del salvataggio dei dati su iCloud non si discosta molto dai principi della programmazione ad oggetti. Apple ha ben pensato di catalogare le informazioni a blocchi o per meglio dire in Record. Un Record Type è l’equivalente di una Classe in OOP. Una volta che si crea un Record, al suo interno si potranno creare gli attributi che ne rappresenteranno lo stato. Es. di classe. class Nota { var titolo :String? var testo :String? init(titolo: String, testo: String) { self.titolo = titolo self.testo = testo } } Creare un Record Type Una volta che si ha idea dell’oggetto che vuoi salvare su iCloud è tempo di creare un Record che ne rappresenti le caratteristiche. Dal menu SCHEMA, selezionare Record Types e premere il tasto + per aggiungere un nuovo Record. Rinonimare il Record in “Nota“ (o comunque il nome che si vuole attribuire a questo nuovo tipo di dato), cambiare l’attributo creato in “titolo” e infine aggiungere un nuovo attributo di tipo String chiamato “testo“, per salvare il Record Type premere Save. (Di seguito aggiungere tutti gli attributi che si vogliono salvare sul cloud dell'oggetto) Public Data e Private Data, le politiche di sicurezza dei dati. Ora che si ha un modello per il salvataggio dei dati dell'applicazione su iCloud è necessario domandarsi come questi dati vengano gestiti. Se tutti gli utilizzatori dell'applicazione utilizzano lo stesso spazio iCloud, vuol dire che i miei dati possono essere letti anche da altri utenti? No, ci sono 2 aree dove vengono salvati i dati di ogni utente, la Public Data e la Private Data. Nella Public Data vengono inseriti tutti i Record types che vogliono essere condivisi con tutti gli utilizzatori dell'applicazione. Se ad esempio l’applicazione utilizza dei dati che servono a tutti gli utenti, allora il record andrà inserito in questa sezione. All’interno della Public Data sono presenti 2 sezioni: 1. User Records: nella sezione Record Types era presente un Record chiamato User. Esso rappresenta gli utilizzatori della tua applicazione e i vari dati che associ all’User vengono conservati qui. 2. Default Zone: in questa sezione vengono salvati tutti i Record Types pubblici e diversi da quelli User. La Private Data è la zone in cui salvare i record types privati. Un Record Types non ha un posto di default in cui si trova e pertanto da codice si possono smistare i vari dati nella relativa zona. Lo sviluppatore dell’applicazione non ha accesso alla Default Zone privata di ogni utente della sua applicazione. CloudController, CKContainer e CKDatabase. Come prima cosa importa il CloudKit sotto l’import già presente: import CloudKit class CloudController { /*Il CKContainer è il riferimento allo spazio di memoria dedicato all'applicazione. In sostanza è il contenitore di tutti i file, pubblici e privati. IL CKDatabase è l'effettivo riferimento allo spazio pubblico/privato del Container */ var container : CKContainer var publicDB : CKDatabase let privateDB : CKDatabase } Creare una connessione con iCloud mediante il CloudKit fa sì che venga realizzato uno spazio di memoria dedicato a tutti i dati dell’applicazione, il nome di questo spazio è Container. Per questo motivo la variabile “container” non sarà altro che il riferimento al Container dell’applicazione. All’interno Container si possono salvare Record, oggetti di tipo Record Types, con due proprietà di sicurezza: pubblico e privato. Tutti i Record diversi da User vengono salvati nella rispettiva Default Zone privata/pubblica. Un CKDatabase è lo spazio pubblico o privato del Container, se il CKContainer rappresenta l’intera nuvola, il CKDatabase è il riferimento sia alla Default Zone privata che pubblica. Ecco che così, il metodo init non fa altro che instanziare il container, prendere la rispettiva zona privata/pubblica e assegnarla alle rispettive variabili. Sotto il precedente codice aggiungi: init() { container = CKContainer.defaultContainer() publicDB = container.publicCloudDatabase privateDB = container.privateCloudDatabase } Come qualsiasi database, per recuperare e aggiungere dati, bisogna fare delle Query. Per inziare si piò richiedere di aggiungere un Record (CKRecord) in un CKDatabase oppure recuperare dati di una tipologia di Record type utilizzando una CKQuery. Aggiungere un Record ad iCloud con CloudKit All’interno della classe è necessario aggiungere il seguente metodo: func salvaNota(titolo :String, testo :String) { // creo un record di tipo "Nota" let recordNota = CKRecord(recordType: "Nota") // al record setto, titolo e testo uguali a quelli del parametro della funzione recordNota.setValue(testo, forKey: "testo") recordNota.setValue(titolo, forKey: "titolo") /* salvo nel Database Pubblico (la record zone public) il record appena creato la funzione è una clousure e il corpo stampa un messaggio quando finisce di registrare */ publicDB.saveRecord(recordNota, completionHandler: { (record, error) -> Void in println("Nota salvata con successo") }) } Il CKRecord è un oggetto di tipo Record Type, infatti dentro la sua inizializzazione vuole in nome del Record Type a cui riferirsi. Nel tuo caso è “Nota” (quello creato come eesempio durante il tutorial). Per accedere al contenuto o modificarlo, il CKRecord, ti mette a disposizione il metodo setValue(<valore>, forKey: <nomeChiave>). Una volta modificati i value del record, viene salvato all’interno del Database con il saveRecord. Il saveRecord viene eseguito dal database in cui vuoi effettivamente salvare il record. Per rendere il record privato devi anche cambiare il database in cui permutare l’azione. Sbagliando DB si possono causare grossi problemi di violazione di privacy. Recuperare uno o più Record con il CloudKit Aggiungi un nuovo File al progetto (File\New File…) e seleziona Swift File, chiamalo Nota e al suo interno inserisci il seguente codice: import Foundation import CloudKit class Nota : NSObject { var record : CKRecord! // il riferimento al Record che rappresenta la Nota var titolo :String! var testo :String! var database : CKDatabase! // il database in cui è stata inserita il record/nota var date: NSDate // la data di creazione del record init(record : CKRecord, database: CKDatabase) { self.record = record self.database = database self.titolo = record.objectForKey("titolo") as String self.testo = record.objectForKey("testo") as String self.date = record.creationDate } } Tornare al CloudController.swift, e sopra la definizione della classe, creare un Protocol (una Interfaccia per chi proviene da altri linguaggi di programmazione) con 2 metodi all’interno che verranno richiamati dal ViewController : protocol CloudControllerDelegate { func errorUpdating(error: NSError) func modelUpdated() } Adesso all’interno della classe CloudController, aggiungi i seguenti attributi: var noteArray = [Nota]() // conterrà tutti i Record recuperati dal Container. var delegate : CloudControllerDelegate? Il metodo per il recupero dei dati dall’iCloud è il seguente: func recuperaNote() { //Il predicate è il modo in cui i dati devono essere recuperati // La CKQuery è il tipo di domanda da fare al database. In particolare si sta richiedendo tutti i tipi record di tipo "Nota" con nessun tipo di filtro (dato che predicate non è impostato) let predicate = NSPredicate(value: true) let query = CKQuery(recordType: "Nota", predicate: predicate) publicDB.performQuery(query, inZoneWithID: nil) { (results, error) -> Void in if error != nil { dispatch_async(dispatch_get_main_queue()) { self.delegate?.errorUpdating(error) return } } else { self.noteArray.removeAll(keepCapacity: true) for record in results { var nota = Nota(record: record as CKRecord, database: self.publicDB) self.noteArray.append(nota) } } dispatch_async(dispatch_get_main_queue()) { self.delegate?.modelUpdated()//utilizzare i dati appena recuperati return } } } Una CKQuery è la domanda che viene fatta al CKDatabase per recuperare una tipologia di contenuti o per meglio dire record. Dato che possono esistere, in un CKDatabase, migliaia di record una query può essere effettuata impostando dei filtri di ricerca chiamati NSPredicate. Se non se ne fa uso, non va messo nil nella CKQuery, bensì un NSPredicate come quello creato nel metodo. Una CKQuery viene eseguita dal CKDatabase, dal quale vuoi recuperare i record, mediante il metodo performQuery. Il primo parametro del metodo è la query da eseguire mentre il secondo parametro, inZoneWithId, rappresenta l’ID della zona in cui performare la Query. La performQuery è una closure con 2 argomenti, results e error. Se l’esecuzione della query ritorna un errore viene effettuata l'operazione utilizzando un dispatch_async in quanto l’applicazione potrebbe bloccarsi perché ferma all’esecuzione della query.