melsam
melsam

Reputation: 4977

RLMException when save an object in a background thread

When saving an object asynchronously in the background, I get RLMException: 'Can not add objects from a different Realm'. However, the same save works fine if remove the async code.

This object has a relationship to an existing object. For example:

class Person: Object {
  name: String
  school: School
}

class School: Object {
  name: String
}

let person = new Person()
person.name = "John"
person.school = school // Existing object selected from a dropdown.

DispatchQueue.global().async {
    do {
        let realm = try Realm!

        try realm.write {
            realm.add(person, update: true)
        }

        DispatchQueue.main.async {
            // Success!
        }
    } catch let error as NSError {
        DispatchQueue.main.async {
            // Error!
        }
    }
}

This code results in a crash. However if I remove the DispatchQueye.global().async, everything works fine. Is there some threading issue I'm running into?

Note: the school object is pre-existing, and selected from a Results<School> collection.

Upvotes: 1

Views: 1180

Answers (1)

TiM
TiM

Reputation: 15991

Realm Object instances can't be moved between threads once they've been saved to Realm. Since school would be an object that was instantiated on the main thread, you're creating a conflict by attaching it to an un-persisted object and moving the lot to a background thread.

To fix this, you'll need to make a background version of the school object using Realm's thread reference feature.

Also, unless you have a specific reason for creating Person on the main thread, I'd also recommend moving its creation to the background thread too.

let schoolRef = ThreadSafeReference(to: school)

DispatchQueue.global().async {
    do {
        let realm = try Realm!

        let person = new Person()
        person.name = "John"
        person.school = realm.resolve(schoolRef)!

        try realm.write {
            realm.add(person, update: true)
        }

        DispatchQueue.main.async {
            // Success!
        }
    } catch let error as NSError {
        DispatchQueue.main.async {
            // Error!
        }
    }
}

Upvotes: 6

Related Questions