Realm Database Guide – Building a Note app in Swift for iOS

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

  1. Introduktion
  2. Installation av beroenden
  3. Modellen
  4. Implementering av Realm
  5. Slutsats

Omedelbart ska vi ta en titt på applikationen som vi kommer att bygga i denna serie…

Introduktion

Applikationens lista med anteckningar
Redigera en anteckning individuellt

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:

Terminalfönster

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:

  1. Skapa Realm-objektet
  2. Brygga mellan vårt Realm-objekt och vårt primitiva Note-objekt
  3. 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 RealmNotes 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 RealmNotes 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 NoteDataSources 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.

Lämna ett svar

Din e-postadress kommer inte publiceras.