编辑 - 第二次尝试
我将尝试在这里回答您的所有问题/评论。
errors = 部分不起作用,因为 flatMapError 需要 SignalProducer (即您的示例代码起作用只是因为 searchStrings 是一个 Signal 字符串,巧合的是,它与我们想要的错误相同:它不适用于任何其他类型的输入)
你是对的,这是因为flatMapError
不改变value
类型。(它的签名是func flatMapError<F>(_ transform: @escaping (Error) -> SignalProducer<Value, F>) -> SignalProducer<Value, F>
)。map
如果您需要将其更改为另一种值类型,您可以在此之后添加另一个调用。
结果 = 部分的行为很奇怪,因为它在我的现实生活场景中一旦遇到错误(这是我不想要的行为)就终止信号
是的,这是因为flatMap(.latest)
将所有错误转发给外部信号,外部信号上的任何错误都会终止它。
好的,这是代码的更新版本,还有额外的要求
errors
应该有不同searchStrings
的类型,比方说Int
- 任何错误
MyService.search($0)
都不会终止流程
我认为解决这两个问题的最简单方法是使用materialize()
. 它所做的基本上是将所有信号事件(新值、错误、终止)“包装”到一个Event
对象中,然后在信号中转发这个对象。所以它将一个类型的信号转换Signal<A, Error>
成一个Signal<Event<A, Error>, NoError>
(你可以看到返回的信号不再有错误,因为它被包裹在了Event
)。
在我们的例子中,这意味着您可以使用它来轻松防止信号在发出错误后终止。如果错误包含在一个Event
中,那么它不会自动终止发送它的信号。(实际上,只有信号调用materialize()
完成,但我们将它包裹在里面,flatMap
所以外面的不应该完成。)
这是它的样子:
// Again, I assume this is what you get from the user
let searchStrings: Signal<String, NoError>
// Keep your flatMap
let searchResults = searchStrings.flatMap(.latest) {
// Except this time, we wrap the events with `materialize()`
return MyService.search($0).materialize()
}
// Now Since `searchResults` is already `NoError` you can simply
// use `filterMap` to filter out the events that are not `.value`
results = searchResults.filterMap { (event) in
// `event.value` will return `nil` for all `Event`
// except `.value(T)` where it returns the wrapped value
return event.value
}
// Same thing for errors
errors = searchResults.filterMap { (event) in
// `event.error` will return `nil` for all `Event`
// except `.failure(Error)` where it returns the wrapped error
// Here I use `underestimatedCount` to have a mapping to Int
return event.error?.map { (error) in
// Whatever your error mapping is, you can return any type here
error.localizedDescription.characters.count
}
}
让我知道这是否有帮助!我实际上认为它看起来比第一次尝试更好:)
第一次尝试
您是否需要访问 viewModel 的状态,或者您是否正在尝试完全无状态?如果无状态,您不需要任何属性,您可以这样做
// I assume this is what you get from the user
let searchStrings: Signal<String, NoError>
// Keep your flatMap
let searchResults = searchStrings.flatMap(.latest) {
return MyService.search($0)
}
// Use flatMapError to remove the error for the values
results = searchResults.flatMapError { .empty }
// Use flatMap to remove the values and keep the errors
errors = searchResults.filter { true }.flatMapError { (error) in
// Whatever you mapping from error to string is, put it inside
// a SignalProducer(value:)
return SignalProducer(value: error.localizedDescription)
}