使用 flatMap 链接

Chaining with flatMap

我很难弄清楚如何循环 TMDb 来自我的发布者 1 的结果,并将我从密钥 id 获得的值用于我的第二个发布者。它没有遍历每个结果,它确实在我的 func fetchVideos(_ id: Int) 中获得了正确的 URL,但它没有从 TMDb 的结果数组中调用每个 URL。我不确定是不是因为我也从 Videos Codable 数据中得到了一组结果?

我试图在我的第一个发布者 flatMap 中使用 Publishers.MergeMany。我仍然绝对处于联合收割机的新手水平,任何提示都会有所帮助。我正在尝试获取电影列表,然后从电影中获取 id 键,然后使用它来获取每部电影的电影预告片数据。

打印输出

https://api.themoviedb.org/3/movie/602223/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/459151/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/385128/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/522478/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/637693/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/529203/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/578701/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/631843/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/645856/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/581644/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/436969/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/568620/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/522931/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/681260/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/630586/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/671/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/618416/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/646207/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/550988/videos?api_key=API_KEY&language=en-US
https://api.themoviedb.org/3/movie/482373/videos?api_key=API_KEY&language=en-US


Videos(id: 459151, results: [themoviedb_demo.Video(id: 3F24D610-4261-4AEB-8906-A9D0E5FE8E4D, iso639_1: "en", iso3166_1: "US", key: "CK6xdYIsaa0", name: "DreamWorks\' The Boss Baby: Family Business | Official Trailer #3 | Peacock", site: "YouTube", size: 1080, type: "Trailer"), themoviedb_demo.Video(id: 417EF49C-B983-4CC3-B435-7A902DECE917, iso639_1: "en", iso3166_1: "US", key: "-rF2j6K5FoM", name: "The Boss Baby 2: Family Business – Official Trailer 2 (Universal Pictures) HD", site: "YouTube", size: 1080, type: "Trailer"), themoviedb_demo.Video(id: C34A3F5F-9429-4267-86F0-5506EF3E8281, iso639_1: "en", iso3166_1: "US", key: "QPzy8Ckza08", name: "THE BOSS BABY: FAMILY BUSINESS | Official Trailer", site: "YouTube", size: 1080, type: "Trailer")])

可编码数据

struct Videos: Codable {
    let id: Int
    let results: [Video]
}

struct Video: Codable {
    let id = Int
    let key: String
    let name: String

    enum CodingKeys: String, CodingKey {
        case id
        case key, name
    }
}


struct TMDb: Codable {
    let results: [Results]?
}

struct Results: Codable {
    let id: Int
    let releaseDate, title: String?
    let name: String?

    enum CodingKeys: String, CodingKey {
        case id
        case releaseDate = "release_date"
        case title
        case name
    }
}

@Published var movies = TMDb(results: Array(repeating: Results(id: 1, releaseDate: "", title: "", name: "") , count: 5))
@Published var videos = Videos(id: 1, results: Array(repeating: Video(id: 1, key: "", name: "") , count: 5))

func getUpcoming() {
    var request = URLRequest(url:URL(string:"https://api.themoviedb.org/3/movie/upcoming?api_key=API_KEY&language=en-US&page=1")!)
    request.httpMethod = "GET"
    let publisher = URLSession.shared.dataTaskPublisher(for: request)
        .map{ [=12=].data }
        .decode(type: TMDb.self, decoder: JSONDecoder())
    let publisher2 = publisher
        .flatMap{
            // loop results from TMDb for id for publisher 2, only one is called
            Publishers.MergeMany([=12=].results!.map { item in
                return self.fetchVideos(item.id)
                    .map { [=12=] as Videos }
                    .replaceError(with: nil)
            })
        }
    // Publishers.CombineLatest
    Publishers.Zip(publisher, publisher2)
        .receive(on:  DispatchQueue.main)
        .sink(receiveCompletion: {_ in
        }, receiveValue: { movies, videos in
            self.movies = movies
            self.videos = videos
        }).store(in: &cancellables)
}

func fetchVideos(_ id: Int) -> AnyPublisher<Videos, Error> {
    let url = URL(string: "https://api.themoviedb.org/3/movie/\(id)/videos?api_key=API_KEY&language=en-US")!
    return URLSession.shared.dataTaskPublisher(for: url)
        .mapError { [=12=] as Error }
        .map{ [=12=].data }
        .decode(type: Videos.self, decoder: JSONDecoder())
        .eraseToAnyPublisher()
}

解决了我自己的问题。我需要为我的 @Published 变量而不是单个项目创建一个数组。然后我需要在我的出版商的 flatMap 中调用 .collect().append(),这将附加到我的 @Published 变量 videos.

你好@cole 对于此操作你不需要合并或压缩,因为你没有订阅两个发布者,你正在尝试在你的第一个发布者发出事件后执行操作。

为此,我认为您只需要一个 map .handleEvents。

所以让我们尝试增强您的代码,我们想分别更新电影和视频,但我们仍然需要视频依赖于电影

首先我们将创建发布者来请求电影:

var request = URLRequest(url:URL(string:"https://api.themoviedb.org/3/movie/upcoming?api_key=API_KEY&language=en-US&page=1")!)
request.httpMethod = "GET"
let publisher = URLSession.shared.dataTaskPublisher(for: request)
    .map{ [=10=].data }
    .decode(type: TMDb.self, decoder: JSONDecoder())

现在我们通过分配电影来增强此发布者的处理能力:

var request = URLRequest(url:URL(string:"https://api.themoviedb.org/3/movie/upcoming?api_key=API_KEY&language=en-US&page=1")!)
request.httpMethod = "GET"
let publisher = URLSession.shared.dataTaskPublisher(for: request)
    .map{ [=11=].data }
    .decode(type: TMDb.self, decoder: JSONDecoder())
    .sink(receiveCompletion: { print ([=11=]) },
      receiveValue: { self.movies = [=11=].results })

现在我们将添加 .handleEvent 以遍历我们的电影以创建所有发布视频事件并为视频数组附加视频的发布者:

var request = URLRequest(url:URL(string:"https://api.themoviedb.org/3/movie/upcoming?api_key=API_KEY&language=en-US&page=1")!)
request.httpMethod = "GET"
let publisher = URLSession.shared.dataTaskPublisher(for: request)
    .map{ [=12=].data }
    .decode(type: TMDb.self, decoder: JSONDecoder())
    .sink(receiveCompletion: { print ([=12=]) },
      receiveValue: { self.movies = [=12=].results })
    .handleEvents(receiveSubscription:nil, receiveOutput: { [weak self] movies in guard let self = self else {return} 
    self.videos = [Videos]()
    for movie in movies.results {
          self.fetchVideos(movie.id)
    }, receiveCompletion:nil, receiveCancel:nil, receiveRequest:nil)
})
     .store(in: &cancellables)

现在进行最后一步,让我们相应地更新 fetchVideos:

func fetchVideos(_ id: Int) {
let url = URL(string: "https://api.themoviedb.org/3/movie/\(id)/videos?api_key=API_KEY&language=en-US")!
return URLSession.shared.dataTaskPublisher(for: url)
    .mapError { [=13=] as Error }
    .map{ [=13=].data }
    .decode(type: Videos.self, decoder: JSONDecoder())
    .sink(receiveCompletion: { print ([=13=]) },
      receiveValue: { [weak self] videos in guard let self = self else {return}
         self.videos.append(videos)
  })
    .store(in: &cancellables)
 }