Sniro
Sniro

Reputation: 1

Combine Future publisher is leaking

Hi So i am trying to wrap an async network request with Combine's Future/Promise.

my code goes something like that:

enum State {
    case initial
    case loading
    case success(movies: [Movie])
    case error(message: String)
}

protocol ViewModelProtocol {

    var statePublisher: AnyPublisher<State, Never> { get }

    func load(genreId: String)
}

class ViewModel: ViewModelProtocol {

   var remoteDataSource = RemoteDataSource()

   @Published state: State = .initial
   var statePublisher: AnyPublisher<State, Never> { $state.eraseToAnyPubliher() }

   public func load(genreId: String) {
        self.state = .loading
        self.getMovies(for: genreId)
            .sink { [weak self] (moveis) in
                guard let self = self else { return }
                if let movies = movies {
                    self.state = .success(movies: movies)
                } else {
                    self.state = .error(message: "failed to load movies")
                }
            }
   }


func getMovies(for genreId: String) -> AnyPublisher<[Movie]?, Never> {
        Future {  promise in
            self.remoteDataSource.getMovies(for: genreId) { (result) in
                switch result {
                case .success(let movies): promise(.success(movies))
                case .failure: promise(.success(nil))
                }
            }
        }.eraseToAnyPublisher()
   }
}

I was trying to see if there are any memory leaks and found that there is a reference to the Future that is not being deallocated same as here: Combine Future Publisher is not getting deallocated

Upvotes: 0

Views: 521

Answers (1)

Josh Homann
Josh Homann

Reputation: 16327

You are strongly capturing self inside of your escaping Future init (just capture remoteDataSource). It doesn't seem to me like this should cause a memory leak. As the link you put in the question suggests Future does not behave like most other publishers; it does work as soon as you create it and not when you subscribe. It also memoizes and shares its results. I strongly suggest that you do no use this behind an AnyPublisher because its very non obvious to the caller that this thing is backed by a Future and that it will start work immediately. I would use URLSession.shared.dataTaskPublisher instead and then you get a regular publisher and you don't need the completion handler or the Future. Otherwise wrap the Future in a Deferred so you don't get the eager evaluation.

Upvotes: 2

Related Questions