How to make a mechanism similar to binding in the view that refers to the Published in the ObservableObject class, but between 2 classes? That is, from class A, call the class B constructor, passing the prop1 from class A. And in class B prop2 exists, and these prop1 and prop2 should mirror each other.
PS: Attached code does not work, just visually show what I want ...
PPS: prop1 in ClassA and prop2 in ClassB, must be Published by condition. It's own views are also subscribed on it.
PPPS: Important to have any binding logic inside the ClassB, for easy reuse.
class ClassA: ObservableObject {
@Published var prop1: Bool = false //When prop1 changed, it must be reflected in prop2
let classB: ClassB
init() {
classB = .init(prop2: _prop1) //Should stay that simple, no setup here, passing only property, ClassB doesn't know about ClassA.
}
}
class ClassB: ObservableObject {
@Published var prop2: Bool //When prop2 changed, it must be reflected in prop1
init(prop2: Published<Bool>) {
_prop2 = prop2
}
}
A simple but generic solution could look like the following.
Add the following extension:
extension Published.Publisher where Value: Equatable {
mutating func link(with other: inout Self) {
removeDuplicates().assign(to: &other)
other.removeDuplicates().assign(to: &self)
}
}
This only works with values that conform to Equatable
, because otherwise you might end up in an endless loop.
Then call the extension in the initializer of ClassA
:
classB.$prop2.link(with: &$prop1)
Original Answer(s)
A simple solution would be to put the following code into your init
of ClassA
:
$prop1
.removeDuplicates()
.assign(to: &classB.$prop2) // mirror changes of prop1 into classB.prop2
classB.$prop2
.removeDuplicates()
.assign(to: &$prop1) // mirror changes of classB.prop2 into prop1
However, I'd recommend removing prop1
from ClassA
and accessing this value via classB.prop2
and classB.$prop2
, respectively. This makes the dependency clearer and doesn't require storing and updating the value in two places.
Answer to question in comments regarding putting the logic in ClassB
To do this, you could add a function in ClassB
that performs the linking:
fileprivate func linkProps(to classA: ClassA) {
$prop2
.removeDuplicates()
.assign(to: &classA.$prop1) // mirror changes of prop2 into classA.prop1
classA.$prop1
.removeDuplicates()
.assign(to: &$prop2) // mirror changes of classA.prop1 into prop2
}
And then call this function in the init
of ClassA
(and wherever else you need to):
init() {
classB = .init(prop2: _prop1)
classB.linkProps(to: self)
}
Answer to question in comments regarding keeping ClassB from knowing ClassA
This could be achieved by only passing the property wrapper's projected value into the function on ClassB
:
fileprivate func linkProp2(to prop1: inout Published<Bool>.Publisher) {
$prop2
.removeDuplicates()
.assign(to: &prop1) // mirror changes of prop2 into prop1
prop1
.removeDuplicates()
.assign(to: &$prop2) // mirror changes of prop1 into prop2
}
The call in the initializer of ClassA
would then look like this:
classB.linkProp2(to: &$prop1)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With