Свифт Комбинат. Как преобразовать ценности издателя - PullRequest
0 голосов
/ 05 октября 2019

Мне нужно скачать файлы, используя предоставленные ссылки из бэкэнда. Для загрузки файлов используется асинхронный API, который возвращает объект Progress (). Проблема в том, что FlatMap не может отображать от Publisher<[Link], Error> до Publisher<[File], Error>. Еще одна проблема, которую я хочу решить, это избавиться от отменяемого и каким-то образом преобразовать информацию о прогрессе filePath, если Progress.fractionCompleted равен 1,0.

Пока я пытался использовать функцию map. См. Код на игровой площадке:

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 голосов
/ 15 ноября 2019

Проблема была решена с помощью расширения Publishers.Sequence () и избежания вызова функции sink (). Также я использовал flatMap () для преобразования выходного значения.

...