I have a class with two variables depending on each other. If one variable changes it should change the other and vice versa.
I am using the Combine framework in iOS Swift. So with sink
I am listening/subscribe to changes of the publisher.
var store = Set<AnyCancellable>()
class Obs: ObservableObject {
@Published var pub: Int = 0
}
let ob1 = Obs()
let ob2 = Obs()
ob1.$pub
.dropFirst() // ignore initial assignment
.sink { num in
if ob2.pub != num {
ob2.pub = num
}
}.store(in: &store)
ob2.$pub
.dropFirst()
.sink { num in
if ob1.pub != num {
ob1.pub = num
}
}.store(in: &store)
ob1.pub = 1
With this code I get a stack overflow. How can I break the "infinite loop"?
Should I use something like semaphores? Or some kind of flag within a tuple? Or are there some special combine filter? Or any other ideas…?
I think I may have some general misunderstanding of the problem…
2
Answers
I got a workaround with the Combine operators
scan
andcompactMap
. So I can compare the new value with the previous/old one. If the new value and the old value are equal the published value is transformed tonil
and dropped/ignored withcompactMap
.This helped me to break the loop in my case.
The "problem" is that publishers have willSet semantics, i.e. the
pub
variable will first publish the new value and then the variablepub
will change its value. So the conditionob1.pub != num
that reads the ob1 value will never be false.In order to send the value after the variable has been updated you modify the publisher to
receive(on:)
a scheduler: