Home > other >  Is Realm smart when updating values or check for new values should be perfomed manually?
Is Realm smart when updating values or check for new values should be perfomed manually?

Time:08-05

I wonder if I should create my own additional layer when updating Realm objects to avoid redundant database writing operations or is it done automatically on a lower level?

Let's take an example:

class SomeEntity: Object {
    @Persisted(primaryKey: true) var id = 0
    @Persisted var aaa: String?
    @Persisted var bbb: Float?
    @Persisted var ccc: Int?
}

when doing some batch update:

newDownloadedData.forEach { entry in
    guard let id = entry["id"].int else {
        return
    }
    try? localRealm.write {
        let entity = existingLocalEntities.first { $0.id == id } ?? SomeEntity(id: id)
        localRealm.add(entity, update: .modified) //this makes an 'upsertion' which is automatically an update or insert
        if entity.aaa != entry["aaa"].string {
            entity.aaa = movieInfo["aaa"].string
        }
        if entity.bbb != entry["bbb"].float {
            entity.bbb = movieInfo["bbb"].float
        }
        if entity.ccc != entry["ccc"].int {
            entity.ccc = movieInfo["ccc"].int
        }
    }
}

I wonder if these checks necessary or can I just go with:

entity.aaa = movieInfo["aaa"].string
entity.bbb = movieInfo["bbb"].float
entity.ccc = movieInfo["ccc"].int

and not worry that values will be updated and written even if downloaded values are the same as existing local ones?

CodePudding user response:

Your observers will be notified if you update a property on a realm object with the same value. Realm does not care if you use a different value or not.

I'm not sure what your use case is, but it may be a pain in the butt to check every value manually.

You can do something like this though:

protocol UniqueUpdating { }

extension UniqueUpdating where Self: AnyObject {

    @discardableResult
    func update<Value: Equatable>(
        _ keyPath: ReferenceWritableKeyPath<Self, Value>,
        to value: Value
    ) -> Bool {
        guard self[keyPath: keyPath] != value else { return false }
        self[keyPath: keyPath] = value
        return true
    }
}

extension Object: UniqueUpdating {}

class Person: Object {
    @Persisted(primaryKey: true) var id: Int = 0
    @Persisted var name: String = ""
}

Usage would be like this:

let realm = try! Realm()
try! realm.write {
    person.update(\.name, to: "BOB")
}

CodePudding user response:

It turns out that YES, Realm can be smart about it!

the clue sits in update parameter in .add function.

using .modified will result in smart data write.

Excerpt from documentation:

/**
 What to do when an object being added to or created in a Realm has a primary key that already exists.
 */
@frozen public enum UpdatePolicy: Int {
    /**
     Throw an exception. This is the default when no policy is specified for `add()` or `create()`.

     This behavior is the same as passing `update: false` to `add()` or `create()`.
     */
    case error = 1
    /**
     Overwrite only properties in the existing object which are different from the new values. This results
     in change notifications reporting only the properties which changed, and influences the sync merge logic.

     If few or no of the properties are changing this will be faster than .all and reduce how much data has
     to be written to the Realm file. If all of the properties are changing, it may be slower than .all (but
     will never result in *more* data being written).
     */
    case modified = 3
    /**
     Overwrite all properties in the existing object with the new values, even if they have not changed. This
     results in change notifications reporting all properties as changed, and influences the sync merge logic.

     This behavior is the same as passing `update: true` to `add()` or `create()`.
     */
    case all = 2
}
  • Related