Glenn Posadas
Glenn Posadas

Reputation: 13283

Swift combine Convert Publisher type

I'm exploring Combine Swift with this project https://github.com/sgl0v/TMDB and I'm trying to replace its imageLoader with something that supports Combine: https://github.com/JanGorman/MapleBacon

The project has a function that returns the type AnyPublisher<UIImage?, Never>. But the imageLoader MapleBacon library returns the type AnyPublisher<UIImage, Error>.

So I'm trying to convert types with this function:

func convert(_ loader: AnyPublisher<UIImage, Error>) -> AnyPublisher<UIImage?, Never> {
    // here.
}

I actually found a question that is kinda similar to mine, but the answers weren't helpful: https://stackoverflow.com/a/58234908/3231194


What I've tried to so far (Matt's answer to the linked question).

The sample project has this function:

func loadImage(for movie: Movie, size: ImageSize) -> AnyPublisher<UIImage?, Never> {
        return Deferred { return Just(movie.poster) }
            .flatMap({ poster -> AnyPublisher<UIImage?, Never> in
                guard let poster = movie.poster else { return .just(nil) }
                let url = size.url.appendingPathComponent(poster)
                let a = MapleBacon.shared.image(with: url)
                    .replaceError(with: UIImage(named: "")!) // <----
            })
            .subscribe(on: Scheduler.backgroundWorkScheduler)
            .receive(on: Scheduler.mainScheduler)
            .share()
            .eraseToAnyPublisher()
    }

if I do replaceError,

I get the type Publishers.ReplaceError<AnyPublisher<UIImage, Error>>


BUT, I was able to solve this one, by extending the library.

extension MapleBacon {
    public func image(with url: URL, imageTransformer: ImageTransforming? = nil) -> AnyPublisher<UIImage?, Never> {
      Future { resolve in
        self.image(with: url, imageTransformer: imageTransformer) { result in
          switch result {
          case .success(let image):
            resolve(.success(image))
          case .failure:
            resolve(.success(UIImage(named: "")))
          }
        }
      }
      .eraseToAnyPublisher()
    }
}

Upvotes: 1

Views: 3853

Answers (1)

Sweeper
Sweeper

Reputation: 270790

First, you need to map a UIImage to a UIImage?. The sensible way to do this is of course to wrap each element in an optional.

Then, you try to turn a publisher that sometimes produces errors to a publisher that Never produces errors. You replaceError(with:) an element of your choice. What element should you replace errors with? The natural answer, since your publisher now publishes optional images, is nil! Of course, assertNoFailure works syntactically too, but you might be downloading an image here, so errors are very likely to happen...

Finally, we need to turn this into an AnyPublisher by doing eraseToAnyPublisher

MapleBacon.shared.image(with: url)
    .map(Optional.some)
    .replaceError(with: nil)
    .eraseToAnyPublisher()

Upvotes: 7

Related Questions