Realm Database is a free and open-source object database. One of benefits of Realm is that it offers high-fidelity data browser and editor app. Realm Studio lets you inspect the contents of your app’s realm files and change any of the data manually, if necessary.
Add the following to your Podfile:
pod 'RealmSwift'
To import Realm into the ViewController, all we need to do is add the following import:
import RealmSwift
Model
When we are creating a model we need that model to be a subclass of Object
.
class Movie: Object { @objc dynamic var id = UUID().uuidString @objc dynamic var title = "" @objc dynamic var year = 0 override static func primaryKey() -> String? { return "id" } convenience init(_ title: String, year: Int) { self.init() item.title = title item.year = year return item } } class Favorites: Object { @objc dynamic var name = "" var movies = List<Movie>() // To-many relationships static func create(withName name: String, movies: [Movie]) -> Favorites { let item = Favorites() item.name = name item.movies.append(objectsIn: movies) return item } }
dynamic
, in combination with @objc
, makes properties accessible at run time via Dynamic Dispatch. This allows the class to provide a custom, under-the-hood implementation of the data storage of these properties.
To add an index, you need to override the static indexedProperties
method. Add the following code to your Movie class:
override static func indexedProperties() -> [String] { return ["title", "year"] }
Write to Realm
Now that we have our models setup, we can start saving data to our database. To write, we are going to create a simple method that will write data to our database.
let realm = try! Realm() private func write() { let movie1 = Movie.create("The Godfather", 1972) let movie2 = Movie.create("The Dark Knight", 2008) let favorites = Favorites.create(withName: "My IMDb", movies: [movie1, movie2]) try! realm.write { realm.add(movie1) realm.add(movie2) realm.add(favorites) } }
Read from Realm
Let's create the fetch
method.
func fetch() { let data = realm.objects(Favorites.self) print(data) } func fetchWithFilter() { realm.objects(Movie.self).filter("year > 2000").sorted("year") } func all(in realm: Realm = try! Realm()) -> Results<Movie> { return realm.objects(Movie.self).sorted("year") }
There’s even a special Realm API you can use to quickly retrieve an object by its primary key:
let object = realm.object(ofType: Movie.self, forPrimaryKey: "89967121-61D0-4874-8FFF-E6E05A109292")
There are some other approaches for fetch
let objects = realm.objects(Movie.self).filter("title IN %@", ["Jane", "Frank"]) let objects = realm.objects(Movie.self).filter("year BETWEEN {%d, %d}", 1978, 2010) let sortedMultiple = realm.objects(Movie.self) .sorted(by: [ SortDescriptor(keyPath: "year", ascending: true), SortDescriptor(keyPath: "title", ascending: false) ])
Update existing data
private func update() { if let favorites = realm.objects(Favorites.self).first { try! realm.write { favorites.name = "My Favorites" } print(realm.objects(Favorites.self).first) } }
Delete data
private func delete() { if let favorites = realm.objects(Favorites.self).first { try! realm.write { realm.delete(favorites) } print(realm.objects(Favorites.self).first) } }
Reacting to data changes
Realm’s own change notifications mechanism lets your classes read and write data independently and be notified, in real-time, about any changes that occurred. Realm’s own notification system is incredibly useful because it lets you cleanly separate your data persistence code.
Realm provides notifications on three levels, going from more granular to less:
Open ViewController.swift and add a new property at the top of the class:
private var itemsToken: NotificationToken?
A notification token keeps a reference to a subscription for change notifications. Continue in viewWillAppear(_)
where you’ll set your notification token:
itemsToken = all()?.observe { [weak tableView] changes in guard let tableView = tableView else { return } switch changes { case .initial: tableView.reloadData() case .update(_, let deletions, let insertions, let updates): tableView.applyChanges(deletions: deletions, insertions: insertions, updates: updates) case .error: break } }
The observe(_)
callback closure is the place to implement any UI code that will reflect the latest data changes in your app’s UI.
Next, since you start observing items in viewWillAppear(_)
, it makes sense to stop the observation in viewWillDisappear(_)
.
Add the following to viewWillDisappear(_)
:
itemsToken?.invalidate()