Getting Started with Realm Database for iOS in Swift iOS 01.08.2020

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:

  • Object level. You can observe a single object for property changes.
  • Collection level. You can observe a collection of objects — a list, results, or linking objects property — for changes happening on any of its elements or the collection itself.
  • Realm level. You can observe changes on an entire Realm, as a notification is sent after each successful write transaction.

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()