我需要使用后端提供的链接下载文件。为了下载文件,使用了异步API,该API返回Progress()对象。问题在于FlatMap无法从Publisher<[Link], Error>
映射到Publisher<[File], Error>
。我要解决的另一个问题是,一旦Progress.fractionCompleted等于1.0,就取消可取消的并且以某种方式转换Progress info filePath。
目前,我尝试使用map
函数。请参阅Playground上的代码:
import UIKit
import Combine
var progress = Progress()
extension ProgressUserInfoKey {
public static var destinationURL = ProgressUserInfoKey("destinationURL")
}
func download(from urlRequest: URLRequest, to destinationURL: URL) -> AnyPublisher<Progress, Error> {
return Future<Progress, Error> { promise in
progress = Progress(totalUnitCount: 1)
progress.setUserInfoObject(destinationURL.absoluteString,
forKey: ProgressUserInfoKey.destinationURL)
promise(.success(progress))
// Simulate async API
DispatchQueue.main.async {
progress.completedUnitCount = 1
}
}.eraseToAnyPublisher()
}
struct Link: Decodable {
let url: String
}
func getLinks() -> AnyPublisher<[Link], Error> {
return URLSession.shared.dataTaskPublisher(for: URL(string: "https://backend.com")!)
.map { $0.data }
.decode(type: [Link].self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
struct File {
let url: URL
let size: UInt32
}
private func destinationUrl(_ fromUrl: String?) -> URL {
guard let path = fromUrl else {
return URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString)
}
return URL(fileURLWithPath: path)
}
/// 2) How to get rid of this state and transoft Progress into filePath directly (using matp(transform: ? )
var cancellableSet = Set<AnyCancellable>()
func getFiles() -> AnyPublisher<[File], Error> {
getLinks()
.flatMap { (links) -> AnyPublisher<[File], Error> in
let sequence = Sequence<[AnyPublisher<File, Error>], Error>(sequence: links.map {
download(from: URLRequest(url: $0), to: destinationUrl(UUID().uuidString))
.sink { progress in
progress.publisher(for: \.fractionCompleted).sink { progressValue in
if progressValue == 1.0 {
let filePath: String = progress.userInfo[ProgressUserInfoKey.destinationURL] as? String ?? ""
/// 1) How to return publisher here
///return Publisher(File(url: URL(string: filePath)!, size: 0))
}
}
.store(in: &cancellableSet)
}
.store(in: &cancellableSet)
} )
return sequence.flatMap { $0 }.collect().eraseToAnyPublisher()
}
}
我希望代码能够成功编译,并且函数getFiles
返回AnyPublisher<[File], Error>
。
当前错误代码如下:
Cannot convert return expression of type 'Publishers.FlatMap<AnyPublisher<[File], Error>, AnyPublisher<[Link], Error>>' to return type 'AnyPublisher<[File], Error>'
答案 0 :(得分:0)
问题已通过使用Publishers.Sequence()扩展名得以解决,并避免了调用函数sink()。另外,我还使用了flatMap()来转换输出值。可以在以下位置找到演示这种方式的代码:Combine publishers together