【问题标题】:Chaining with flatMap使用 flatMap 链接
【发布时间】:2021-07-29 18:22:22
【问题描述】:

我很难弄清楚如何循环来自我的发布者 1 的 TMDb 结果并将我从键 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{ $0.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($0.results!.map { item in
                return self.fetchVideos(item.id)
                    .map { $0 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 { $0 as Error }
        .map{ $0.data }
        .decode(type: Videos.self, decoder: JSONDecoder())
        .eraseToAnyPublisher()
}

【问题讨论】:

    标签: swift combine


    【解决方案1】:

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

    在我看来,您只需要一个地图 .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{ $0.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{ $0.data }
        .decode(type: TMDb.self, decoder: JSONDecoder())
        .sink(receiveCompletion: { print ($0) },
          receiveValue: { self.movies = $0.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{ $0.data }
        .decode(type: TMDb.self, decoder: JSONDecoder())
        .sink(receiveCompletion: { print ($0) },
          receiveValue: { self.movies = $0.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 { $0 as Error }
        .map{ $0.data }
        .decode(type: Videos.self, decoder: JSONDecoder())
        .sink(receiveCompletion: { print ($0) },
          receiveValue: { [weak self] videos in guard let self = self else {return}
             self.videos.append(videos)
      })
        .store(in: &cancellables)
     }
    

    【讨论】:

    • 感谢您的精彩解释!我必须测试一下,但这看起来比我的代码干净得多。
    • 祝你好运@cole
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2018-01-03
      • 1970-01-01
      • 1970-01-01
      • 2017-04-16
      • 1970-01-01
      • 2013-01-01
      • 1970-01-01
      • 2019-07-13
      • 1970-01-01
      相关资源
      最近更新 更多