Om du föredrar videor framför skrivna artiklar är den här artikeln en skriftlig version av en video som jag producerade. Innehållet är identiskt.
Den här videon är den första i en serie som jag hoppas ska tjäna till att lära dig hur du använder iOS-ramverk och verktyg som Siri Shortcuts, CloudKit med mera. Om du har ett specifikt ramverk eller en specifik funktion som du vill att den här serien ska täcka, skriv gärna till mig på jordanosterbergshadowsystemstech, eller på Twitter @josterbe1.
Innehållsförteckning
- Introduktion
- Installation av beroenden
- Modellen
- Implementering av Realm
- Slutsats
Omedelbart ska vi ta en titt på applikationen som vi kommer att bygga i denna serie…
Introduktion
Vi har vår lista med anteckningar och när vi trycker på en kan vi läsa och börja redigera eller radera den. Det är ganska enkelt. Du märker om du laddar ner startprojektet att anteckningarna inte finns kvar när du går in och ut ur NoteDetailController när du trycker på anteckningen ”Apple Park Visit”. Det vill säga, innehållet i anteckningen sparas inte när du redigerar den.
Det är vad vi ska bygga ut i den här artikeln, med hjälp av Realm Database. Jag har använt Realm i ungefär två år, och jag tycker att dess enkelhet uppväger kostnaden för att använda ett bibliotek från tredje part. Faktum är att jag använder det i den app som jag lägger mest av min personliga utvecklingstid på (se https://countdowns.download) på både macOS och iOS.
Installera beroenden
För att börja använda Realm måste vi installera det med hjälp av CocoaPods. CocoaPods, om du inte känner till det, CocoaPods är ett beroendehanteringsverktyg som används flitigt inom iOS-området. Om du inte redan har CocoaPods installerat på din Mac kan du använda kommandot sudo gem install cocoapods
för att komma igång. Öppna nu projektmappen samt ett Terminal-fönster i den mappen.
Typ in pod init
och sedan open Podfile
.
Inuti den nyskapade ”Podfilen” måste vi skriva lite text som informerar CocoaPods om vilka bibliotek du vill installera i ditt projekt.
Under # Pods for NotesApp
skriver du dessa två rader:
pod 'Realm', '~> 3.12.0'pod 'RealmSwift', '~> 3.12.0'
Din Podfile bör se ut ungefär så här när du har skrivit dessa rader:
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
Nu när vi har lagt till våra beroenden kan vi be CocoaPods att installera dem med pod install
Detta kommer att ta lite tid när du först använder CocoaPods. Oroa dig inte, CocoaPods laddar bara ner några inledande komponenter. Detta kommer inte att ske varje gång du pod install
.
Ditt terminalfönster kommer att se ut så här när det kommandot är klart:
Efter detta, om du redan har öppnat NotesApp.xcodeproj
, stänger du ut det. När du använder CocoaPods måste du använda .xcworkspace
-filen i stället för standardfilen .xcodeproj
. Öppna NotesApp.xcworkspace
-filen och gå till Note.swift
.
Modellen
Den här klassen innehåller vårt Note-modellobjekt, som innehåller några grundläggande egenskaper:
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 }}
Standardmodellkod, inget speciellt händer här.
Vi har också ett tillägg till vårt Note-objekt i samma fil, som underklassar som protokoll kallat Writeable
extension Note: Writable { func write(dataSource: DataSource) { self.lastEdited = Date() dataSource.store(object: self) } func delete(dataSource: DataSource) { dataSource.delete(object: self) }}
Inom funktionerna write
och delete
märker du att vi har en DataSource
egenskap. DataSource
är ett generiskt protokoll som hjälper till att göra modifiering av data så abstrakt som möjligt på de högre nivåerna i vår kod.
Här är protokolldefinitionen:
protocol DataSource { func store<T>(object: T) func delete<T>(object: T)}
Om du inte är bekant med generiska egenskaper har var och en av våra funktioner en T
-parameter, vilket i huvudsak innebär att det kan vara vilken typ av objekt som helst. Detta används inte särskilt mycket i vårt projekt, men det skulle kunna utvecklas och användas vidare för att skapa flera DataSources med olika begränsningar kring vilka objekt de kan lagra.
Vi implementerar vårt DataSource
-protokoll i NoteDataSource
. Det finns inget speciellt här heller, bortsett från en liten detalj som jag vill notera för förklaringens skull.
När vi store
eller delete
-objekt använder vi följande anrop till 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")}
I huvudsak informerar vi alla lyssnare om att våra data har ändrats, närhelst en anteckning lagras eller raderas, så att de kan uppdatera sitt användargränssnitt i enlighet med detta.
Med alla våra modellfiler och klasser ur vägen kan vi börja implementera Realm!
Implementera Realm
Det finns tre steg för att integrera Realm med vårt projekt:
- Skapa Realm-objektet
- Brygga mellan vårt Realm-objekt och vårt primitiva Note-objekt
- Begynna att hämta och ändra data med Realm
Skapa Realm-objektet
Detta steg är relativt enkelt. Vi skapar en ny Swift-fil som heter RealmNote.swift
. Inne i RealmNote.swift
importerar du RealmSwift-ramverket och skapar en klassdeklaration så här:
import RealmSwiftclass RealmNote: Object {}
Vi kommer att underklassa Realms Object
-klass vilket gör det möjligt för oss att använda RealmNote
i Realms databasfunktioner.
Nu lägger vi till de tre egenskaperna som vi har i vår Note-modell:
@objc dynamic var identifier: String = ""@objc dynamic var content: String = ""@objc dynamic var lastEdited: Date = Date()
De @objc dynamic
-bitar som ingår i vår variabeldeklaration exponerar våra egenskaper för Objective-C, som många av iOS-skikten i Realm är skrivna i. Det gör det också möjligt för Realm att lyssna efter ändringar i våra RealmNote
objekt.
För att avsluta vår klass åsidosätter du class func
primaryKey, som returnerar en valfri sträng (String?
) med värdet ”identifier”. Detta informerar Realm om att RealmNote
s primära nyckel, ett sätt att unikt identifiera våra objekt, lagras i egenskapen ”identifier”.
När du har slutfört dessa steg kommer din RealmNote
att se ut så här:
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" }}
Det var allt för steg ett.
Bryggning mellan vårt Realm-objekt och vårt primitiva Note-objekt
Vi har två separata modellobjekt: Note
och RealmNote
. RealmNote används internt när vi hanterar Realm, för att hålla vårt modellskikt frikopplat från vårt användargränssnitt. Genom att använda två separata objekt kan vi byta bort Realm i framtiden om behovet uppstår.
I filen RealmNote.swift
skapar du en förlängning av RealmNote
:
extension RealmNote {}
Skapa nu en convenience init
inne i förlängningen som tar emot en Note
som dess enda egenskap. Detta gör att vi kan skapa RealmNote
-objekt genom att använda ett Note
-objekt.
convenience init(note: Note) { self.init() self.identifier = note.identifier self.content = note.content self.lastEdited = note.lastEdited}
Skönt, nu, för att avsluta RealmNote.swift
, skapa en Note
-variabel inne i förlängningen som initialiseras från ett RealmNote
:
var note: Note { return Note(realmNote: self)}
Oroa dig inte om Xcode ger dig ett fel om Notes initialiserare, vi ska strax åtgärda det.
Hoppa över till Note.swift
, där vi ska skriva den andra halvan av vår bro. Den här koden är i princip samma sak som RealmNote
s förlängning.
extension Note { convenience init(realmNote: RealmNote) { self.init(identifier: realmNote.identifier, content: realmNote.content, lastEdited: realmNote.lastEdited) } var realmNote: RealmNote { return RealmNote(note: self) }}
Skönt, det är steg två. Vi kan nu komma åt en RealmNote
från en Note
och en Note
från en RealmNote
. Det betyder också att den här koden är helt giltig:
Note(content: "Example").realmNote.note.realmNote.note.realmNote.note.realmNote// and so on...
Skämt åsido, låt oss avsluta vår applikation med steg tre.
Börja hämta och ändra data med Realm
Hoppa in i NoteDataSource.swift
, och import RealmSwift
före klassdeklarationen. Innan vi ändrar, tar bort och hämtar Realm-objekt behöver vi en instans av Realm-databasen. Ersätt NoteDataSource
s init med detta:
var realm: Realminit() { // Load our data self.realm = try! Realm()}
Detta kommer att skapa en instans av Realm-databasen. I produktion vill du troligen inte använda operatören bang (!) när du skapar instansen eftersom programmet kommer att krascha om databasen inte är tillgänglig.
Nästan, låt oss redigera store
-funktionen för att faktiskt lagra objekt i vår databas.
Att skriva med Realm är enkelt:
try? self.realm.write {}
Inom det kodblocket kan vi uppdatera objekt i databasen. Skriv detta:
self.realm.add(note.realmNote, update: true)
Detta kommer antingen att skapa en ny RealmNote
i databasen eller uppdatera en befintlig RealmNote
om en sådan finns. Det var allt! Vi lagrar nu objekt i Realm-databasen (att kalla anteckningens skrivfunktion utförs i NoteDetailController.swift
om du vill se den funktion som faktiskt används för att utföra denna skrivning.)
Nu ska vi skriva vår delete
-funktion. På grund av hur vi skrev vår bro (utan att konsultera Realm när vi skapar en RealmNote
från en Note
) måste vi hämta objektet direkt från databasen med hjälp av notisens identifierare i stället för att använda dess realmNote
-egenskap.
Detta är enkelt:
if let realmNote = self.realm.object(ofType: RealmNote.self, forPrimaryKey: note.identifier) {}
Detta ber Realm att hämta ett RealmNote
-objekt från databasen, med identifieraren, eller primärnyckeln, note.identifier
.
Detta kan vara lite lustigt. Av någon anledning kraschar programmet om vi använder den traditionella write
-funktionen på Realm. Som en lösning är den här koden helt giltig och utför i princip samma uppgift som write
:
self.realm.beginWrite()self.realm.delete(realmNote)try? self.realm.commitWrite()
Vi påbörjar vår skrivning, vi raderar objektet och vi bekräftar vår skrivning.
Med det har vi byggt en fungerande anteckningsapplikation!
Slutsats
Jag hoppas att du har gillat denna handledning om hur man använder Realm-databasen. Jag har haft väldigt roligt att göra den och jag kan inte vänta på att utveckla den här serien och lägga till fler funktioner till vår app med tiden, till exempel Siri Shortcuts, CloudKit med mera. Tack för att du läste.