Pattern for background thread re-use?


#1

Hello, new to Realm (and Swift) here and have a question about keeping my code DRY when doing background threads in realm.

I am using Eureka forms, and the definition I have is like this:

form
+++ Section()

<<< TextRow("name") {
    $0.title = "Name"
    $0.placeholder = "My home"
    $0.add(rule: RuleRequired())
    if homeProfile.name != "" {
        $0.value = homeProfile.name
    }
    }.onChange { row in
        if let value = row.value {
            DispatchQueue.global(qos: .background).async {
                let realm = try! Realm()
                if let homeProfile = realm.object(ofType: HomeProfile.self, forPrimaryKey: 1) {
                    try! realm.write {
                        homeProfile.name = value
                    }
                }
            }
        }
    }

<<< ZipCodeRow("zip") {
    $0.title = "Zipcode"
    $0.placeholder = "10001"
    $0.add(rule: RuleRequired())
    if homeProfile.zip != "" {
        $0.value = homeProfile.zip
    }
    }.onChange { row in
        if let value = row.value {
            DispatchQueue.global(qos: .background).async {
                let realm = try! Realm()
                if let homeProfile = realm.object(ofType: HomeProfile.self, forPrimaryKey: 1) {
                    try! realm.write {
                        homeProfile.zip = value
                    }
                }
            }
        }
    }

As you can see, I am duplicating a large amount of code just to update the name vs the zip. Is there a way to pull out all of the DispatchQueue.global... logic into a separate function? I tried it with generics but could not figure out how to specify a specific field like “.zip” or “.name” as a parameter. Is this possible with realm while remaining thread safe?

Something like:

func bgUpdate(fieldName: String, value: Any) {
    DispatchQueue.global(qos: .background).async {
        let realm = try! Realm()
        if let homeProfile = realm.object(ofType: HomeProfile.self, forPrimaryKey: 1) {
            try! realm.write {
                homeProfile[fieldName] = value // this doesn't actually work, just example of what I want
            }
        }
    }
}

...later 
bgUpdate('name', 'My home')
bgUpdate('zip', 10001)

?


#2

I was able to solve this using a callback func like this:

    func updateFieldAsync(callback: @escaping (HomeProfile) -> Void) {
        self.dispatchQueue.async {
            let realm = try! Realm()
            if let homeProfile = realm.object(ofType: HomeProfile.self, forPrimaryKey: 1) {
                try! realm.write {
                    callback(homeProfile)
                }
            }
        }
    }

then use like this:

.onChange {
    [weak self]
    row in
    if let value = row.value {
        self?.updateFieldAsync() {
            hp in
            hp.name = value
        }
    }
}

#3

I was able to make more generic like so:

func updateFieldAsync<T>(callback: @escaping (T) -> Void) {
    DispatchQueue.global(qos: .background).async {
        let realm = try! Realm()
        if let objectToUpdate = realm.object(ofType: T.self as! Object.Type, forPrimaryKey: 1) {
            try! realm.write {
                callback(objectToUpdate as! T)
            }
        }
    }
}

and call it like:

if let value = row.value {
    self?.updateFieldAsync() {
        (hp: HomeProfile) in
        hp.name = value
    }
}

I have primary key hardcoded because I want only one of these objects in my app, but trivial to add that as a param instead… hope it helps :slight_smile: