结合:如何在不完成原始发布者的情况下替换/捕获错误?

swa*_*ner 15 ios swift combine

鉴于以下代码:

    enum MyError: Error {
        case someError
    }

    myButton.publisher(for: .touchUpInside).tryMap({ _ in
        if Bool.random() {
            throw MyError.someError
        } else {
            return "we're in the else case"
        }
    })
        .replaceError(with: "replaced Error")
        .sink(receiveCompletion: { (completed) in
            print(completed)
        }, receiveValue: { (sadf) in
            print(sadf)
        }).store(in: &cancellables)
Run Code Online (Sandbox Code Playgroud)

每当我点击按钮时,我都会得到we're in the else case直到Bool.random()为真 - 现在抛出一个错误。我尝试了不同的方法,但我无法捕获/替换/忽略错误并在点击按钮后继续。

在代码示例中,我希望有以下输出

we're in the else case
we're in the else case
replaced Error
we're in the else case
...
Run Code Online (Sandbox Code Playgroud)

相反,我得到finishedreplaced error并且没有发出任何事件。

编辑 给定一个带有 的发布者AnyPublisher<String, Error>,如何AnyPublisher<String, Never>在发生错误时将其转换为而不完成,即忽略原始发布者发出的错误?

sma*_*8dd 12

有个WWDC电影提到,我相信是2019年的《实践结合》,6点24分左右开始看:https : //developer.apple.com/wwdc19/721

是的,.catch()终止上游发布者(电影 7:45)并将其替换为参数中的给定一个,.catch因此通常会导致在用作替换发布者.finished时被交付Just()

如果原始出版商在失败后应该继续工作,则需要一个涉及的构造.flatMap()(电影 9:34)。导致可能出现故障的操作符需要在 中执行.flatMap,必要时可以在 中处理。诀窍是使用

.flatMap { data in
    return Just(data).decode(...).catch { Just(replacement) }
}
Run Code Online (Sandbox Code Playgroud)

代替

.catch { return Just(replacement) } // DOES STOP UPSTREAM PUBLISHER
Run Code Online (Sandbox Code Playgroud)

在里面.flatMap你总是替换发布者,因此不关心替换发布者是否被终止.catch,因为它已经是一个替代者并且我们的原始上游发布者是安全的。这个例子来自电影。

这也是您的Edit:问题的答案,关于如何将 a <Output, Error>into <Output, Never>,因为.flatMap不输出任何错误,所以它在 flatMap 之前和之后都不会。所有与错误相关的步骤都封装在 flatMap 中。(提示检查Failure=Never:如果你得到 Xcode 自动完成,.assign(to:)那么我相信你有一个 Failure=Never 流,否则该订阅者不可用。最后是完整的操场代码

.flatMap { data in
    return Just(data).decode(...).catch { Just(replacement) }
}
Run Code Online (Sandbox Code Playgroud)


Gil*_*man 8

我相信 E. Coms 的答案是正确的,但我会说得更简单。处理错误而不导致管道在发生错误后停止处理值的关键是将错误处理发布者嵌套在 内flatMap

import UIKit
import Combine

enum MyError: Error {
  case someError
}

let cancel = [1,2,3]
  .publisher
  .flatMap { value in
    Just(value)
      .tryMap { value throws -> Int in
        if value == 2 { throw MyError.someError }
        return value
    }
    .replaceError(with: 666)
  }
  .sink(receiveCompletion: { (completed) in
    print(completed)
  }, receiveValue: { (sadf) in
    print(sadf)
  })
Run Code Online (Sandbox Code Playgroud)

输出:

1
666
3
finished
Run Code Online (Sandbox Code Playgroud)

您可以在操场上运行此示例。


关于 OP 的编辑:

编辑给定一个带有 的发布者AnyPublisher<String, Error>,如何AnyPublisher<String, Never>在发生错误时将其转换为而不完成,即忽略原始发布者发出的错误?

你不能。


E.C*_*oms 0

只需如下插入 flatMap 即可实现您想要的

   self.myButton.publisher(for: \.touchUpInside).flatMap{
            (data: Bool) in
        return Just(data).tryMap({ _ -> String in
        if Bool.random() {
            throw MyError.someError
        } else {
            return "we're in the else case"
        }}).replaceError(with: "replaced Error")
    }.sink(receiveCompletion: { (completed) in
            print(completed)
        }, receiveValue: { (sadf) in
            print(sadf)
       }).store(in: &cancellables)
Run Code Online (Sandbox Code Playgroud)

工作模型看起来像这样:

 Just(parameter).
 flatMap{ (value)->AnyPublisher<String, Never> in 
 return MyPublisher(value).catch { <String, Never>() } 
 }.sink(....)
Run Code Online (Sandbox Code Playgroud)

如果我们使用上面的例子,它可能是这样的:

let firstPublisher    = {(value: Int) -> AnyPublisher<String, Error> in
           Just(value).tryMap({ _ -> String in
           if Bool.random() {
               throw MyError.someError
           } else {
               return "we're in the else case"
            }}).eraseToAnyPublisher()
    }

    Just(1).flatMap{ (value: Int) in
        return  firstPublisher(value).replaceError(with: "replaced Error")
   }.sink(receiveCompletion: { (completed) in
            print(completed)
        }, receiveValue: { (sadf) in
            print(sadf)
       }).store(in: &cancellables)
Run Code Online (Sandbox Code Playgroud)

此处,您可以将 替换firstPublisher为带有一个参数的 AnyPublisher。

另外,这里,firstPublisher只有一个值,它只能产生一个值。但是,如果您的发布者可以生成多个值,则在发出所有值之前它不会完成。