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