Reputation: 1184
I am trying to use Combine to update a colour when my red, green or blue variables change. The examples I have looked at use sink() and that seems appropriate for me but eraseToAnySubscriber is MIA and I can't find an alternate.
What seems to work is to use an assign() to a computed variable but that seems like a bit of a hack.
init() {
redCancellable = red.hasChanged.receive(on: RunLoop.main).assign(to: \.rgbUpdated, on: self)
}
Is there any way to save the value returned by sink()?
Upvotes: 2
Views: 659
Reputation: 535231
This sounds like a job for CombineLatest. And yes, sink
is a perfectly good way to dispose of the end of the pipeline in whatever way you like.
Here's a simple example. I'll start with an object that has r
, g
, and b
variables:
class ColorObject {
@Published var r : CGFloat = 1
@Published var g : CGFloat = 1
@Published var b : CGFloat = 1
}
Now imagine that somewhere we have an instance of that object; call it colorObject
. Then we can configure a publisher:
let rpub = colorObject.$r
let gpub = colorObject.$g
let bpub = colorObject.$b
let colorpub = Publishers.CombineLatest3(rpub,gpub,bpub)
.map { UIColor(red: $0.0, green: $0.1, blue: $0.2, alpha: 1) }
The result is that every time colorObject
's r
or g
or b
changes, a UIColor comes down the pipeline. Now we can receive a notification from colorpub
by subscribing to it with sink
and dispose of the result however we like. Let's set some interface object's color to that color:
let sink = colorpub.sink { self.view.backgroundColor = $0 }
Alternatively, I could write it using assign
, which perhaps is cleaner, though backgroundColor
is an Optional so I have to interpose a map
operator because keyPaths are not covariant:
let assign = colorpub.map{Optional($0)}
.assign(to: \.backgroundColor, on: self.view)
Now whenever colorObject
's r
, g
, or b
changes, our view's color changes accordingly.
This is not the only way to accomplish this goal — far from it! But it's a simple example of getting things done with Combine. A possibly useful variant would be to move the colorpub
publisher up into the ColorObject; that way, the ColorObject vends the color, directly, itself:
class ColorObject {
@Published var r : CGFloat = 1
@Published var g : CGFloat = 1
@Published var b : CGFloat = 1
lazy var colorpub = Publishers.CombineLatest3($r,$g,$b)
.map { UIColor(red: $0.0, green: $0.1, blue: $0.2, alpha: 1) }
}
This changes nothing about the sink
or assign
:
let sink = colorObject.colorpub.sink { // ... whatever
// or
let assign = colorObject.colorpub.map{Optional($0)}
.assign(to: \.backgroundColor, on: self.view)
Upvotes: 2