Encadenando editores de cosechadora rápida y recibiendo cada resultado

Aug 18 2020

En el siguiente ejemplo, estoy haciendo una solicitud de red para cargar diferentes géneros de películas y luego lo uso para cargar todas las películas. El fregadero solo devuelve los resultados de la película. ¿Cómo puedo recibir tanto los géneros como las películas?

struct Genre: Codable, Identifiable{
    let id: Int
    let name: String
    var movies: [Movie]?
}

struct Movie: Codable, Hashable, Identifiable {
    let title: String
    let id: Int
    let posterPath: String?
    let backdropPath : String?
    var tagline: String?
}

loadGenres() is AnyPublisher<[Genre], Error> 
fetchMoviesIn() is AnyPublisher<[Movie], Error>

class GenresViewModel: ObservableObject{
    @Published var genres = [Genre]()
    @Published var movies = [Movie]()
    var requests = Set<AnyCancellable>()
    
    init(){
        NetworkManager.shared.loadGenres()
            .flatMap{ genres in
                genres.publisher.flatMap{ genre in
                    NetworkManager.shared.fetchMoviesIn(genre)
                }
            }
            .collect()
            .retry(1)
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { completion in
                switch completion{
                case .finished:
                    print("Finished loading all movies in every genre")
                case .failure(let error):
                    print("Error: \(error)")
                }
            }, receiveValue: { [self] values in
                let allMovies = values.joined()
                self.movies = allMovies.map{$0}
            })
            .store(in: &self.requests)
    }
}

Respuestas

2 NewDev Aug 18 2020 at 17:03

Depende de cómo quieras recopilar géneros y películas.

Por ejemplo, ¿quieres un género y una lista de películas de ese género? El resultado podría ser una matriz de (Genre, [Movies]).

NetworkManager.shared.loadGenres()
   .flatMap { genres in
       genres.publisher.setFailureType(to: Error.self)
   }
   .flatMap { genre in
       NetworkManager.shared.fetchMoviesIn(genre)
          .map { movies in (genre, movies) } 
   }
   .collect()

O, si desea una variedad de (Genre, Movie)tuplas, entonces es un enfoque similar, pero con un nivel adicional .flatMappara obtener películas individuales.

NetworkManager.shared.loadGenres()
   .flatMap { genres in
       genres.publisher.setFailureType(to: Error.self)
   }
   .flatMap { genre in
       NetworkManager.shared.fetchMoviesIn(genre)
          .flatMap { movies in
              movies.publisher.setFailureType(to: Error.self)
          }
          .map { movie in (genre, movie) }
   }
   .collect()

Para responder a la pregunta de su comentario, desea devolver la actualización Genre, puede devolverla en lugar de devolver una tupla. Tenga en cuenta que, dado que Genrees una estructura, deberá crear una copia variable del objeto (la genredisponible en el flatMapcierre es una constante), actualizar la copia y devolver eso:

NetworkManager.shared.loadGenres()
   .flatMap { genres in
       genres.publisher.setFailureType(to: Error.self)
   }
   .flatMap { genre in
       NetworkManager.shared.fetchMoviesIn(genre)
          .map { movies -> Genre in
             var genreCopy = genre
             genreCopy.movies = movies
             return genreCopy
          }
   }
   .collect()