Concatenare rapidamente gli editori di combinare e ricevere ogni risultato

Aug 18 2020

Nell'esempio seguente, sto effettuando una richiesta di rete per caricare diversi generi di film, quindi lo utilizzo per caricare tutti i film. Il lavandino restituisce solo i risultati del film. Come potrei ricevere sia i generi che i film?

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)
    }
}

Risposte

2 NewDev Aug 18 2020 at 17:03

Dipende da come vuoi raccogliere generi e film.

Ad esempio, vuoi un genere e un elenco di film in quel genere? Il risultato potrebbe essere un array di (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()

Oppure, se desideri un array di (Genre, Movie)tuple, è un approccio simile, ma con un livello aggiuntivo .flatMapper ottenere singoli film

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()

Per rispondere alla tua domanda di commento, vuoi restituire l'aggiornamento Genre, potresti restituirlo invece di restituire una tupla. Tieni presente che poiché Genreè una struttura, dovresti creare una copia variabile dell'oggetto (quella genredisponibile nella flatMapchiusura è una costante), aggiornare la copia e restituirla:

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()