Let’s say you are using the built-in .store(in:)
method on AnyCancellable like so:
private var subscriptions = Set<AnyCancellable>()
let newPhotos = photos.selectedPhotos
newPhotos
.map { [unowned self] newImage in
return self.images.value + [newImage]
}
.assign(to: \.value, on: images)
.store(in: &subscriptions)
If you have an app that does this a lot - are these removed when the publishers complete?
Also, If i decide to go with this approach instead:
private var newPhotosSubscription: AnyCancellable?
self.newPhotosSubscription = newPhotos
.map { [unowned self] newImage in
self.images.value + [newImage]
}
.assign(to: \.value, on: images)
Everytime I call the method again, it override the AnyCancellable, what happens to the previous one? Does it still complete before being deallocated?
Dávid Pásztor's solution is close, but it has a race condition. Specifically, suppose the newPhotos
publisher completes synchronously, before the sink
method returns. Some publishers operate this way. Just
and Result.Publisher
both do, among others.
In that case, the completion block runs before sink
returns. Then, sink
returns an AnyCancellable
which is stored in newPhotosSubscription
. But the subscription has already completed, so newPhotosSubscription
will never be set back to nil.
So for example if you use a URLSession.DataTaskPublisher
in live code but substitute a Just
publisher in some test cases, the tests could trigger the race condition.
Here's one way to fix this: keep track of whether the subscription has completed. Check it after sink
returns, before setting newPhotosSubscription
.
private var ticket: AnyCancellable? = nil
if ticket == nil {
var didComplete = false
let newTicket = newPhotos
.sink(
receiveValue: { [weak self] in
self?.images.value.append($0)
},
receiveCompletion: { [weak self] _ in
didComplete = true
self?.ticket = nil
}
)
if !didComplete {
ticket = newTicket
}
}
Everytime I call the method again, it override the AnyCancellable, what happens to the previous one? Does it still complete before being deallocated?
The previous one, if any, is cancelled, because the only reference to the old AnyCancellable
is destroyed and so the AnyCancellable
is destroyed. When an AnyCancellable
is destroyed, it cancels itself (if not already cancelled).
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