0

I have two lists of URLs that return some links to images. The lists are passed into a future like

static func loadRecentEpisodeImagesFuture(request: [URL]) -> AnyPublisher<[RecentEpisodeImages], Never> { return Future { promise in print(request) networkAPI.recentEpisodeImages(url: request) .sink(receiveCompletion: { _ in }, receiveValue: { recentEpisodeImages in promise(.success(recentEpisodeImages)) }) .store(in: &recentImagesSubscription) } .eraseToAnyPublisher() } 

Which calls:

 /// Get a list of image sizes associated with a featured episode . func featuredEpisodeImages(featuredUrl: [URL]) -> AnyPublisher<[FeaturedEpisodeImages], Error> { let featuredEpisodesImages = featuredUrl.map { (featuredUrl) -> AnyPublisher<FeaturedEpisodeImages, Error> in return URLSession.shared .dataTaskPublisher(for: featuredUrl) .map(\.data) .decode(type: FeaturedEpisodeImages.self, decoder: decoder) .receive(on: networkApiQueue) .catch { _ in Empty<FeaturedEpisodeImages, Error>() } .print("###Featured###") .eraseToAnyPublisher() } return Publishers.MergeMany(featuredEpisodesImages).collect().eraseToAnyPublisher() } /// Get a list of image sizes associated with a recent episode . func recentEpisodeImages(recentUrl: [URL]) -> AnyPublisher<[RecentEpisodeImages], Error> { let recentEpisodesImages = recentUrl.map { (recentUrl) -> AnyPublisher<RecentEpisodeImages, Error> in return URLSession.shared .dataTaskPublisher(for: recentUrl) .map(\.data) .decode(type: RecentEpisodeImages.self, decoder: decoder) .receive(on: networkApiQueue) .catch { _ in Empty<RecentEpisodeImages, Error>() } .print("###Recent###") .eraseToAnyPublisher() } return Publishers.MergeMany(recentEpisodesImages).collect().eraseToAnyPublisher() } 

and is attached to the app state:

/// Takes an action and returns a future mapped to another action. static func recentEpisodeImages(action: RequestRecentEpisodeImages) -> AnyPublisher<Action, Never> { return loadRecentEpisodeImagesFuture(request: action.request) .receive(on: networkApiQueue) .map({ images in ResponseRecentEpisodeImages(response: images) }) .replaceError(with: RequestFailed()) .eraseToAnyPublisher() } 

It seems that:

return Publishers.MergeMany(recentEpisodes).collect().eraseToAnyPublisher() 

doesn't give me a reliable downstream value as whichever response finishes last overwrites the earlier response.

I am able to log the responses of both series of requests. Both are processing the correct arrays and returning the proper json.

I would like something like:

return recentEpisodeImages 

but currently this gives me the error

Cannot convert return expression of type '[AnyPublisher<RecentEpisodeImages, Error>]' to return type 'AnyPublisher<[RecentEpisodeImages], Error>'

How can I collect the values of the inner publisher and return them as

AnyPublisher<[RecentEpisodeImages], Error> 
4
  • Yeah the merge is definitely the problem, the functions take two different arrays of URLs and return two different arrays of URLs. The order they are returned in is irrelevant. I need to replace Publishers.MergeMany() with something that grabs the results of url.map without merging with streams outside its scope. Commented Mar 17, 2021 at 19:14
  • I need the type AnyPublisher<[RecentEpisodeImages], Error> from the input [URL]. The array inside the stream is an array of json that hold links for images. I need to be able to pass this stream on to some other streams and these streams need to stop overwriting each other. Commented Mar 17, 2021 at 20:18
  • They are called by a future is initiated by some redux actions. Those arrays are saved into the network response store. I'll add the future to the question in an edit. Commented Mar 17, 2021 at 20:37
  • How would I map from a pipeline that pops out any array of request urls to one that pops out the response json. I have an [URL]. From what I think I understand about URLSession I am getting back [AnyPublisher<RecentEpisodeImages, Error>] from recentUrl.map but I need need AnyPublisher<[RecentEpisodeImages], Error> Commented Mar 17, 2021 at 21:58

1 Answer 1

2

Presuming that the question is how to turn an array of URLs into an array of what you get when you download and process the data from those URLs, the answer is: turn the array into a sequence publisher, process each URL by way of flatMap, and collect the result.

Here, for instance, is how to turn an array of URLs representing images into an array of the actual images (not identically what you're trying to do, but probably pretty close):

func publisherOfArrayOfImages(urls:[URL]) -> AnyPublisher<[UIImage],Error> { urls.publisher .flatMap { (url:URL) -> AnyPublisher<UIImage,Error> in return URLSession.shared.dataTaskPublisher(for: url) .compactMap { UIImage(data: $0.0) } .mapError { $0 as Error } .eraseToAnyPublisher() }.collect().eraseToAnyPublisher() } 

And here's how to test it:

let urls = [ URL(string:"http://www.apeth.com/pep/moe.jpg")!, URL(string:"http://www.apeth.com/pep/manny.jpg")!, URL(string:"http://www.apeth.com/pep/jack.jpg")!, ] let pub = publisherOfArrayOfImages(urls:urls) pub.sink { print($0) } receiveValue: { print($0) } .store(in: &storage) 

You'll see that what pops out the bottom of the pipeline is an array of three images, corresponding to the array of three URLs we started with.

(Note, please, that the order of the resulting array is random. We fetched the images asynchronously, so the results arrive back at our machine in whatever order they please. There are ways around that problem, but it is not what you asked about.)

Sign up to request clarification or add additional context in comments.

2 Comments

Ok I though by goal you meant explicitly like at the type level. Like big picture this is I have a list of shows, they have as props some links to some different sized images. I am using the show props links to fetch the json interface for fetching different sized images. When the page that needs the episode images loads I am dispatching an action with the list of URLs to fetch as the payload. The action passes the two arrays to two effects which call call two futures with which call the api methods which calls the URL.Session Streams.
@internetdotcom I believe the OP's acceptance of the answer indicates that I do know the answer to the question. But you're quite right that there's old history unnecessarily embedded in the answer; I'll remove that sentence.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.