skip to Main Content

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?

2

Answers


  1. Chosen as BEST ANSWER

    EDIT: things described below do not work like expected.

    eg. if name = "Tom" and later the same value is assigned ( self.name = "Tom") it will be treated as modification.


    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
    }
    

  2. 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")
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search