使用 Swift 和 Combine 链接 + 压缩多个网络请求
Posted
技术标签:
【中文标题】使用 Swift 和 Combine 链接 + 压缩多个网络请求【英文标题】:Chaining + zipping multiple network requests using Swift and Combine 【发布时间】:2021-10-23 02:47:17 【问题描述】:总体用例是 4 个网络请求
A.请求 1 和 2 需要并行并等待两者 完成 乙。请求 3 在 1 和 2 完成后发生 C.请求 4 3 次完成后发生C 之后的最终输出应该是AnyPublisher
类型
我正在尝试使用 Combine 来实现上述目标,到目前为止,我可以使用 Publishers.Zip
执行 A 和使用 flatMap
执行 C。我正在苦苦挣扎的是 B。我可以使用嵌套的完成处理程序来做到这一点,但不能使用 flatMap
这是带有字符串的示例代码(不是实际代码)
func overallFunc(arg1: "arg1", arg2: "arg2", arg3: "arg3", arg4: "arg4" ) -> AnyPublisher<String?, Error>
let pub1 = func1(arg1: arg1, arg2: arg2)
let pub2 = func2(arg1: arg3, arg2: arg4)
let combinedPub = Publishers.Zip(pub1, pub2)
combinedPub
.flatMap (response1, response2) in
return func3(arg1: response1.attribute1, arg2: response2.attribute2)
func1
、func2
和 func3
都返回 URLSession.shared.dataTaskPublisher
,返回类型为 AnyPublisher<String?, Error>
现在我正在努力完成overallFunc
的代码。编译器在flatMap
周围给出以下错误。
Type of expression is ambiguous without more context
如果我在overallFunc
的最后一行添加一个额外的返回值,那么错误将变为
No 'flatMap' candidates produce the expected contextual result type 'AnyPublisher<String?, Error>'
简而言之,我想在 Publishers.Zip
的结果上使用 flatMap
并返回另一个发布者,以便我可以添加另一个 flatMap
来执行第四个请求,但我无法找出正确的语法和顺序要做的事情。
【问题讨论】:
尝试显式添加flatMap
闭包参数的类型和返回类型,编译器应该会给你一个更好的错误信息。
我很确定您的返回类型不匹配。但是,查看所有方法的方法签名(即:func1/2/3)会有所帮助。您应该发布一个完整的示例。我也很困惑你打算从overallFunc 返回什么。联合酒吧?结合Pub.eraseToAnyPublisher()?等等
@Cristik 谢谢!添加返回类型有助于并提供更多有用的错误消息。
@Cristik 如果您提供您的评论作为答案,我可以接受它作为解决方案。
我只提出了一种调试技术,我觉得这不符合答案的条件。您应该添加一个答案,其中包含您所做的代码更改,并且可能在答案中提及评论对您有帮助。
【参考方案1】:
我认为您对代码进行混淆的尝试已经消除了您遇到的问题。以下代码编译得很好:
func func1(arg1: String, arg2: String) -> AnyPublisher<String?, Error> fatalError()
func func2(arg1: String, arg2: String) -> AnyPublisher<String?, Error> fatalError()
func func3(arg1: String, arg2: String) -> AnyPublisher<String?, Error> fatalError()
func func4(arg1: String) -> AnyPublisher<String?, Error> fatalError()
func overallFunc(arg1: String, arg2: String, arg3: String, arg4: String) -> AnyPublisher<String?, Error>
Publishers.Zip(
func1(arg1: arg1, arg2: arg2),
func2(arg1: arg3, arg2: arg4)
)
.flatMap (response1, response2) in
func3(arg1: response1 ?? "", arg2: response2 ?? "")
.flatMap response3 in
func4(arg1: response3 ?? "")
.eraseToAnyPublisher()
【讨论】:
对不起,下次会提供更具体的例子。谢谢你的回答!添加.eraseToAnyPublisher
有助于更好地隔离问题。解决它的方法是在 flatMap
闭包中显式添加返回类型。
啊,是的。如果你在 flatMap 中有不止一行代码,那么你通常必须定义返回类型(除非在闭包之外有一些东西可以让编译器清楚地知道返回类型应该是什么。)【参考方案2】:
flatMap
的结果将是 String?
(请求 3 的结果),然后您希望将其传递给 map
。 map
会将字符串转换为第四个请求。然后,您可以将订阅者添加到第四个请求,这将是您的整体序列的结果。所以...
pub1
.zip(pub2)
.flatMap (response1, response2) in
func3(arg1: response1.attribute1, arg2: response2.attribute2)
.map
(func3Result : String?) in
/* return publisher of 4th request */
.eraseToAnyPublisher()
所有这一切的结果将是第四个请求的发布者。
【讨论】:
以上是关于使用 Swift 和 Combine 链接 + 压缩多个网络请求的主要内容,如果未能解决你的问题,请参考以下文章
Swift Combine - @Published 属性数组
如何使用 Combine + Swift 复制 PromiseKit 风格的链式异步流
如何在 Swift 中使用 Combine 读取 JSON 错误对象的属性值?
Swift Combine:没有“distinct”运算符?