Compatibile con Xcode 8

Introduzione al Core Data in Swift. Come salvare dati nella memoria del dispositivo

ENTRA NEL NOSTRO GRUPPO FACEBOOK

Il Core Data non è un Database.

Un Database è un archivio di dati organizzato in strutture e salvato localmente. É costruito per fornire la massima velocità di recupero e scrittura delle informazioni all’interno di questo archivio.

Un database lo puoi immaginare come un enorme archivio dove le informazioni vengono conservate in degli appositi cassetti. A differenza degli archivi reali e antichi, dove le persone devono cercare da sé i dati salvati nei cassetti, un Database moderno o DB è automatizzato. L’interazione con il Database avviene mediante l’utilizzo delle così dette Query.

Una Query è il comando che avvia il meccanismo di ricerca dell’informazione richiesta all’interno del DB. Ma non solo. Le Query vengono utilizzate anche per altri scopi.

Fai finta di trovarti una biblioteca pubblica e di voler affittare un libro. La libreria della biblioteca, cioè il Database, è generalmente composta da degli scaffali ordinati con una qualche sorta di logica (nome, anno, autore ecc). Dato che non conosci come la Biblioteca gestisce la Libreria, quello che fai generalmente è chiedere al bibliotecario: “Senti, puoi cercarmi questo libro X?”.

L’azione di domandare un libro ad un bibliotecario, cioè di recuperare un’informazione dal Database, si chiama Query. Nel gergo tecnico si dice che si sta interrogando il Database con una Query.

Dato che il bibliotecario conosce perfettamente la struttura della sua Libreria, in pochissimo tempo prenderà il libro e te lo porterà. La stessa identica procedura avviene quando vuoi inserire un nuovo libro o eliminarne uno dalla libreria. Lasci che sia il bibliotecario a farlo per te.

Quindi le Query o interrogazioni al DB non vengono utilizzate solo per la ricerca bensì per tutte le operazioni di relazione con il Database (inserimento, modifica, eliminazione e ordinamento).

Fin qui ci sei?

Ogni biblioteca può scegliere di organizzare la sua libreria come preferisce. iOS, o meglio, il Core Data (tra poco scoprirai cos’è), utilizza un tipo di Database chiamato Relazionale. Un Database relazionale organizza le informazioni in una sorta di tabella (in realtà per chiamarsi Relazione deve rispettare delle regole ben precise definite dal matematico inglese Edgar Codd. Tra queste c’è quella della visualizzazione in tabella).

Ipotizziamo di voler salvare una lista di persone definite da un nome e da un’età.

Ogni riga del Database rappresenterà un’insieme di valori (come nel nostro caso, il nome e l’età di una persona). Le colonne, invece, organizzano i dati della riga in modo da aver tutti delle stesso tipo sotto la stessa colonna.

Nel nostro esempio, quindi, avremo due colonne. La prima colonna si chiamerà “Nome” e la seconda “Età”. Ogni riga mette i suoi dati all’interno della colonna corretta. “Giuseppe” andrà sotto la colonna “Nome” e “22” sotto la colonna “Età”. Alla seconda riga avrai la stessa dislocazione ma con valori differenti.

[su_box style=”bubbles” box_color=”#4daaaaa” radius=”5″]Questo tutorial è un estratto di un intero modulo del mio corso di sviluppo applicazioni iOS con il linguaggio Swift. Buona lettura ;)[/su_box]

Visualizzazione dati DB tabella

Quindi, in un DB Relazionale, i dati di un Entry (Entry = riga del DB) sono legati proprio per il semplice fatto di risiedere sulla stessa riga. Ovviamente gli elementi di una tabella possono essere collegati ad altri che risiedono in altre tabelle e così via. Per esempio, l’elemento “giuseppe” potrebbe avere dei dati associati a lui presenti in un’altra tabella delegata alla gestione di altre informazioni. Sto super semplificando perché non è compito di questo corso studiare la struttura reale di un DB.

Comunque, per farla breve, la cosa interessante dei Database è che non ti devi interessare di come effettivamente vengono salvati i dati in memoria. Questa rappresentazione a tabelle è le query fanno da intermediari con il sistema reale di memorizzazioni. Infatti, uno dei punti a favore dei DB è che possono essere esportati ed importati su altre macchine senza preoccuparsi di come verranno salvati i dati in un nuovo ambiente (salvo casi particolari).

Ora, per quanto io l’abbia fatta semplice, i Database proprio perché utilizzano linguaggi differenti da quelli di programmazione, per esempio le query vengono scritte in un linguaggio appositamente creato, sono difficili da comprendere e ci vorrebbe decisamente troppo tempo per saperli utilizzare appieno.

Mamma Apple, per evitare di farti perdere tempo con l’esplorazione del lato oscuro della Luna, ha ben pensato di creare uno strumento chiamato Core Data che semplifica l’interazione con il Database che ci sta sotto.

Ecco perché ho esordito dicendo che il Core Data non è un Database. Il Core Data è uno strumento intermedio o ponte che fa da tramite tra la tua applicazione iOS e la memorizzazione dei dati in memoria. Infatti ha tantissimi strumenti che vanno al di là del semplice salvataggio delle informazioni dell’applicazione.

Il fatto che il Core Data è uno strumento intermedio, non deve assolutamente scoraggiarti. Dato che è stato creato da Apple per la memorizzazione dei dati in memoria è pensato per essere quanto più vicino ai concetti di programmazione che già possiedi. In pratica, scriverai del semplice codice in Swift ed utilizzerai dei metodi appositi contenuti nell’omonimo framework CoreData.

Core Data e linguaggio swift. salvare dati in memoria

Super premessa fatta. Adesso è arrivato il momento di scoprire come utilizzare il Core Data con il linguaggio Swift per cominciare a salvare dati nella memoria della tua applicazione iOS.

Pronto a ballare con il Core Data?

Aggiungere il Core Data ad un progetto

L’inserimento del Core Data in un’applicazione comincia fin dai primi passi della creazione di un progetto. Crea una nuova applicazione iOS single ViewNella pagina in cui inserisci i primi dati troverai una spunta da seleziona per attivare il Core Data:

Aggiungere il Core Data ad un progetto iOS

Questa semplice spunta aggiungerà dei file e codici per la gestione della persistenza dei tuoi dati. Infatti, appena arriverai al tuo progetto vedrai un nuovo file chiamato NomeProgetto.xcatdatamodeld e nell’AppDelegate dei nuovi metodi.

In particolare, i metodi presenti nell’AppDelegate creano delle relazioni con il Database reale che conterrà i dati che salverai. Non ci interrogheremo sul significato di tali metodi proprio perché non ci interessa capire come gestisce i dati e dove andranno salvati.

Per quanto riguarda il file xcatdatamodeld, tra poco scoprirai a cosa serve.

Se invece hai un progetto iOS e vuoi aggiungergli il Core Data segui queste semplici istruzioni: Come aggiungere il Core Data ad un progetto esistente.

Il concetto di Model o Entity

ModelModel…Mmmh… Modello!

Un Model, o meglio conosciuto come Entity, è un prototipo di ciò che verrà inserito e salvato nella memoria del dispositivo grazie al Core Data

Una Entity puoi considerarla come una Classe, un qualcosa di astratto che serve a raggruppare dei dati seguendo delle caratteristiche comuni. Essendo un concetto astratto, una Entity serve solo ed esclusivamente a identificare se un tipo di dato assomiglia o no a quella Entity.

Mi spiego meglio!

Io e Tu siamo entrambi delle persone, giusto? Credo di si :D

Come persone siamo caratterizzati da qualcosa che ci accomuna. Entrambi abbiamo i capelli, due occhi, siamo mammiferi, possiamo parlare ecc. Ma, pur essendo simili siamo entrambi diversi. Io mi chiamo Giuseppe e tu X, io sono nato giorno 19/07/93 e tu giorno Y.

In sostanza, non voglio ripetere quello che già ho detto per la programmazione ad oggetti (SI! Ti sto costringendo ad andare a leggere quello che ho scritto), una Entity è il modello, mentre ciò che salverai nella memoria sarà la rappresentazione reale della Entity:

Se Persona la consideri una Entity, allora io e tu siamo istanza dell’Entity Persona e, di conseguenza, solamente noi saremo memorizzati all’interno del dispositivo.

Può sembrare una banalità ma molti utenti mi contattano chiedendomi: “Ehi Peppe! Non capisco come posso salvare un’Entity, mi aiuti?”. Queste persone hanno completamente frainteso ciò che la Entity rappresenta. Per questo motivo gli sarà impossibile sfruttare le potenzialità di questo strumento.

Ti avevo detto che il Core Data cerca di avvicinarti agli strumenti di persistenza con le conoscenze che già possiedi. Tra poco vedrai che creerai delle classi speciali, costruite appositamente, per poter permettere agli oggetti di quella classe di poter essere salvati in memoria.

Creare una Entity

Ipotizziamo che l’applicazione che stai realizzando ti permette di salvare dei libri che hai letto. Un libro è formata dalle informazioni: nome, autore e numero di pagine. Quindi una volta che l’utente inserirà queste informazioni, il Core Data, provvederà a memorizzarle.

Il Core Data vuole conoscere a priori i tipi di dato che andrai a memorizzare dentro il dispositivo. Cioè vuole sapere le Entity, i modelli, che utilizzerai nella tua applicazione iOS.

Qui entra in gioco il file xcatdatamodeld. Il file xcatdatamodeld serve a creare la struttura delle Entity e assolve ad altri compiti che vedrai più avanti.

Aprilo. In basso a sinistra c’è un bottone a forma di più. Se lo premi creerà una nuova Entity che vedrai nella colonna in alto a sinistra. Doppio click sul suo nome e rinominalo in “Libro“. A destra, sotto il menu Attributes, potrai aggiungere le proprietà dell’Entity. In basso a sinistra c’è il bottone + che ti permette d’aggiungere le proprietà ed il relativo tipo di dato. Aggiungi le proprietà nome (seleziona type String), autore (String) e numero di pagine (Int16):

Aggiungere una nuova Entity Core Data

Esattamente come una classe, l’Entity del Core Data, presenta attributi ed i relativi tipi di dato. Nel tuo caso, per esempio, la proprietà autore sarà di tipo String. Questo perché, probabilmente avrai una textField da cui recupererai il nome del libro ecc.

Se già a questo punto c’è qualcosa che non ti suona o che non riesci a capire appieno, scrivi un commento. Sopratutto questa prima parte è fondamentale per comprendere tutto il resto.

La classe NSManagedObject

Adesso che il tuo Core Data sa che andrai a salvare oggetti di tipo Libro (Entity Libro) devi passare alla concretizzazione a livello di codice. L’xcatdatamodeld ti serve esclusivamente per avere una visione d’insieme dei modelli ma niente di più (nella prossima lezione vedrai meglio altre funzionalità di questo strumento).

Per gestire una Entity da codice hai bisogno di creare una classe che la rappresenta. Cioè ti serve qualcosa di concreto, qualcosa scritto con il linguaggio Swift, che potrai utilizzare all’interno del tuo progetto e che contemporaneamente faccia riferimento a quel Model Entity che hai nell’xcatdatamodeld.

La classe delegata alla gestione delle relazioni con le Entity del tuo xcatdatamodel si chiama NSManagedObject. Con questa classe sarà possibile creare dei metodi che interagiranno direttamente sul modello. Ogni classe NSManagedObject sarà associata ad una Entity che provvederà a dargli tutte le informazioni su quel modello.

Anche in questo caso Xcode offre uno strumento veloce che permette di creare una classe subclass della NSManagedObject direttamente associata alla Entity del tuo xcatadatamodeld.

Per crearla, vai su Editor\Create NSManagedObject Subclass. Il processo di guiderà verso la scelta dell’Entity da associare alla nuova classe che stai creando. Infine ti basterà cliccare su Next e Create:

L’operazione creerà due file. Una classe ed una exstension. All’interno dell’extension sono presenti le stesse proprietà che hai inserito nell’Entity del tuo xcatdatamodeld (più un metodo fetchRequest() che vedremo più avanti). L’extension non fa altro che aggiungere le proprietà dell’Entity alla classe.

Perché crea due file separati? Per ribadire meglio il concetto di collegamento tra Classe NSManagedObject e la Entity a cui è associata. Quando dovrai aggiungere nuove proprietà alla tua Entity e automaticamente dovrai riportarle nella classe NSManagedObject, le dovrai aggiungere da questa exstension (puoi ovviamente farlo anche dalla classe).

Cos’è quella scritta @NSManaged? Questo marcatore indica che la variabile è stata creata per rappresentare l’attributo presente nell’Entity. Il marcatore @NSManaged è obbligatorio, pena la mancanza di relazione tra classe ed Entity.

Domande

[su_spoiler title=”Devo obbligatoriamente seguire questi passaggi per creare una NSManagedObject class?” style=”fancy”]

No. Se vuoi puoi creare un File Swift e scrivere la tua subclass della NSManagedObject. In questo modo puoi prevenire la creazione dell’exstension per controllare tutto da un’unico file.

Nel caso tu intraprenda questa strada, leggi il punto sotto.

[/su_spoiler] [su_spoiler title=”Come associare una Entity ad una Classe NSManagedObject?” style=”fancy”]

Nel caso in cui avessi creato una Custom subclass della NSManagedObject, senza l’aiuto di Xcode, tale file deve essere associato manualmente alla Entity.

Per associare una classe NSManagedObject ad una Entity: torna sul xcdatamodeld, seleziona l’entity in questione e dal Data Model Inspector cambia la Class di riferimento (l’immagine che vedi sotto è di un altro progetto ma rende comunque l’idea):

Associare una classe ad una entity core data

[/su_spoiler]

condividi xcoding

Se cominci a vedere Size Class da tutte le parti, prenditi una pausa amico mio. Nel frattempo, se ti va, dammi una mano a diffondere xCoding.it!

[addtoany]

Grazie ;)

NSManagedObjectContext

Introduciamo un nuovo attore, il Managed Object Context.

L’NSManagedObjectContext o abbreviato in Context, è il gestore delle informazioni contenute nella memoria della tua applicazione e salvate tramite le funzionalità del Core Data.

Pensa al Managed Object Context come l’involucro che contiene tutte le istanze delle varie NSManagedObject che conterrà la tua applicazione.

Ti ricordi il bibliotecario della libreria?

Se un NSManagedObject è un libro della libreria, l’NSManagedObjectContext ha funzione di bibliotecario ed, al tempo stesso, di libreria. Infatti, il Managed Object Context ti permetterà di ricercare, inserire, modificare ed eliminare i Managed Object che man mano inserirai.

Dato che ho parlato abbastanza, vediamo di mettere qualcosa in pratica!

Il Core Data Controller

Quando lavoro con il Core Data e non solo, una delle operazioni che più mi aiuta nello sviluppo è quello di creare una classe per la gestione delle operazioni più importanti del Core Data. Dato che capiterà spesso di dover inserire, recuperare dati o altre operazioni c’è il rischio di scrivere e ripetere sempre lo stesso codice su diversi View Controller.

Per evitare la ripetizione di codice inutile preferisco crearmi una classe, che generalmente chiamo CoreDataController, dentro la quale inserisco le funzioni che utilizzerò.

Quindi, per prima cosa, crea un nuovo Swift File chiamato CoreDataController. Al suo interno crea l’omonima classe:

Dato che è una classe che utilizzerai in molti ViewController, la regola vuole (o meglio, io ti impongo) che questa classe sia gestita come singleton. Quindi il secondo step è quella dell’aggiunta del singleton:

Subito sotto l’import Foundation, aggiungi sia l‘import del framework CoreData che UIKit. Successivamente, dentro la classe, crea una proprietà chiamata context di tipo NSManagedObjectContext:

La proprietà context conterrà il riferimento all’NSManagedObjectContext della tua applicazione. Se torni, per un momento, all’AppDelegate vedrai che una delle proprietà aggiunte dal Core Data si chiama persistantContainer. Questa proprietà mette in relazione il tuo xcatdatamodeld con i dati reali memorizzati nel dispositivo e gestiti dall’ManagedObjectContext.

Dal persistantContainer ricaverai il ManagedObjectContext per poter eseguire tutte le operazioni di salvataggio, recupero, ricerca ecc.

Come si recupera?

Dato che l’AppDelegate è l’oggetto delegato alla gestione degli stati della tua applicazione, puoi recuperare la sua istanza accendendovi tramite la classe UIApplication. Questa classe fornisce una proprietà, chiamata shared, che permette di recuperare l’istanza della tua applicazione. Da qui puoi accedere alla proprietà delegate per il recupero del tuo AppDelegate.

Nel metodo init, quindi, recupera l’istanza dell’AppDelegate dalla proprietà shared. Infine, dal persistentContainer accedi alla proprietà viewContext e assegnala alla proprietà context della tua classe:

A questo punto sei pronto per poter salvare le informazioni nel tuo ManagedObjectContext.

L’interfaccia dell’applicazione

Non voglio soffermarmi molto sull’interfaccia dell’applicazione. Nel tuo View Controller aggiungi tre Text Field per l’inserimento dei dati del libro e due bottoni, uno per il salvataggio ed uno per il recupero di tutti i libri. Ricordati di creare le rispettive IBOutlet e IBAction. Su DropBox trovi il progetto completo:

Schermata 2016-02-29 alle 12.18.50

Salvare dati in memoria

Ok, adesso è tutto pronto. Dobbiamo semplicemente vedere come salvare i dati in memoria con il Core Data in Swift.

Il primo passaggio è quello di definire il metodo di inizializzazione della classe NSManagedObject Libro. Un oggetto NSManagedObject viene creato grazie ad un metodo init che ha due parametri:

  • L’Entity a cui è associato. Ti ricordo che un NSManagedObject è l’oggetto reale che andrà memorizzato nella memoria, mentre l’entity è solamente un blue print.
  • Il context, cioè l’NSManagedObjectContext, in cui salvare l’oggetto in questione.

Dato che tu hai una subclass della NSManagedObject, cioè la tua classe Libro, per renderti più semplice questo concetto, implementa il seguente metodo init della classe padre:

La classe genitore, cioè la NSManagedObject, contiene l’init di cui ti parlavo. Te l’ho fatto riscrivere all’interno della classe solamente per farti comprendere il meccanismo di salvataggio degli oggetti (ne avresti potuto fare a meno dato che i metodi della classe padre sono accessibili dal figlio come spiegato nei principi della programmazione OOP).

Ogni volta che un oggetto NSManagedObject viene creato, viene specificato l’Entity da cui deriva ed il context in cui andrà memorizzato.

Ritorna al tuo CoreDataController.

Dato che la tua Entity Libro è composta da tre attributi (NomeAutore e Num pagine) aggiungi una funzione con tre parametri dello stesso tipo. Questa funzione verrà utilizzata dalla IBAction del bottone “Salva Libro” la quale gli passerà i valori ottenuti dai tre text field:

Per creare un NSManagedObject, come hai visto dal metodo init, bisogna conoscere l’Entity di riferimento dell’oggetto. Per recuperare l’Entity dal tuo xcatdatamodeld puoi utilizzare la classe NSEntityDescription ed il suo metodo entity(forEntityName: String, in: NSManagedObjectContext).

Quindi, come prima riga della funzione, scrivi:

Adesso hai tutti i componenti per creare e memorizzare un oggetto NSManagedObject. Hai sia il context in cui salvarlo che la Entity di riferimento. Dunque, alla riga sotto, crea un nuovo oggetto Libro. Una volta fatto, passa alle sue proprietà nome, autore e num_pag i rispettivi parametri delle funzioni:

Infine, bisogna rendere persistente questo nuovo oggetto salvandolo in memoria. Per farlo, devi utilizzare il metodo save() dell’NSManagedObjectContext.

Questo passaggio è importantissimo, in quanto il Core Data non tiene traccia dei movimenti e delle modifiche che stai eseguendo al context o ai vari NSManagedObject e, di conseguenza, devi forzarlo a salvare le varie modifiche che hai effettuato durante la sessione.

Quindi, è una buona norma, salvare ad ogni modifica, cancellazione o inserimento di un nuovo oggetto in memoria.

Il metodo save() è passibile ad eccezione, quindi deve essere inglobato in un controllo di tipo do-try-catch:

Well done. Semplice, veloce e conciso. Non ti resta che provare!

Nella tua IBAction, dopo aver effettuato tutti i controlli, chiama il metodo addLibro dalla sharedIstance del CoreDataController:

Salvare dati con Core Data in Swift

Recuperare dati dalla memoria

Il recupero delle informazioni è altrettanto semplice. Dato che tutti i dati sono già in memoria devi semplicemente fare una richiesta al ManagedObjectContextScusi signor ManagedObjectContext mi può tornare la lista dei ManagedObject che ho salvato?”

Certo signor programmatore, prima però devi mandarmi una richiesta o NSFetchRequest.

Ti ricordi quel metodo fetchRequest() presente nell’Extension della tua classe?

Benissimo! Questo metodo restituisce un oggetto NSFetchRequest con all’interno la richiesta per il recupero degli oggetti “Libro” (entityName).

L’NSFetchRequest viene letteralmente passato al context, che lo esegue con il metodo fetch, che provvederà a ricercare tutti i ManagedObject figli di quell’Entity.

Il metodo execute restituisce un array di Any. Questo vuol dire che, i suoi elementi, dovranno essere castati nel tipo di dato cercato per poter essere opportunamente letti.

Il metodo execute, infine, può sollevare eccezioni quindi va controllato con un do-try-catch. Una volta recuperato l’array eseguiremo un ciclo for-in per la lettura di tutti i dati recuperati.

Per concludere, all’interno del tuo CoreDataController, aggiungi il seguente metodo:

Credo che il codice sia abbastanza auto esplicativo. Nel caso ci fossero dei problemi di comprensione, scrivi pure un commento o scarica il progetto completo dove ho scritto un commento per ogni riga di codice.

Dopo aver assegnato il metodo alla IBAction dell’altro bottone, fai una prova:

Recuperare dati dal Core Data in Swift

Domande

[su_spoiler title=”Se ho salvato solamente un oggetto, la fetch request restituisce comunque un array?” style=”fancy”]

Si. Anche se salvi un solo oggetto in memoria, la fectRequest ritorna un Array di AnyObject composto da un solo elemento.

[/su_spoiler] [su_spoiler title=”returnObjectsAsFaults, cos’è?” style=”fancy”]

Dopo aver creato la FetchRequest ho passato a false la proprietà returnsObjectsAsFaults. Questa proprietà, che di default è true, permette di recuperare gli oggetti in maniera non completa. Questo ti permette di ottimizzare i tempi di recupero degli oggetti nel caso in cui il context sia composto da più di 1k managed object.

In pratica, con la proprietà uguale a true, vengono recuperati gli oggetti ma i valori delle loro proprietà vengono mantenuti in cache e recuperati solo quando d’effettivo bisogno (cioè quando ci accedi).

Nel nostro caso, dato che stiamo leggendo i valori degli oggetti, questa proprietà risulterebbe inutile dato che vogliamo leggere immediatamente i valori. Quindi, a valore = false, significa che gli oggetti vengono recuperati per interi.

[/su_spoiler]

NSPredicate e ricerca ottimizzata

Ipotizziamo il caso in cui ci siano N elementi salvati nel tuo NSManagedObjectContext e che vuoi recuperare soltanto quelli che soddisfanno una determina condizione. Per esempio, immagina di voler pescare solamente i libri scritti da Dante Alighieri.

Con le conoscenze attuali quello che faresti sarebbe recuperare tutto l’array dal context ed iterarlo per cercare i libri richiesti. Un processo, se ci pensi, troppo oneroso. Infatti, non solo devi recuperare tutti i dati inutilmente, in più, devi anche scorrerli tutti per ricercare un unico libro.

Un po’ come uscire tutti i libri dalla libreria, metterli su un tavolo e cominciare a cercare quello desiderato.

Non sarebbe meglio tenere tutti i libri sulle mensole e cercare quello richiesto guardando attraverso la vetrina?

Un qualcosa d’analogo la ottieni utilizzando l’oggetto NSPredicate. Se ti ricordi il discorso delle Query e dei DB che utilizzano dei linguaggi particolari, bene! perché adesso utilizzerai qualcosa di mai visto nel linguaggio swift.

Un NSPredicate è un oggetto che serve a creare interrogazioni ad un DB o, più in generale, agli oggetti che conservano quantità di dati (Liste, Dizionari ecc).

Le interrogazioni vengono eseguite in un linguaggio simile allo pseudo codice. Se per esempio vuoi cercare un libro chiamato “Casa”, l’NSPredicate avrà una forma del genere: NSPredicate(format: “nomeLibro = ‘Casa’ “).

Ora, un NSPredicate può essere passato ad una NSFetchRequest. Quindi, oltre a cercare una tipologia di Entity potrai restringere il campo di ricerca in base al Predicate che creerai.

Nel caso degli oggetti Libro salvati nel context, un NSPredicate potrebbe essere: NSPredicate(format: “autore = ‘Dante Alighieri'”)La parte a sinistra dell’uguale è occupata dal parametro che si vuole prendere in considerazioni per la ricerca (in questo caso autore è un parametro della classe Libro).

Questo farà si che la FetchRequest passi in esame tutti gli oggetti Libro che hanno come valore associato alla proprietà “autore” quello di “Dante Alighieri”. Clicca qui per scoprire di più sulla sintassi dell’NSPredicate.

Inserisci un nuovo metodo chiamato loadBooksFromAuthor(author: String) ed inserisci il seguente codice:

Aggiungiamo una piccola pillola in più. Se guardi bene la forma del Predicate noterai che ha questa sintassi “autore = %@. Quel simbolo percentuale seguito dalla chiocciola verrà sostituito con il parametro “author” passato alla funzione:

Dopo la creazione dell’NSPredicate deve essere attaccato alla FetchRequest ed infatti, alla riga successiva, viene passato alla proprietà predicate.

Infine il context eseguirà la NSFetchRequest come previsto. L’unica differenza è che lo farà utilizzando il Predicate, quindi scartando a priori gli elementi che non rispettano quella regola.

Per semplicità e per non aggiungere altri bottoni, cambia la funzione nell’IBAction di recupero dei dati con questo nuovo metodo:

Recuperare dati con Predicate dal Core Data in Swift

Ottimizzazioni del CoreDataController

Prima di procedere oltre, facciamo qualche ottimizzazione.

Sia la funzione di recupero di tutti i libri che quella dei libri per autore, utilizzano lo stesso codice per l’esecuzione della fetch request.

Per il principio del divide et impera tanto osannato dal corso gratuito sul linguaggio Swift, possiamo dividere quel codice per crearne una terza funzione che richiamerai dalle altre due:

Adesso sia la funzione loadBooksFromAuthor che la loadBooks utilizzano la private loadBooksFromFetchRequest per stampare sulla console i ManagedObject cercati tramite una request passata come parametro.

Il metodo restituisce un array di oggetti Libro perché ti servirà tra poco per poter modificare o eliminare un oggetto dalla memoria. In altre situazioni può esserti utile per prendere i valori ed aggiornare le interfacce dei tuoi ViewController.

Domande

[su_spoiler title=”Come creo un NSPredicate di numeri?” style=”fancy”]

Nel caso in cui volessi recuperare dei ManagedObject filtrando per qualche proprietà numerica, per esempio il num_pagine del Libro, puoi utilizzare la seguente sintassi:

In questo caso la fetch request prenderà in considerazione solo i libri che hanno un num_pagine maggiore o uguale a 30. Ovviamente puoi usare anche gli altri simboli matemati (=, <, >, <= , >= e != ).

[/su_spoiler]

Modificare un NSManagedObject

Una volta che recuperi gli oggetti dal context, puoi modificarli come se fossero normalissimi dati. Qualsiasi modifica farai agli attributi di un NSManagedObject si ripercuoterà ovviamente anche sulla sua persistenza in memoria. L’unica cosa da ricordarti è che, una volta fatta la modifica, devi salvare il context in modo da rendere effettivo il cambiamento.

Aggiungi una nuova funzione al tuo CoreDataController:

Questa funzione restituisce un oggetto Libro dopo averlo ricercato all’interno del context. Stiamo dando per scontato che non esistano libri con nome identico per evitare di complicare l’algoritmo.

Infine il metodo restituisce il libro in posizione zero, cioè l’unico che dovrebbe esistere.

Nella tua idilliaca applicazione stiamo ipotizzando che tutto vada secondo i piani.

Esercizio 0. Ti sei chiesto cosa succederebbe nel caso in cui non esistesse il libro cercato? Ovviamente l’applicazione si arresterebbe in quanto il valore alla posizione zero dell’array non esisterebbe. Lascio a te il compito di risolvere questo problema.
Suggerimento: L’opzionalità in questo caso potrebbe salvarti la vita.

Un esempio di modifica potrebbe essere la seguente:

Da notare che un NSManagedObject, in questo caso “libro”, può accedere al relativo ManagedObjectContext dall’omonima proprietà. In questo caso ho sfruttato questa caratteristica per salvare le modifiche effettuate.

Eliminare un oggetto

La classe NSManagedObjectContext contiene due metodi per la rimozioni degli oggetti salvati in memoria. Il primo si chiama deleteObjects e come parametro accetta un array di NSManagedObject ed il secondo si chiama deleteObject ed accetta un NSManagedObject.

Una volta invocato uno dei due metodi, è necessario salvare il context per rendere effettiva l’eliminazione.

Considerazioni e Download

Con questo concludo la prima lezione sul Core Data in Swift. Per il momento hai tutti gli strumenti necessari per poter memorizzare qualsiasi tipo d’oggetto in memoria. Il prossimo tutorial estenderà le tue conoscenza del Core Data e ti darà una visione d’insieme più completa, permettendoti di creare app più complete e robuste.

Dove andare da qui

Download del progetto

Se hai avuto problemi con il tuo progetto, qui sotto trovi quello fatto da me:

[sociallocker] DropBox Download [/sociallocker]

Buona Programmazione!

Changelog

  • 5/10/2016 – Aggiunto il changelog. Modifica del tutorial e verifica compatibilità con Xcode 8

Start typing and press Enter to search

Tutorial modificare e Trascinare un MKPointAnnotation in SwiftSegue ed Unwind Segue in Swift. Passare dati avanti e indietro tra ViewController