Search code examples
swiftrealmrealm-mobile-platform

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.


Solution

  • 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!
            }
        }
    }