Hızlı zincirleme yayıncıları birleştirir ve her sonucu alır

Aug 18 2020

Aşağıdaki örnekte, farklı film türlerini yüklemek için bir ağ isteğinde bulunuyorum, ardından bunu tüm filmleri yüklemek için kullanıyorum. Lavabo yalnızca film sonuçlarını döndürür. Hem türleri hem de filmleri nasıl alabilirim?

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

Yanıtlar

2 NewDev Aug 18 2020 at 17:03

Türleri ve filmleri nasıl toplamak istediğinize bağlıdır.

Örneğin, bir tür ve bu türdeki filmlerin bir listesini ister misiniz? Sonuç bir dizi olabilir (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()

Veya, bir dizi (Genre, Movie)tuple istiyorsanız , bu benzer bir yaklaşımdır, ancak .flatMapayrı ayrı filmler elde etmek için ek bir seviye vardır.

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

Yorum sorunuzu cevaplamak için, güncellenmiş Genreolanı iade etmek istersiniz , bir demet döndürmek yerine onu iade edebilirsiniz. Çünkü akılda Ayı Genreolan bir yapıdır, nesnenin bir değişken kopyasını oluşturmak gerekir ( genremevcut flatMap, kopyalama güncellemek ve dönüş o kapanması bir sabittir):

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