Se preferite i video agli articoli scritti, questo articolo è una versione scritta di un video che ho prodotto. Il contenuto è identico.
Questo video è il primo di una serie che spero serva ad insegnarvi come usare i framework e gli strumenti di iOS come Siri Shortcuts, CloudKit, e altro. Se avete un framework specifico o una caratteristica che vorreste vedere coperta da questa serie, sentitevi liberi di scrivermi a jordanosterbergshadowsystemstech, o su Twitter @josterbe1.
Indice
- Introduzione
- Installazione delle dipendenze
- Il modello
- Implementazione di Realm
- Conclusione
Senza ulteriori indugi, diamo uno sguardo all’applicazione che costruiremo in questa serie…
Introduzione
Abbiamo la nostra lista di note, e quando tocchiamo una, possiamo leggerla e iniziare a modificarla o cancellarla. Abbastanza semplice. Noterete se scaricate il progetto iniziale che le note non persistono quando entrate e uscite dal NoteDetailController quando toccate la nota “Apple Park Visit”. Cioè, il contenuto della nota non viene salvato quando la si modifica.
Questo è ciò che costruiremo in questo articolo, usando il database Realm. Ho usato Realm per circa due anni, e trovo che la sua semplicità superi il costo dell’uso di una libreria di terze parti. Infatti, lo uso nell’app a cui dedico la maggior parte del mio tempo di sviluppo personale (vedi https://countdowns.download) sia su macOS che su iOS.
Installare le dipendenze
Per iniziare ad usare Realm, dobbiamo installarlo usando CocoaPods. CocoaPods, se non ne siete a conoscenza, CocoaPods è uno strumento di gestione delle dipendenze che è ampiamente utilizzato nello spazio iOS. Se non avete già CocoaPods installato sul vostro Mac, potete usare il comando sudo gem install cocoapods
per iniziare. Ora, aprite la cartella del progetto, così come una finestra del Terminale all’interno di quella directory.
Digitate pod init
, e poi open Podfile
.
All’interno del “Podfile” appena creato, abbiamo bisogno di scrivere del testo che informerà CocoaPods quali librerie vorreste installare nel vostro progetto.
Sotto # Pods for NotesApp
, scrivete queste due righe:
pod 'Realm', '~> 3.12.0'pod 'RealmSwift', '~> 3.12.0'
Il vostro Podfile dovrebbe essere simile a questo dopo aver scritto queste righe:
platform :ios, '12.0'target 'NotesApp' do use_frameworks! # Pods for NotesApp pod 'Realm', '~> 3.12.0' pod 'RealmSwift', '~> 3.12.0' target 'NotesAppTests' do inherit! :search_paths end target 'NotesAppUITests' do inherit! :search_paths endend
Ora che abbiamo aggiunto le nostre dipendenze, chiediamo a CocoaPods di installarle con pod install
Questo richiederà del tempo quando userete CocoaPods per la prima volta. Non preoccupatevi, CocoaPods sta solo scaricando alcuni componenti iniziali. Questo non accadrà ogni volta che pod install
.
La tua finestra del Terminale avrà questo aspetto una volta che il comando avrà finito di essere eseguito:
Dopo questo, se hai già aperto il NotesApp.xcodeproj
, chiudilo. Quando usate CocoaPods, dovete usare il file .xcworkspace
invece di quello predefinito .xcodeproj
. Apri il file NotesApp.xcworkspace
e vai a Note.swift
.
Il Modello
Questa classe contiene il nostro oggetto modello Note, che contiene alcune proprietà di base:
class Note { var identifier: String var content: String var lastEdited: Date init( identifier: String = UUID().uuidString, content: String, lastEdited: Date = Date()) { self.identifier = identifier self.content = content self.lastEdited = lastEdited }}
Codice modello standard, niente di speciale.
Abbiamo anche un’estensione del nostro oggetto Note nello stesso file, che sottoclassa come protocollo chiamato Writeable
extension Note: Writable { func write(dataSource: DataSource) { self.lastEdited = Date() dataSource.store(object: self) } func delete(dataSource: DataSource) { dataSource.delete(object: self) }}
Dentro le funzioni write
e delete
, noterete che abbiamo una proprietà DataSource
. DataSource
è un protocollo generico per aiutare a rendere la modifica dei dati il più astratta possibile ai livelli più alti del nostro codice.
Ecco la definizione del protocollo:
protocol DataSource { func store<T>(object: T) func delete<T>(object: T)}
Se non avete familiarità con i generici, ciascuna delle nostre funzioni ha un parametro T
che essenzialmente significa che può essere qualsiasi tipo di oggetto. Questo non è usato molto nel nostro progetto, ma potrebbe essere evoluto e usato ulteriormente per creare più DataSource con diversi vincoli su quali oggetti possono memorizzare.
Attuiamo il nostro protocollo DataSource
in NoteDataSource
. Non c’è niente di speciale qui, a parte un piccolo dettaglio che vorrei notare per una spiegazione.
Ogni volta che abbiamo oggetti store
o delete
, usiamo la seguente chiamata a NotificationCenter
:
NotificationCenter.default.post(name: .noteDataChanged, object: nil)// We also have this extension of Notification.Name to make sending and receiving this notification simple.extension Notification.Name { static let noteDataChanged = Notification.Name(rawValue: "noteDataChanged")}
In sostanza, ogni volta che una nota viene memorizzata o cancellata, informiamo gli ascoltatori che i nostri dati sono cambiati, così possono aggiornare la loro UI di conseguenza.
Con tutti i nostri file modello e classi fuori dai piedi, iniziamo ad implementare Realm!
Implementare Realm
Ci sono tre passi per integrare Realm con il nostro progetto:
- Creazione dell’oggetto Realm
- Collegamento tra il nostro oggetto Realm e il nostro oggetto primitivo Note
- Inizio a recuperare e modificare dati con Realm
Creazione dell’oggetto Realm
Questo passo è relativamente semplice. Creiamo un nuovo file Swift chiamato RealmNote.swift
. All’interno di RealmNote.swift
, importiamo il framework RealmSwift e creiamo una dichiarazione di classe come questa:
import RealmSwiftclass RealmNote: Object {}
Sottoclassificheremo la classe Object
di Realm che ci permetterà di usare RealmNote
nelle funzioni del database Realm.
Ora, aggiungiamo le tre proprietà che abbiamo nel nostro modello Note:
@objc dynamic var identifier: String = ""@objc dynamic var content: String = ""@objc dynamic var lastEdited: Date = Date()
I pezzi @objc dynamic
della nostra dichiarazione di variabile espongono le nostre proprietà a Objective-C, in cui sono scritti molti dei livelli iOS di Realm. Permette anche a Realm di ascoltare le modifiche ai nostri oggetti RealmNote
.
Per finire la nostra classe, sovrascriviamo la class func
primaryKey, che restituisce una stringa opzionale (String?
) con il valore “identifier”. Questo informa Realm che la chiave primaria di RealmNote
, un modo per identificare univocamente i nostri oggetti, è memorizzata nella proprietà “identifier”.
Una volta completati questi passi, il tuo RealmNote
sarà così:
class RealmNote: Object { @objc dynamic var identifier: String = "" @objc dynamic var content: String = "" @objc dynamic var lastEdited: Date = Date() override class func primaryKey() -> String? { return "identifier" }}
Questo è tutto per il primo passo.
Ponte tra il nostro oggetto Realm e il nostro oggetto primitivo Note
Abbiamo due oggetti modello separati: Note
e RealmNote
. RealmNote è usato internamente quando si ha a che fare con Realm, al fine di mantenere il nostro livello di modello disaccoppiato dalla nostra UI. Usando due oggetti separati, potremmo abbandonare l’uso di Realm in futuro, se necessario.
Nel file RealmNote.swift
, create un’estensione di RealmNote
:
extension RealmNote {}
Ora, create un convenience init
dentro l’estensione che prende un Note
come unica proprietà. Questo ci permetterà di creare oggetti RealmNote
usando un oggetto Note
.
convenience init(note: Note) { self.init() self.identifier = note.identifier self.content = note.content self.lastEdited = note.lastEdited}
Bene, ora, per finire RealmNote.swift
, crea una variabile Note
dentro l’estensione che inizializza da un RealmNote
:
var note: Note { return Note(realmNote: self)}
Non preoccuparti se Xcode ti dà un errore sull’inizializzatore di Note, stiamo per sistemarlo.
Vai a Note.swift
, dove stiamo per scrivere l’altra metà del nostro ponte. Questo codice è essenzialmente la stessa cosa dell’estensione di RealmNote
.
extension Note { convenience init(realmNote: RealmNote) { self.init(identifier: realmNote.identifier, content: realmNote.content, lastEdited: realmNote.lastEdited) } var realmNote: RealmNote { return RealmNote(note: self) }}
Bene, questo è il secondo passo. Ora possiamo accedere a un RealmNote
da un Note
, e un Note
da un RealmNote
. Questo significa anche che questo codice è perfettamente valido:
Note(content: "Example").realmNote.note.realmNote.note.realmNote.note.realmNote// and so on...
Scherzi a parte, finiamo la nostra applicazione con il terzo passo.
Iniziare a recuperare e modificare i dati con Realm
Capovolto a NoteDataSource.swift
, e import RealmSwift
prima della dichiarazione della classe. Prima di modificare, cancellare e recuperare gli oggetti Realm, abbiamo bisogno di un’istanza del Realm Database. Sostituisci l’init di NoteDataSource
con questo:
var realm: Realminit() { // Load our data self.realm = try! Realm()}
Questo creerà un’istanza del database Realm. In produzione, probabilmente non vorrete usare l’operatore bang (!) quando create l’istanza perché la vostra applicazione andrà in crash se il database non è disponibile.
Prossimo, modifichiamo la funzione store
per memorizzare effettivamente gli oggetti nel nostro database.
Scrivere con Realm è semplice:
try? self.realm.write {}
All’interno di quel blocco di codice, possiamo aggiornare gli oggetti nel database. Scrivi questo:
self.realm.add(note.realmNote, update: true)
Questo creerà un nuovo RealmNote
nel database o ne aggiornerà uno esistente se esiste. Questo è tutto! Ora stiamo memorizzando oggetti nel database di Realm (la funzione di scrittura della nota viene eseguita in NoteDetailController.swift
se vuoi vedere la funzione che viene effettivamente usata per eseguire questa scrittura.)
Ora, scriviamo la nostra funzione delete
. A causa del modo in cui abbiamo scritto il nostro ponte (senza consultare Realm ogni volta che creiamo un RealmNote
da un Note
), dobbiamo recuperare l’oggetto direttamente dal database usando l’identificatore della nota piuttosto che la sua proprietà realmNote
.
Questo è semplice:
if let realmNote = self.realm.object(ofType: RealmNote.self, forPrimaryKey: note.identifier) {}
Questo chiede a Realm di recuperare un oggetto RealmNote
dal database, con l’identificatore, o chiave primaria, di note.identifier
.
Questo può essere un po’ strano. Per qualche ragione, l’applicazione va in crash se usiamo la tradizionale funzione write
su Realm. Come workaround, questo codice è perfettamente valido ed essenzialmente esegue lo stesso compito di write
:
self.realm.beginWrite()self.realm.delete(realmNote)try? self.realm.commitWrite()
Iniziamo la nostra scrittura, cancelliamo l’oggetto, e commettiamo la nostra scrittura.
Con questo, abbiamo costruito un’applicazione di note funzionante!
Conclusione
Spero che ti sia piaciuto questo tutorial su come usare il database Realm. Mi sono divertito molto a realizzarlo e non vedo l’ora di evolvere questa serie e aggiungere altre caratteristiche alla nostra applicazione nel tempo, come Siri Shortcuts, CloudKit e altro. Grazie per aver letto.