Chaining swift menggabungkan penerbit dan menerima setiap hasil
Pada contoh di bawah, saya membuat permintaan jaringan untuk memuat genre film yang berbeda, lalu menggunakannya untuk memuat semua film. Wastafel hanya mengembalikan hasil film. Bagaimana saya bisa menerima genre dan 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)
}
}
Jawaban
Tergantung bagaimana Anda ingin mengumpulkan genre dan film.
Misalnya, apakah Anda menginginkan genre dan daftar film dalam genre tersebut? Hasilnya bisa berupa array (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()
Atau, jika Anda menginginkan serangkaian (Genre, Movie)
tupel, maka itu pendekatan yang serupa, tetapi dengan tingkat tambahan .flatMap
untuk mendapatkan film individual
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()
Untuk menjawab pertanyaan komentar Anda, Anda ingin mengembalikan yang telah diperbarui Genre
, Anda dapat mengembalikannya daripada mengembalikan tupel. Ingatlah bahwa karena Genre
adalah struct, Anda perlu membuat salinan variabel dari objek (yang genre
tersedia di flatMap
closure adalah konstanta), perbarui salinannya, dan kembalikan:
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()