Swift 合并嵌套发布者
Swift Combine Nested Publishers
我正在尝试结合 Swift 组合一个嵌套的发布者链,但我被难住了。我当前的代码开始在 .flatMap 行抛出错误,我不知道为什么。我一直在努力让它发挥作用,但我没有运气。
我想要完成的是下载 TrailerVideoResult 并对其进行解码,获取 TrailerVideo 对象数组,将其转换为 YouTube 网址数组,然后为每个 YouTube URL 获取 LPLinkMetadata .最终发布者应该 return 一组 LPLinkMetadata 对象。一切正常,直到 LPLinkMetadata 部分。
编辑:我更新了 loadTrailerLinks 函数。我原来忘了删除一些与这个例子无关的部分。
您将需要导入“LinkPresentation”。这是一个 Apple 框架,用于在您的应用程序中获取、提供和呈现丰富的链接。
最后一行 (eraseToAnyPublisher) 出现错误“表达式类型不明确,没有更多上下文”。
func loadTrailerLinks() -> AnyPublisher<[LPLinkMetadata], Error>{
return URLSession.shared.dataTaskPublisher(for: URL(string: "Doesn't matter")!)
.tryMap() { element -> Data in
guard let httpResponse = element.response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return element.data
}
.decode(type: TrailerVideoResult.self, decoder: JSONDecoder(.convertFromSnakeCase))
.compactMap{ [=11=].results }
.map{ trailerVideoArray -> [TrailerVideo] in
let youTubeTrailer = trailerVideoArray.filter({[=11=].site == "YouTube"})
return youTubeTrailer
}
.map({ youTubeTrailer -> [URL] in
return youTubeTrailer.compactMap{
let urlString = "https://www.youtube.com/watch?v=\([=11=].key)"
let url = URL(string: urlString)!
return url
}
})
.flatMap{ urls -> [AnyPublisher<LPLinkMetadata, Never>] in
return urls.map{ url -> AnyPublisher <LPLinkMetadata, Never> in
return self.getMetaData(url: url)
.map{ metadata -> LPLinkMetadata in
return metadata
}
.eraseToAnyPublisher()
}
}
.eraseToAnyPublisher()
}
func fetchMetaData(url: URL) -> AnyPublisher <LPLinkMetadata, Never> {
return Deferred {
Future { promise in
LPMetadataProvider().startFetchingMetadata(for: url) { (metadata, error) in
promise(Result.success(metadata!))
}
}
}.eraseToAnyPublisher()
}
struct TrailerVideoResult: Codable {
let results : [TrailerVideo]
}
struct TrailerVideo: Codable {
let key: String
let site: String
}
您可以为此使用 Publishers.MergeMany
and collect()
:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
func loadTrailerLinks() -> AnyPublisher<[LPLinkMetadata], Error> {
// Download data
URLSession.shared.dataTaskPublisher(for: URL(string: "Doesn't matter")!)
.tryMap() { element -> Data in
guard let httpResponse = element.response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return element.data
}
.decode(type: TrailerVideoResult.self, decoder: decoder)
// Convert the TrailerVideoResult to a MergeMany publisher, which merges the
// [AnyPublisher<LPLinkMetadata, Never>] into a single publisher with output
// type LPLinkMetadata
.flatMap {
Publishers.MergeMany(
[=10=].results
.filter { [=10=].site == "YouTube" }
.compactMap { URL(string: "https://www.youtube.com/watch?v=\([=10=].key)") }
.map(fetchMetaData)
)
// Change the error type from Never to Error
.setFailureType(to: Error.self)
}
// Collect all the LPLinkMetadata and then publish a single result of
// [LPLinkMetadata]
.collect()
.eraseToAnyPublisher()
}
将输入的值数组转换为结果数组有点棘手,每个结果都是通过发布者获得的。
如果顺序不重要,您可以 flatMap
输入 Publishers.Sequence
发布者,然后处理每个值,然后 .collect
它们:
.flatMap { urls in
urls.publisher // returns a Publishers.Sequence<URL, Never> publisher
}
.flatMap { url in
self.getMetaData(url: url) // gets metadata publisher per for each url
}
.collect()
(我假设 getMetaData
returns AnyPublisher<LPLinkMetadata, Never>
)
.collect
将收集所有发出的值,直到上游完成(但每个值可能不是按原始顺序到达)
如果您需要保留订单,还有更多工作要做。您可能需要发送原始索引,然后再对其进行排序。
我正在尝试结合 Swift 组合一个嵌套的发布者链,但我被难住了。我当前的代码开始在 .flatMap 行抛出错误,我不知道为什么。我一直在努力让它发挥作用,但我没有运气。
我想要完成的是下载 TrailerVideoResult 并对其进行解码,获取 TrailerVideo 对象数组,将其转换为 YouTube 网址数组,然后为每个 YouTube URL 获取 LPLinkMetadata .最终发布者应该 return 一组 LPLinkMetadata 对象。一切正常,直到 LPLinkMetadata 部分。
编辑:我更新了 loadTrailerLinks 函数。我原来忘了删除一些与这个例子无关的部分。
您将需要导入“LinkPresentation”。这是一个 Apple 框架,用于在您的应用程序中获取、提供和呈现丰富的链接。
最后一行 (eraseToAnyPublisher) 出现错误“表达式类型不明确,没有更多上下文”。
func loadTrailerLinks() -> AnyPublisher<[LPLinkMetadata], Error>{
return URLSession.shared.dataTaskPublisher(for: URL(string: "Doesn't matter")!)
.tryMap() { element -> Data in
guard let httpResponse = element.response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return element.data
}
.decode(type: TrailerVideoResult.self, decoder: JSONDecoder(.convertFromSnakeCase))
.compactMap{ [=11=].results }
.map{ trailerVideoArray -> [TrailerVideo] in
let youTubeTrailer = trailerVideoArray.filter({[=11=].site == "YouTube"})
return youTubeTrailer
}
.map({ youTubeTrailer -> [URL] in
return youTubeTrailer.compactMap{
let urlString = "https://www.youtube.com/watch?v=\([=11=].key)"
let url = URL(string: urlString)!
return url
}
})
.flatMap{ urls -> [AnyPublisher<LPLinkMetadata, Never>] in
return urls.map{ url -> AnyPublisher <LPLinkMetadata, Never> in
return self.getMetaData(url: url)
.map{ metadata -> LPLinkMetadata in
return metadata
}
.eraseToAnyPublisher()
}
}
.eraseToAnyPublisher()
}
func fetchMetaData(url: URL) -> AnyPublisher <LPLinkMetadata, Never> {
return Deferred {
Future { promise in
LPMetadataProvider().startFetchingMetadata(for: url) { (metadata, error) in
promise(Result.success(metadata!))
}
}
}.eraseToAnyPublisher()
}
struct TrailerVideoResult: Codable {
let results : [TrailerVideo]
}
struct TrailerVideo: Codable {
let key: String
let site: String
}
您可以为此使用 Publishers.MergeMany
and collect()
:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
func loadTrailerLinks() -> AnyPublisher<[LPLinkMetadata], Error> {
// Download data
URLSession.shared.dataTaskPublisher(for: URL(string: "Doesn't matter")!)
.tryMap() { element -> Data in
guard let httpResponse = element.response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return element.data
}
.decode(type: TrailerVideoResult.self, decoder: decoder)
// Convert the TrailerVideoResult to a MergeMany publisher, which merges the
// [AnyPublisher<LPLinkMetadata, Never>] into a single publisher with output
// type LPLinkMetadata
.flatMap {
Publishers.MergeMany(
[=10=].results
.filter { [=10=].site == "YouTube" }
.compactMap { URL(string: "https://www.youtube.com/watch?v=\([=10=].key)") }
.map(fetchMetaData)
)
// Change the error type from Never to Error
.setFailureType(to: Error.self)
}
// Collect all the LPLinkMetadata and then publish a single result of
// [LPLinkMetadata]
.collect()
.eraseToAnyPublisher()
}
将输入的值数组转换为结果数组有点棘手,每个结果都是通过发布者获得的。
如果顺序不重要,您可以 flatMap
输入 Publishers.Sequence
发布者,然后处理每个值,然后 .collect
它们:
.flatMap { urls in
urls.publisher // returns a Publishers.Sequence<URL, Never> publisher
}
.flatMap { url in
self.getMetaData(url: url) // gets metadata publisher per for each url
}
.collect()
(我假设 getMetaData
returns AnyPublisher<LPLinkMetadata, Never>
)
.collect
将收集所有发出的值,直到上游完成(但每个值可能不是按原始顺序到达)
如果您需要保留订单,还有更多工作要做。您可能需要发送原始索引,然后再对其进行排序。