gpt4 book ai didi

swift - 有没有办法避免到处使用 AnyPublisher/eraseToAnyPublisher?

转载 作者:行者123 更新时间:2023-12-03 09:17:06 24 4
gpt4 key购买 nike

我只是在学习如何使用Combine。我有使用 Rx(RxSwift 和 RxJava)的经验,我注意到它非常相似。

然而,完全不同的一件事(而且有点烦人)是 Publisher协议(protocol)不为其 Output 使用泛型和 Failure类型;它改为使用关联类型。

这意味着我不能指定多态 Publisher类型(例如 Publisher<Int, Error> )并简单地返回符合 Publisher 的任何类型与那些类型。我需要使用 AnyPublisher<Int, Error>相反,我被迫包含eraseToAnyPublisher()到处都是。

如果这是唯一的选择,那我就忍了。但是,我最近也了解了 Swift 中的不透明类型,我想知道是否可以使用它们来解决这个问题。

有没有办法让我拥有一个返回 some Publisher 的函数?并为 Output 使用特定类型和 Failure ?

这似乎是不透明类型的完美案例,但我不知道是否有办法既使用不透明类型又指定关联类型。

我正在描绘这样的事情:

func createPublisher() -> some Publisher where Output = Int, Failure = Error {
return Just(1)
}

最佳答案

在撰写本文时,Swift 没有您想要的功能。 Joe Groff 在他的 “Improving the UI of generics” document 中标题为“函数返回缺少类型级抽象”的部分中具体描述了缺少的内容。 :

However, it's common to want to abstract a return type chosen by theimplementation from the caller. For instance, a function may producea collection, but not want to reveal the details of exactly what kindof collection it is. This may be because the implementer wants toreserve the right to change the collection type in future versions, orbecause the implementation uses composed lazy transforms and doesn'twant to expose a long, brittle, confusing return type in itsinterface. At first, one might try to use an existential in thissituation:

func evenValues<C: Collection>(in collection: C) -> Collection where C.Element == Int {
return collection.lazy.filter { $0 % 2 == 0 }
}

but Swift will tell you today that Collection can only be used as ageneric constraint, leading someone to naturally try this instead:

func evenValues<C: Collection, Output: Collection>(in collection: C) -> Output
where C.Element == Int, Output.Element == Int
{
return collection.lazy.filter { $0 % 2 == 0 }
}

but this doesn't work either, because as noted above, the Outputgeneric argument is chosen by the caller—this function signature isclaiming to be able to return any kind of collection the caller asksfor, instead of one specific kind of collection used by theimplementation.


不透明的返回类型语法( some Publisher )有可能有一天会被扩展以支持这种使用。
今天你有三个选择。为了理解它们,让我们考虑一个具体的例子。假设您想从 URL 获取整数的文本列表,每行一个,并将每个整数发布为单独的输出:
return dataTaskPublisher(for: url)
.mapError { $0 as Error }
.flatMap { data, response in
(response as? HTTPURLResponse)?.statusCode == 200
? Result.success(data).publisher
: Result.failure(URLError(.resourceUnavailable)).publisher
}
.compactMap { String(data: $0, encoding: .utf8) }
.map { data in
data
.split(separator: "\n")
.compactMap { Int($0) }
}
.flatMap { $0.publisher.mapError { $0 as Error } }

选项 1:说明返回类型
您可以使用完整的、复杂的返回类型。它看起来像这样:
extension URLSession {
func ints(from url: URL) -> Publishers.FlatMap<
Publishers.MapError<
Publishers.Sequence<[Int], Never>,
Error
>,
Publishers.CompactMap<
Publishers.FlatMap<
Result<Data, Error>.Publisher,
Publishers.MapError<
URLSession.DataTaskPublisher,
Error
>
>,
[Int]
>
> {
return dataTaskPublisher(for: url)
... blah blah blah ...
.flatMap { $0.publisher.mapError { $0 as Error } }
}
}
我自己没有弄清楚返回类型。我将返回类型设置为 Int然后编译器告诉我 Int不是正确的返回类型,并且错误消息包含正确的返回类型。这并不漂亮,如果您更改实现,您将不得不找出新的返回类型。
选项 2:使用 AnyPublisher添加 .eraseToAnyPublisher()到出版商的末尾:
extension URLSession {
func ints(from url: URL) -> AnyPublisher<Int, Error> {
return dataTaskPublisher(for: url)
... blah blah blah ...
.flatMap { $0.publisher.mapError { $0 as Error } }
.eraseToAnyPublisher()
}
}
这是常见且简单的解决方案,通常也是您想要的。如果您不喜欢拼写 eraseToAnyPublisher ,你可以自己写 Publisher扩展名以使用较短的名称执行此操作,如下所示:
extension Publisher {
var typeErased: AnyPublisher<Output, Failure> { eraseToAnyPublisher() }
}
选项 3:编写自己的 Publisher类型
您可以用自己的类型包装您的发布者。您的类型的 receive(subscriber:)构造“真正的”发布者,然后将订阅者传递给它,如下所示:
extension URLSession {
func ints(from url: URL) -> IntListPublisher {
return .init(session: self, url: url)
}
}

struct IntListPublisher: Publisher {
typealias Output = Int
typealias Failure = Error

let session: URLSession
let url: URL

func receive<S: Subscriber>(subscriber: S) where
S.Failure == Self.Failure, S.Input == Self.Output
{
session.dataTaskPublisher(for: url)
.flatMap { $0.publisher.mapError { $0 as Error } }
... blah blah blah ...
.subscribe(subscriber)
}
}

关于swift - 有没有办法避免到处使用 AnyPublisher/eraseToAnyPublisher?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61553264/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com