Создание издателя комбината, например RxSwift's Observable.Create для запроса Alamofire - PullRequest
0 голосов
/ 17 января 2020

Я использую следующий фрагмент кода для генерации холодного RxSwift Observable:

func doRequest<T :Mappable>(request:URLRequestConvertible) -> Observable<T> {
        let observable = Observable<T>.create { [weak self] observer in
        guard let self = self else { return Disposables.create() }
        self.session.request(request).validate().responseObject { (response: AFDataResponse<T>) in
            switch response.result {
                case .success(let obj):
                    observer.onNext(obj)
                    observer.onCompleted()
                case .failure(let error):
                    let theError = error as Error
                    observer.onError(theError)
            }
        }
         return Disposables.create()
    }
    return observable
}

, где Mappable - это тип ObjectMapper , а self.session - Alamofire's Session object.

Я не могу найти эквивалент Observable.create {...} в Apple Combine framework. Я нашел только URLSession.shared.dataTaskPublisher(for:), который создает издателя с использованием класса URLSession от Apple.

Как я могу преобразовать вышеприведенное, наблюдаемое в издателя комбината Alamofire?

EDIT : используя решение, предоставленное rob, я получил следующее:

 private let apiQueue = DispatchQueue(label: "API", qos: .default, attributes: .concurrent)

  func doRequest<T>(request: URLRequestConvertible) -> AnyPublisher<T, AFError> where T : Mappable {

       Deferred { [weak self] () -> Future<T, AFError> in

          guard let self = self else {
              return Future<T, AFError> { promise in  
promise(.failure(.explicitlyCancelled))  }
        }

          return Future { promise in
            self.session
            .request(request)
            .validate()
            .responseObject { (response: AFDataResponse<T>) in
                promise(response.result)
            }
        }
    }
    .handleEvents(receiveCompletion: { completion in
        if case .failure (let error) = completion {
                //handle the error
        }
    })
    .receive(on: self.apiQueue)
    .eraseToAnyPublisher()
}

EDIT2: Мне нужно удалить личную очередь, так как она не нужна, Alamofire выполняет анализ самостоятельное декодирование, поэтому удалите очередь и ее использование (.receive(on: self.apiQueue))

1 Ответ

5 голосов
/ 18 января 2020

Вы можете использовать Future для подключения обратного вызова responseObject к комбайну Publisher. У меня нет Alamofire для тестирования, но я думаю, что должно работать следующее:

func doRequest<T: Mappable>(request: URLRequestConvertible) -> AnyPublisher<T, AFError> {
    return Future { promise in
        self.session
            .request(request)
            .validate()
            .responseObject { (response: AFDataResponse<T>) in
            promise(response.result)
        }
    }.eraseToAnyPublisher()
}

Обратите внимание, что это несколько проще, чем версия RxSwift, потому что promise принимает Result напрямую, поэтому мы не нужно переключаться response.result.

A Future является своего рода «теплым» издателем. Это похоже на горячую наблюдаемость, потому что он выполняет свое тело немедленно и только один раз, поэтому он немедленно запускает запрос Alamofire. Это также похоже на наблюдаемую холодность, потому что каждый подписчик в конечном итоге получает значение или ошибку (при условии, что вы в конечном итоге вызовете promise). Future выполняет свое тело только один раз, но кэширует Result, который вы передаете promise.

Вы можете создать действительно холодного издателя, заключив Future в Deferred:

func doRequest<T: Mappable>(request: URLRequestConvertible) -> AnyPublisher<T, AFError> {
    return Deferred {
        Future { promise in
            self.session
                .request(request)
                .validate()
                .responseObject { (response: AFDataResponse<T>) in
                    promise(response.result) }
        }
    }.eraseToAnyPublisher()
}

Deferred вызывает его тело для создания нового внутреннего Publisher каждый раз, когда вы подписываетесь на него. Поэтому каждый раз, когда вы подписываетесь, вы создаете новый Future, который немедленно запускает новый запрос Alamofire. Это полезно, если вы хотите использовать оператор retry, как в этот вопрос .

...