Search code examples
swiftreactive-swift

Lifetime purpose on ReactiveSwift


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.


Solution

  • 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, a Signal 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)
                }
        }
    }