Преобразовать URLSession.DataTaskPublisher в будущего издателя - PullRequest
0 голосов
/ 27 февраля 2020

Как конвертировать URLSession.DataTaskPublisher в Future в Combine framework. На мой взгляд, издатель Future здесь более уместен, потому что вызов может выдать только один ответ и в конечном итоге завершится неудачей.

В RxSwift есть вспомогательный метод, такой как asSingle.

Я достиг этого преобразование с использованием следующего подхода, но не знаю, является ли это лучшим методом.

        return Future<ResponseType, Error>.init { (observer) in
        self.urlSession.dataTaskPublisher(for: urlRequest)
            .tryMap { (object) -> Data in
            //......
            }
            .receive(on: RunLoop.main)
            .sink(receiveCompletion: { (completion) in
                if case let .failure(error) = completion {
                    observer(.failure(error))
                }
            }) { (response) in
                observer(.success(response))
            }.store(in: &self.cancellable)
    }
}

Есть ли простой способ сделать это?

Ответы [ 2 ]

2 голосов
/ 28 февраля 2020

Насколько я понимаю, причина использования .asSingle в RxSwift заключается в том, что когда вы подписываетесь, ваш подписчик получает SingleEvent, который либо .success(value), либо .error(error). Таким образом, вашему подписчику не нужно беспокоиться о получении события типа .completion, потому что его нет.

Этого не существует в Combine. В Combine, с точки зрения подписчика, Future - это просто еще один тип Publisher, который может выдавать выходные значения и .finished или .failure(error). Система типов не навязывает тот факт, что Future никогда не испускает .finished.

. Из-за этого не существует программной c причины для конкретного возврата Future. Вы можете утверждать, что при возврате Future документируется ваше намерение всегда возвращать либо только один вывод, либо ошибку. Но это не меняет способ написания вашего подписчика.

Более того, из-за интенсивного использования обобщений Combine, как только вы захотите применить какой-либо оператор к Future, у вас нет будущее больше. Если вы примените map к некоторому Future<V, E>, вы получите Map<Future<V, E>, V2> и аналогичное значение для любого другого оператора. Типы быстро выходят из-под контроля и скрывают тот факт, что внизу есть Future.

Если вы действительно хотите, вы можете реализовать свой собственный оператор для преобразования любого Publisher в Future , Но вам придется решить, что делать, если восходящий поток испускает .finished, поскольку Future не может испускать .finished.

extension Publisher {
    func asFuture() -> Future<Output, Failure> {
        return Future { promise in
            var ticket: AnyCancellable? = nil
            ticket = self.sink(
                receiveCompletion: {
                    ticket?.cancel()
                    ticket = nil
                    switch $0 {
                    case .failure(let error):
                        promise(.failure(error))
                    case .finished:
                        // WHAT DO WE DO HERE???
                        fatalError()
                    }
            },
                receiveValue: {
                    ticket?.cancel()
                    ticket = nil
                    promise(.success($0))
            })
        }
    }
}
0 голосов
/ 28 февраля 2020

Вместо преобразования задачи данных издатель в будущее конвертируйте задачу данных в будущее. Просто оберните Future вокруг вызова URLSession dataTask(...){...}.resume(), и проблема решена. Это как раз то, за чем будущее: превратить любую асинхронную операцию в издателя.

...