I've been using ReactiveSwift for a few months now but there is a something that I dont fully understand: lifetime objects.
For example, lets say I have a SignalProducer
which will make an API call, this is wrapped on a class:
class ServiceWrapped {
private let service: Service // the method called on this object returns the SignalProducer
private let (lifetime, token) = Lifetime.make()
// more stuff
func fetchSomething(completion: @escaping (Value?, Error?) -> Void) {
lifetime += service.fetchSomething()
.startWithResult { result in
switch result {
case .success(let value):
completion(value, nil)
case .failure(let error):
completion(nil, error)
}
}
}
}
My question is: Is it necessary to use lifetime
on this case?
I understood that lifetime
will retain the service call so it has something when it return but since this is also wrapped on ServiceWrapped
I don't think using lifetime
is really necessary.
Thanks in advance.
You're correct that you don't need to keep the result of startWithResult
in order to keep the subscription alive. The relevant part of the documentation says:
A
Signal
must be publicly retained for attaching new observers, but not necessarily for keeping the stream of events alive. Moreover, aSignal
retains itself as long as there is still an active observer.
So as long as you don't dispose the object returned from startWithResult
, the operation will continue even if you don't retain it.
Rather, Lifetime
is about cancelling operations. In this case, because you've attached the result of startWithResult
to ServiceWrapped
's lifetime, the operation will be cancelled when the ServiceWrapped
object is deallocated. If you omit lifetime +=
, then the operation will continue even if ServiceWrapped
is deallocated.
A practical example of why this is useful is if you have a view controller that loads an image from the web. If the user dismisses the view controller before the image is finished loading then you probably want to cancel the web request. You can do that by tying the image load producer to the lifetime of the view controller. It's not about keeping the web request alive, it's about cancelling it when it's no longer necessary.
As an aside about style, the documentation recommends you use operators rather than handling the result of the startWithResult
:
func fetchSomething(completion: @escaping (Value?, Error?) -> Void) {
service.fetchSomething()
.take(during: lifetime)
.startWithResult { result in
switch result {
case .success(let value):
completion(value, nil)
case .failure(let error):
completion(nil, error)
}
}
}