Usare CloudKit con SproutCore
Wednesday, April 28th, 2010Dopo aver introdotto CloudKit, vediamo come usarlo con SproutCore, configurandolo come backend per il tutorial al posto dei più classici PHP, Rails, Django e compagnia bella.
Il tutorial consiste nel creare una todo list, i cui record stanno sotto <app>/tasks. Dato che SproutCore e CloudKit seguono a grandi linee gli stessi principi REST, i punti su cui intervenire non sono molti: più che altro si tratta di fare in modo che vengano usati correttamente gli etag.
Fetch dei record
Se chiediamo /tasks CloudKit non restituisce i dati dei record ma solo i loro link e alcuni dati descrittivi come il numero dei record e l’offset1. In questo tutorial, però, faremo in modo che restituisca tutti i dati in un colpo solo. Quindi, al posto di girare semplicemente la richiesta, faremo un GET verso /tasks/_resolved (in grassetto le parti modificate rispetto al tutorial):
fetch: function(store, query) {
if (query === Todos.TASKS_QUERY) {
SC.Request.getUrl('/tasks/_resolved').json()
.notify(this, 'didFetchTasks', store, query)
.send();
return YES;
}
return NO;
},
ottenendo una risposta simile a questa:
{
"documents": [{
"document": "{
\"campo\": \"valore\",
...
}",
"etag": "3b990fe0-2913-012d-e490-0019e33a5348",
"uri": "/tasks/3",
"last_modified": "Tue, 13 Apr 2010 10:14:13 GMT",
},
...],
"offset": 0,
"total": 3
}
La nostra data source sarà semplicemente quello di spacchettare documents che, ragioni ignote, tratta ogni documento come una stringa e non un oggetto JSON, aggiungendo contemporaneamente l’etag che verrà usato nelle operazioni di modifica:
didFetchTasks: function(response, store, query) {
var records = [];
response.get('body').documents.forEach(function(item, index, documents) {
var record = SC.json.decode(item.document);
records[index] = {
"guid": item.uri,
"description": record.description,
"isDone": record.isDone,
"etag": item.etag
}
}, this);
if (SC.ok(response)) {
store.loadRecords(Todos.Task, records);
store.dataSourceDidFetchQuery(query);
} else store.dataSourceDidErrorQuery(query, response);
},
Inserimento dei record
Nella creazione dei record SproutCore esegue subito il POST con i dati, lasciando a un delegato il compito di aggiornare il dato store. Anche in questo caso dovremo aggiungere l’etag:
…
var record = store.readDataHash(storeKey);
record.etag = response.get('body').etag;
store.dataSourceDidComplete(storeKey, record, url);
…
Aggiornamento dei record
Adesso possiamo sfruttare gli etag salvati prima per impostare il valore dell’header If-Match:
updateRecord: function(store, storeKey) {
if (SC.kindOf(store.recordTypeFor(storeKey), Todos.Task)) {
var record = store.readDataHash(storeKey);
SC.Request.putUrl(store.idFor(storeKey)).json()
.header('If-Match', record.etag)
.notify(this, this.didUpdateTask, store, storeKey)
.send(record);
return YES;
} else return NO ;
},
Lo stesso vale per la cancellazione:
destroyRecord: function(store, storeKey) {
if (SC.kindOf(store.recordTypeFor(storeKey), Todos.Task)) {
SC.Request.deleteUrl(store.idFor(storeKey)).json()
.header('If-Match', store.readDataHash(storeKey).etag)
.notify(this, this.didDestroyTask, store, storeKey)
.send();
return YES;
} else return NO;
},
Tutto qui?
Beh, sì! Abbiamo solo dato un’occhiata al tutorial e sicuramente per cose più realistiche CloudKit è insufficiente ma, come ricordavo l’altra volta, vedo il suo utilizzo nell’ambito della prototipazione dove, da un lato, per la GUI, abbiamo SproutCore; dall’altro, per il backend, abbiamo CloudKit.
Rispetto al tutorial siamo dovuti intervenire sulla definizione della data source, mentre nell’originale la stessa implementazione va bene con tutti i backend ma non ci vuole molto per sostituirla in base alle esigenze2.