迅速结合。如何转变出版商价值

时间:2019-10-05 19:59:17

标签: swift combine

我需要使用后端提供的链接下载文件。为了下载文件,使用了异步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>'

1 个答案:

答案 0 :(得分:0)

问题已通过使用Publishers.Sequence()扩展名得以解决,并避免了调用函数sink()。另外,我还使用了flatMap()来转换输出值。可以在以下位置找到演示这种方式的代码:Combine publishers together