Search code examples
iosswiftmvvmreactive-cocoareactive-swift

Initializing a RAC ReactiveSwift Property that has multiple dependencies?


I am fairly new with using ReactiveSwift and ReactiveCocoa and I seem to have hit a road block regarding the proper way of initializing a Property that has a dependencies.

For instance in the following code, I try to initialize a Property but i get a compiler error which is expected. My question is how/ what is the "correct" way to do this.

class SomeViewModel {
// illustration purposes, in reality the property (dependency) we will observe will change over time
let dependency = Property(value: true)
let dependency2 = Property(value: "dependency2")
let dependency3 = Property(value: 12345)
let weightLabel: Property<String>

// private(set) var weightLabel: Property<String>!
// using private(set) var weightLabel: Property<String>! works, 
// however this changes the meaning behind using let, because we could
// reinitalize weightLabel again which is not similar to using a let so not a good alternative

// let weightLabel: Property<String> = Property(value: "")
// another solution that will work but will result in a wrong value
// upon initalization then, changed into the "correct value" thus, i
// am discrading this as well

init() {
    weightLabel = dependency.map {
        // compiler error, 'self' captured by closure before all members were initalized. 
        // My question is if there is a way to handle this scenario properly
        if $0 && self.dependency2.value == "dependency2" && self.dependency3.value == 12345 {
            return ""
        }
        return ""
    }
}
}

So as you might have noticed above in the comments I am wondering if there is a way to handle this scenario with ReactiveSwift other then the ones i mentioned above that are not really ideal solutions.


Solution

  • The instrument that fits the scenario is combineLatest, which provides a combined version of all these properties (streams) whenever any of them has been updated.

    weightLabel = Property.combineLatest(dependency, dependency2, dependency3)
        .map { d1, d2, d3 in
            return "Hello World! \(d1) \(d2) \(d3)"
        }
    

    Regarding the compiler error, the issue is that you are capturing/referring to self in a closure before every stored property has been initialised. Depending on the intention, you may use a capture list to capture directly the values and objects you are interested w/o self.

    let title: String
    let action: () -> Void
    
    init() {
        title = "Hello World!"
    
        // 🚫 `action` has not been initialised when `self` is
        // being captured.
        action = { print(self.title) } 
    
        // ✅ Capture `title` directly. Now the compiler is happy.
        action = { [title] in print(title) }
    }