Realm + Swift

For this tutorial I will be using a Local Realm database.

Install

Using SPM install the SDK: https://github.com/realm/realm-swift.git and select both Realm and RealmSwift.

You can import it into your project with import RealmSwift

Let's take a look now at the basic CRUD functions:

Define model

You can find all the supported data types on the official docs.

To approach this I'll use the typical to-do list example. Create a model with the following:

import Foundation
import RealmSwift

class ToDo: Object, ObjectKeyIdentifiable {
    @Persisted(primaryKey: true) var id: ObjectId
    @Persisted var name: String
    @Persisted var completed = false
    @Persisted var urgency: Urgency = .neutral
    
    enum Urgency: Int, PersistableEnum {
        case trivial, neutral, urgent
        
        var text: String {
            switch self {
            case .trivial:
                return "Trivial"
            case .neutral:
                return "Neutral"
            case .urgent:
                return "Urgent"
            }
        }
    }
    
    convenience init(name: String) {
        self.init()
        self.name = name
    }
}

Explanation:

In Realm you define your model classes by subclassing Object and adding properties to be managed. You then instantiate and use your custom subclasses instead of using the Object class directly.

The @Persisted property wrapper is managed by Realm and will allow us to persist the data. We also add a unique key to our model by adding @Persisted(primaryKey: true)

Create

Think of creating new objects as adding rows to a database, every time you create a new object you add a new row to the local DB, in our Swift example it would be adding new items to an array, so you append them. First, access the Observer on our view:

@ObservedResults(ToDo.self) var toDos

then you can create the object like this:

let newToDo = ToDo(name: name)
$toDos.append(newToDo)

Read

To read the model you just created you can use @ObservedResults(ToDo.self) var toDos, the brilliant thing about this is that we can use it in our view as if we were using a SwiftUI environment, very handy:

List() {
    ForEach(toDos) { toDo in
        Text(toDo.name)
    }
    .listRowSeparator(.hidden)
}
.listStyle(.plain)
Important: To be able to react to the changes so all the views are in sync, read/update the values on the views inside the ForEach with: `@ObservedRealmObject var toDo: ToDo`

Using Realm Studio - optional

Realm Studio is a visual tool to view, edit, and design Realm Database files. Download link.

After you download the app, on the @main view, create an .onAppear like so to find the path of the realm objects:

.onAppear {
    print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.path)
}

Next, relunch the app and go to the path printed on the console (if you didn't create any entry on the db yet then the dir. will be empty). If you have some items, you'll see a file called default.realm, you can open it with realm studio. It looks like this:

If you edit the data while running the simulator, you can see the changes in real time.

Update

To update the value of 1 object using @ObservedRealmObject var toDo: ToDo you can use:

$toDo.completed.wrappedValue.toggle()

Delete

You can delete an item inside a ForEach by pointing to it with:

$toDo.delete()

Possible errors

  1. The SwiftUI View is not updating the state once you update Realm objects

This happens because you updated the object, but the previous view has no clue what you did. What you have to do is make sure both views data are linked by Binding the Realm objects, so if you add a new view, make sure you do something like: AddNewToDo(toDo: $toDo)

2. If you get something like below is because you are trying to update the Realm object that is frozen. It's very likely you used @ObservedResults instead of @ObservedRealmObject to update that object. Also make sure you have the $ at the beggining of your update line, check above how I did the Update for reference.

Thread 1: "Attempting to modify a frozen object - call thaw on the Object instance first."

The above would "fix" it when you are modifying 1 object, however if you want to modify other objects that are not the one in the current view, you'll get something similar. For that, you need to thaw the object, ex: .thaw()?.name = ""

See for more info on thawing here & here.

Special thanks to: Realm, Stewart -> sourcecode