将URLSession.DataTaskPublisher转换为Future发布者

时间:2020-02-27 07:35:39

标签: ios swift combine

如何在Combine框架中将URLSession.DataTaskPublisher转换为Future。 我认为,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)
    }
}

有没有简单的方法可以做到这一点?

3 个答案:

答案 0 :(得分:4)

据我了解,在RxSwift中使用.asSingle的原因是,当您订阅时,您的订阅者会收到SingleEvent或{{1} }。因此,您的订阅者不必担心会收到.success(value)类型的事件,因为没有一个事件。

没有与Combine中的等效。从用户的角度来看,在Combine中,.error(error)只是另一种.completion,可以发出输出值和FuturePublisher。类型系统不会强制执行.finished从不发出.failure(error)的事实。

因此,没有程序上的原因专门返回Future。您可能会争辩说,返回.finished证明了您总是总是恰好返回一个输出或失败的意图。但这并不会改变您编写订阅者的方式。

此外,由于Combine大量使用泛型,因此只要您想将任何运算符应用于Future,就没有前途了。如果将Future应用于某些Future,则会得到map,并且对于其他所有运算符也是如此。这些类型很快就变得一发不可收拾,并掩盖了底部有Future<V, E>的事实。

如果确实需要,可以实现自己的运算符将任何Map<Future<V, E>, V2>转换为Future。但是您必须决定上游发出Publisher时该怎么做,因为Future无法发出.finished

Future

答案 1 :(得分:2)

不是将数据任务 publisher 转换为Future,而是将 data任务转换为Future。只需将Future包裹在对URLSession的[ERROR] The project com.company.jcompany:persistence-api:5.0.0-SNAPSHOT (/var/lib/jenkins/jobs/jcompany_persistence_api-trunk/workspace/pom.xml) has 1 error [ERROR] Non-resolvable parent POM: Could not transfer artifact com.company.jcompany:persistence:pom:5-SNAPSHOT from/to company-repo-snap (${company.repo.download.endpoint}/libs-snapshots): No connector available to access repository company-repo-snap (${company.repo.download.endpoint}/libs-snapshots) of type default using the available factories WagonRepositoryConnectorFactory and 'parent.relativePath' points at wrong local POM @ line 5, column 10 -> [Help 2] org.apache.maven.model.resolution.UnresolvableModelException: Could not transfer artifact com.company.jcompany:persistence:pom:5-SNAPSHOT from/to company-repo-snap (${company.repo.download.endpoint}/libs-snapshots): No connector available to access repository company-repo-snap (${company.repo.download.endpoint}/libs-snapshots) of type default using the available factories WagonRepositoryConnectorFactory at org.apache.maven.project.ProjectModelResolver.resolveModel(ProjectModelResolver.java:159) at org.apache.maven.model.building.DefaultModelBuilder.readParentExternally(DefaultModelBuilder.java:813) at org.apache.maven.model.building.DefaultModelBuilder.readParent(DefaultModelBuilder.java:664) at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:310) at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:232) 的调用周围,即可解决问题。这正是未来的发展方向:将任何异步操作转变为发布者。

答案 2 :(得分:0)

如何从函数返回未来

您需要将现有发布者转换为 AnyPublisher<Value, Error>,而不是尝试从函数返回“未来”。您可以使用 .eraseToAnyPublisher() 运算符执行此操作。

func getUser() -> AnyPublisher<User, Error> {
    URLSession.shared.dataTaskPublisher(for: request)
        .tryMap { output -> Data in
            // handle response
            return output.data
        }
        .decode(type: User.self, decoder: JSONDecoder())
        .mapError { error in
            // handle error
            return error
        }
        .eraseToAnyPublisher()
}