Ios 合并:如何在不完成原始发布服务器的情况下替换/捕获错误?
给定以下代码:Ios 合并:如何在不完成原始发布服务器的情况下替换/捕获错误?,ios,swift,combine,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"
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)
每当我点击按钮时,我都会得到我们处于else情况下
,直到Bool.random()
为真-现在抛出一个错误。我尝试了不同的方法,但我无法捕获/替换/忽略错误并在点击按钮后继续
在代码示例中,我希望有以下输出
we're in the else case
we're in the else case
replaced Error
we're in the else case
...
相反,我在替换错误之后得到完成
,并且没有发出任何事件
编辑
给定一个拥有AnyPublisher
的发布者,我如何在发生错误时将其转换为AnyPublisher
而不完成,即忽略原始发布者发出的错误?只需按以下方式插入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)
工作模式如下所示:
Just(parameter).
flatMap{ (value)->AnyPublisher<String, Never> in
return MyPublisher(value).catch { <String, Never>() }
}.sink(....)
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)
Just(参数)。
flatMap{(值)->中的任意发布者
返回MyPublisher(value).catch{()}
}.水槽(……)
如果我们使用上述示例,它可能是这样的:
Just(parameter).
flatMap{ (value)->AnyPublisher<String, Never> in
return MyPublisher(value).catch { <String, Never>() }
}.sink(....)
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)
let firstPublisher={(值:Int)->AnyPublisher-in
Just(value).tryMap({u0->字符串输入)
if Bool.random(){
抛出我的错误
}否则{
return“我们在其他情况下”
}}).删除任何发布者()
}
仅(1).flatMap{(值:Int)在
返回firstPublisher(value).replaceError(带:“已替换错误”)
}.sink(receiveCompletion:{(completed)in
打印(已完成)
},receiveValue:{(sadf)在
打印(sadf)
}).store(在:&可取消项中)
在这里,您可以将firstPublisher
替换为接受一个参数的任何publisher
在这里,firstPublisher只有一个值,它只能产生一个值。但是,如果您的publisher可以生成多个值,则在发出所有值之前,它不会完成。我建议将publisher
与typealias Failure=Never
并作为可选结果输出:typealias output=Result
要执行此操作,可以使用catch
操作符和Empty
发布者:
让stringErrorPublisher=Just(“你好”)
.setFailureType(收件人:Error.self)
.eraseToAnyPublisher()//AnyPublisher
设stringPublisher=stringErrorPublisher
.catch{inempty()}
.eraseToAnyPublisher()//AnyPublisher
我也在努力解决这个问题,最终找到了解决办法。需要了解的一件事是,您无法从完成的流量中恢复。解决方案是返回结果
,而不是错误
let button = UIButton()
button.publisher(for: .touchUpInside)
.map({ control -> Result<String, Error> in
if Bool.random() {
return .failure(MyError.someError)
} else {
return .success("we're in the else case")
}
}).sink (receiveValue: { (result) in
switch(result) {
case .success(let value):
print("Received value: \(value)")
case .failure(let error):
print("Failure: \(String(describing: error))")
}
})
您不能直接使用PassthroughSubject()
,否则,当出现错误时,通量将完成。我相信E.com的答案是正确的,但我会说得简单得多。处理错误而不导致管道在错误后停止处理值的关键是将错误处理发布服务器嵌套在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)
})
输出:
1
666
3
finished
您可以在操场上运行此示例
关于OP的编辑:
编辑如果某个发布者拥有AnyPublisher
,我如何将其转换为AnyPublisher
,而不在发生错误时完成,即忽略原始发布者发出的错误
你不能。提到了一部WWDC电影,我相信它是2019年的“实践中的结合”,从6:24左右开始观看:
是的,.catch()
终止上游发布服务器(电影7:45),并将其替换为.catch
参数中的给定发布服务器,因此通常会导致在使用Just()
作为替换发布服务器时交付.finished
如果原始发布者在失败后继续工作,则需要一个涉及.flatMap()
的构造(电影9:34)。导致可能故障的运算符需要在.flatMap
中执行,必要时可以在那里处理。诀窍是使用
.flatMap { data in
return Just(data).decode(...).catch { Just(replacement) }
}
而不是
.catch { return Just(replacement) } // DOES STOP UPSTREAM PUBLISHER
在.flatMap
内部,您总是替换发布服务器,因此不关心替换发布服务器是否被.catch
终止,因为它已经是一个替换服务器,并且我们的原始上游发布服务器是安全的。这个例子来自电影
这也是您的编辑:问题的答案,关于如何将
转换为
,因为.flatMap
不会输出任何错误,在flatMap之前和之后都不会有错误。所有与错误相关的步骤都封装在flatMap中。
(提示检查Failure=Never
:如果您获得了的Xcode自动完成。分配(to:)
,那么我相信您有一个Failure=Never流,否则订户不可用。
最后是完整的操场代码
PlaygroundSupport.PlaygroundPage.current.needsIndefiniteExecution=true
枚举MyError:错误{
案例错误
}
let cancelable=Timer.publish(每:1,on:.main,in:.default)
.自动连接()
.flatMap({(输入)在
只是(输入)
.tryMap({(输入)->中的字符串
if Bool.random(){
抛出我的错误
}否则{
return“我们在其他情况下”
}
})
.catch{(错误)在
只是(“替换错误”)
}
})
.sink(receiveCompletion:{(completion)in
打印(完成)
PlaygroundSupport.PlaygroundPage.current.finishExecution()
}){(输出)输入
打印(输出)
}
发布服务器将发出消息,直到它完成或失败(出现错误),然后流将被终止
克服此问题的一种方法是将结果用作发布者类型
出版商
protocol SerivceProtocol {
var value: Published<Result<Double, MyError>>.Publisher { get }
}
service.value
.sink { [weak self] in
switch $0 {
case let .success(value): self?.receiveServiceValue(value)
case let .failure(error): self?.receiveServiceError(error)
}
}
.store(in: &subscriptions)
您需要使用catch{},但是在catch块中写什么呢?如果我使用Just