[英]How to bind Signal<Bool, NoError> to enabled property of UIButton in Reactive Cocoa 4
[英]How can I transform a signal with errors into a NoError one with ReactiveSwift? (and be elegant)
將ReactiveSwift的SignalProducer<A, NetworkError>
轉換為Signal<A, NoError>
的最優雅方法是什么?
大多數時候,我的信號生成器是網絡調用的結果,所以我想將結果分成兩種情況:
Signal<A, NoError>
Signal<String, NoError>
(為什么?因為我想盡可能成為MVVM )
到目前為止,我最終寫了很多樣板文,如下所示:
let resultsProperty = MutableProperty<SearchResults?>(nil)
let alertMessageProperty = MutableProperty<String?>(nil)
let results = resultsProperty.signal // `Signal<SearchResults?, NoError>`
let alertMessage = alertMessageProperty.signal // `Signal<String?, NoError>`
// ...
searchStrings.flatMap(.latest) { string -> SignalProducer<SearchResults, NetworkError> in
return MyService.search(string)
}
.observe { event in
switch event {
case let .value(results):
resultsProperty.value = results
case let .failed(error):
alertMessageProperty.value = error
case .completed, .interrupted:
break
}
}
即:
MutableProperty
實例,我必須將其設置為可選,以便能夠初始化它們 任何幫助(A)保持我的信號不可選和(B)優雅地將它們分成2個NoError
信號將非常感激。
我會盡力在這里回答你的所有問題/意見。
errors = part不起作用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<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)
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.