Jeśli wolisz filmy wideo od artykułów pisanych, ten artykuł jest pisemną wersją filmu, który wyprodukowałem. Treść jest identyczna.
To wideo jest pierwszym z serii, która, mam nadzieję, posłuży do nauczenia Cię, jak korzystać z frameworków i narzędzi iOS, takich jak Siri Shortcuts, CloudKit i inne. Jeśli masz jakiś konkretny framework lub funkcję, którą chciałbyś zobaczyć w tej serii, napisz do mnie na adres jordanosterbergshadowsystemstech lub na Twitterze @josterbe1.
Spis treści
- Wprowadzenie
- Instalacja zależności
- Model
- Implementacja Realm
- Zakończenie
Bez dalszych ceregieli, spójrzmy na aplikację, którą zbudujemy w tej serii…
Wprowadzenie
Mamy naszą listę notatek, i kiedy dotkniemy jednej z nich, możemy ją przeczytać i zacząć edytować, lub usunąć. Całkiem proste. Zauważysz, jeśli pobierzesz projekt startowy, że notatki nie zachowują się, gdy wchodzisz i wychodzisz z kontrolera NoteDetailController podczas stukania w notatkę „Wizyta w Apple Park”. To znaczy, zawartość notatki nie zapisuje się, kiedy ją edytujesz.
To właśnie zbudujemy w tym artykule, używając bazy danych Realm. Używam Realm od około dwóch lat i uważam, że jego prostota przewyższa koszty korzystania z biblioteki innej firmy. W rzeczywistości używam jej w aplikacji, nad którą spędzam większość mojego osobistego czasu na rozwoju (zobacz https://countdowns.download) zarówno na macOS, jak i iOS.
Instalowanie zależności
Aby zacząć używać Realm, musimy zainstalować go za pomocą CocoaPods. CocoaPods, jeśli nie jesteś świadomy, CocoaPods jest narzędziem do zarządzania zależnościami, które jest szeroko stosowane w przestrzeni iOS. Jeśli nie masz jeszcze zainstalowanego CocoaPods na swoim Macu, możesz użyć polecenia sudo gem install cocoapods
, aby zacząć. Teraz otwórz folder projektu, a także okno Terminala wewnątrz tego katalogu.
Wpisz pod init
, a następnie open Podfile
.
Wewnątrz nowo utworzonego pliku „Podfile” musimy napisać trochę tekstu, który poinformuje CocoaPods, jakie biblioteki chcesz zainstalować w swoim projekcie.
Poniżej # Pods for NotesApp
, napisz te dwie linie:
pod 'Realm', '~> 3.12.0'pod 'RealmSwift', '~> 3.12.0'
Twój plik Podfile powinien wyglądać podobnie do tego po napisaniu tych linii:
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
Teraz, gdy dodaliśmy nasze zależności, poprośmy CocoaPods, aby je zainstalował pod install
To zajmie trochę czasu, gdy po raz pierwszy użyjesz CocoaPods. Nie martw się, CocoaPods właśnie pobiera niektóre początkowe komponenty. Nie będzie się to działo za każdym razem, gdy pod install
.
Twoje okno terminala będzie wyglądało tak, jak poniżej, gdy to polecenie zostanie wykonane:
Po tym, jeśli już otworzyłeś NotesApp.xcodeproj
, zamknij je. Gdy używasz CocoaPods, musisz użyć pliku .xcworkspace
zamiast domyślnego pliku .xcodeproj
. Otwórz plik NotesApp.xcworkspace
i skieruj się do Note.swift
.
The Model
Ta klasa zawiera nasz obiekt modelu Note, który zawiera kilka podstawowych właściwości:
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 }}
Standardowy kod modelu, nic specjalnego się tu nie dzieje.
W tym samym pliku mamy również rozszerzenie naszego obiektu Note, który podklasuje jako protokół o nazwie Writeable
extension Note: Writable { func write(dataSource: DataSource) { self.lastEdited = Date() dataSource.store(object: self) } func delete(dataSource: DataSource) { dataSource.delete(object: self) }}
Wewnątrz funkcji write
i delete
zauważysz, że mamy właściwość DataSource
. DataSource
jest protokołem generycznym, który pomaga uczynić modyfikację danych tak abstrakcyjną na wyższych poziomach naszego kodu, jak to tylko możliwe.
Oto definicja protokołu:
protocol DataSource { func store<T>(object: T) func delete<T>(object: T)}
Jeśli nie jesteś zaznajomiony z generycznymi, każda z naszych funkcji ma parametr T
, co zasadniczo oznacza, że może to być dowolny typ obiektu. Nie jest to bardzo mocno wykorzystywane w naszym projekcie, ale mogłoby być rozwinięte i użyte dalej do tworzenia wielu DataSources z różnymi ograniczeniami wokół obiektów, które mogą przechowywać.
Wdrażamy nasz DataSource
protokół w NoteDataSource
. Tutaj również nie ma nic specjalnego, poza jedną małą ciekawostką, którą chciałbym zauważyć dla wyjaśnienia.
Kiedy my store
lub delete
obiekty, używamy następującego wywołania do 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")}
Podstawowo, za każdym razem, gdy jakakolwiek notatka jest przechowywana lub usuwana, informujemy wszystkich słuchaczy, że nasze dane uległy zmianie, więc mogą oni odpowiednio zaktualizować swoje UI.
Mając już za sobą wszystkie nasze pliki modeli i klasy, zacznijmy implementować Realm!
Wdrażanie Realm
Aby zintegrować Realm z naszym projektem, należy wykonać trzy kroki:
- Tworzenie obiektu Realm
- Bridging pomiędzy naszym obiektem Realm a naszym prymitywnym obiektem Note
- Zacznij pobierać i modyfikować dane za pomocą Realm
Tworzenie obiektu Realm
Ten krok jest stosunkowo prosty. Utwórzmy nowy plik Swift o nazwie RealmNote.swift
. Wewnątrz RealmNote.swift
zaimportujmy framework RealmSwift i utwórzmy deklarację klasy w taki sposób:
import RealmSwiftclass RealmNote: Object {}
Podklasujemy klasę Object
Realm, która pozwoli nam używać RealmNote
w funkcjach bazy danych Realm.
Teraz dodajemy trzy właściwości, które mamy w naszym modelu Note:
@objc dynamic var identifier: String = ""@objc dynamic var content: String = ""@objc dynamic var lastEdited: Date = Date()
Części @objc dynamic
naszej deklaracji zmiennych wystawiają nasze właściwości na Objective-C, w którym napisanych jest wiele warstw Realm dla iOS. Pozwala to również Realm na nasłuchiwanie zmian w naszych RealmNote
obiektach.
Aby zakończyć naszą klasę, nadpisujemy class func
primaryKey, która zwraca opcjonalny łańcuch (String?
) o wartości „identyfikator”. To informuje Realm, że klucz główny RealmNote
, sposób na unikalną identyfikację naszych obiektów, jest przechowywany we właściwości „identifier”.
Po wykonaniu tych kroków, twój RealmNote
będzie wyglądał tak:
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" }}
To wszystko dla kroku pierwszego.
Pomost między naszym obiektem Realm a naszym prymitywnym obiektem Note
Mamy dwa oddzielne obiekty modelu: Note
i RealmNote
. RealmNote jest używany wewnętrznie, kiedy mamy do czynienia z Realm, aby utrzymać naszą warstwę modelu oddzieloną od naszego UI. Używając dwóch oddzielnych obiektów, możemy zrezygnować z Realm w przyszłości, jeśli zajdzie taka potrzeba.
W pliku RealmNote.swift
utwórz rozszerzenie RealmNote
:
extension RealmNote {}
Teraz utwórz convenience init
wewnątrz rozszerzenia, które przyjmuje Note
jako swoją jedyną właściwość. To pozwoli nam tworzyć obiekty RealmNote
za pomocą obiektu Note
.
convenience init(note: Note) { self.init() self.identifier = note.identifier self.content = note.content self.lastEdited = note.lastEdited}
Dobrze, teraz, aby zakończyć RealmNote.swift
off, utwórz Note
zmienną wewnątrz rozszerzenia, która inicjalizuje się z RealmNote
:
var note: Note { return Note(realmNote: self)}
Nie martw się, jeśli Xcode daje ci błąd o inicjalizatorze Note, zaraz to naprawimy.
Przejdź do Note.swift
, gdzie zamierzamy napisać drugą połowę naszego mostu. Ten kod jest w zasadzie tym samym, co rozszerzenie 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) }}
Dobrze, to jest krok drugi. Możemy teraz uzyskać dostęp do RealmNote
z Note
, a do Note
z RealmNote
. Oznacza to również, że ten kod jest całkowicie poprawny:
Note(content: "Example").realmNote.note.realmNote.note.realmNote.note.realmNote// and so on...
Żarty na bok, zakończmy naszą aplikację krokiem trzecim.
Rozpocznij pobieranie i modyfikowanie danych za pomocą Realm
Zagłęb się w NoteDataSource.swift
, a import RealmSwift
przed deklaracją klasy. Zanim zaczniemy modyfikować, usuwać i pobierać obiekty Realm, potrzebujemy instancji bazy danych Realm. Zamień init NoteDataSource
na to:
var realm: Realminit() { // Load our data self.realm = try! Realm()}
To utworzy instancję bazy danych Realm. W produkcji, prawdopodobnie nie chciałbyś używać operatora bang (!) podczas tworzenia instancji, ponieważ twoja aplikacja ulegnie awarii, jeśli baza danych nie będzie dostępna.
Następnie, edytujmy funkcję store
, aby faktycznie przechowywać obiekty w naszej bazie danych.
Pisanie z Realm jest proste:
try? self.realm.write {}
Wewnątrz tego bloku kodu, możemy aktualizować obiekty w bazie danych. Napisz to:
self.realm.add(note.realmNote, update: true)
To utworzy nowy RealmNote
w bazie danych lub zaktualizuje istniejący, jeśli taki istnieje. To wszystko! Teraz przechowujemy obiekty w bazie danych Realm (wywołanie funkcji zapisu notatki jest wykonywane w NoteDetailController.swift
, jeśli chciałbyś zobaczyć funkcję, która jest faktycznie używana do wykonania tego zapisu.)
Teraz napiszmy naszą funkcję delete
. Ze względu na sposób, w jaki napisaliśmy nasz most (bez konsultacji z Realm za każdym razem, gdy tworzymy RealmNote
z Note
), musimy pobrać obiekt bezpośrednio z bazy danych używając identyfikatora notatki, a nie używając jej właściwości realmNote
.
To jest proste:
if let realmNote = self.realm.object(ofType: RealmNote.self, forPrimaryKey: note.identifier) {}
To prosi Realm o pobranie obiektu RealmNote
z bazy danych, z identyfikatorem lub kluczem głównym note.identifier
.
To może być trochę dziwne. Z jakiegoś powodu, aplikacja zawiesza się, jeśli użyjemy tradycyjnej funkcji write
na Realm. Jako obejście, ten kod jest całkowicie poprawny i zasadniczo wykonuje to samo zadanie, co write
:
self.realm.beginWrite()self.realm.delete(realmNote)try? self.realm.commitWrite()
Zaczynamy nasz zapis, usuwamy obiekt i zatwierdzamy nasz zapis.
W ten sposób zbudowaliśmy działającą aplikację notatek!
Zakończenie
Mam nadzieję, że podobał ci się ten poradnik, jak używać bazy danych Realm. Miałem dużo zabawy przy jego tworzeniu i nie mogę się doczekać, aby rozwinąć tę serię i dodać więcej funkcji do naszej aplikacji z czasem, takich jak skróty Siri, CloudKit i więcej. Dzięki za przeczytanie.