Hi I am trying to get an understanding of the RxSwift library to write better, functional code.
Currently I am stuck at a very basic problem. Lets say I got this variables of type Variable<[CiteModel?]>
:
var allCites: Variable<[CiteModel?]> = Variable([])
var shownCites: Variable<[CiteModel?]> = Variable([])
Now I want to filter all cites from allCites
array which contain a specific text and add them to shownCites
.
This is what I have tried but it does not compile because inside my filter block $0
is [CiteModel?]
not CiteModel?
what I would expect. Could you explain to me what I did wrong ?
private func filterCitesByQuery(query: String) {
self.shownCites = self.allCites.asObservable().filter {
$0?.cite.containsString(query)
}
}
Error when executing the above code:
Cannot assign value of type 'Observable<[CiteModel?]>' (aka 'Observable<Array<Optional<CiteModel>>>') to type 'Variable<[CiteModel?]>' (aka 'Variable<Array<Optional<CiteModel>>>')
map
performs an operation on each values of a sequence. When applying map
to an Observable<T>
, map
will receive T
as a parameter to its block.
In the case of Variable<[CiteModel?]>
, Observable
is the sequence hence T == [CiteModel?]
.
Because we really want to filter an array of [CiteModel?]
, you could change the definition of filterCitesByQuery
to
private func filterCitesByQuery(query: String) {
// bag probably needs to be reset here
allCites.asObservable()
.map { // map: apply a transformation to $0
// The desired transformation of $0 is to remove cite that do not contain query
$0.filter { $0.cite.containsString(query) }
}
.bindTo(shownCites)
.addDisposableTo(bag)
}
But this snippet is still sub-optimal, as it requires a subscription to allCites
to be made, where we don't really want to observe its changes.
A better implementation would be
var allCites: Variable<[CiteModel?]> = Variable([])
var searchQuery: Variable<String> = Variable("")
var shownCites: Observable<[CiteModel?]> = Observable .combineLatest(allCites.asObservable(), searchQuery.asObservable()) {
allCites, query in
return allCites.map { cites in cites.filter { $0.cite.containsString(query) } }
}
private func filterCitesByQuery(query: String) {
searchQuery.value = query
}
What's going on here ?
combineLatest
takes the last 2 known values of allCites
and searchQuery
. When either of those changes, the block is executed. We now can subscribe to shownCites
and will get updated values everytime either of the source observable changes.
I got it working with this code:
private func filterCitesByQuery(query: String) {
self.shownCites.value = self.allCites.value.filter {
return $0?.cite.containsString(query) ?? false
}
}
However i still would like to know which was wrong with original code. So if anyone has an answer for me this would be really appreciated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With