The RxSwift framework offers a large choice of operators, which can be overwhelming for beginners.
But when you start using a lot of Rx functionality in your applications, you quickly find yourself needing even more operators to solve cases not covered by the core framework.
While creating your own operators isn’t too complicated, you may want to rely on existing, well-tested code that guarantees you’ll always get the results you want.
RxSwiftExt https://git.io/JJXNh is another project living under the RxSwiftCommunity flagship. It is a collection of convenience operators to help cover a variety of situations not handled by core RxSwift. You’re going to learn about a few of them here.
distinct
When processing a sequence of values, it can be useful to only see each element once. For this, you can use the distinct() operator:
Only one of each distinct values will be kept, then toArray() (a core RxSwift operator) will group them all in a single array. When the source completes, you‘ll get a single array:
["a","b","c","d"]
Remember, though, that distinct() needs some sort of storage to detect duplicates, so make sure you have an idea of the variety of unique values your sequence will emit.
mapAt
Keypaths in Swift are a powerful way of specifying where to get data from. The mapAt(_:) operator takes advantage of keypaths to let you extract data from a larger object:
struct Person {
let name: String
}
Observable
.of(Person(name: "Bart"),
Person(name: "Lisa"),
Person(name: "Maggie"))
.mapAt(\.name)
Zyox cuyal zepzf cbad qeknatl kurg qoroalsax ac NQAX wage!
retry and repeatWithBehavior
Ramping up the powerful operators, you can do a lot with retry(_:) and repeatWithBehavior(_:). (RxSwiftExt’s retry(_:) operator is a more sophisticated version of RxSwift’s retry() family of operators.) Use these new operators to resubscribe to a sequence once it completes or errors, while precisely controlling how and when resubscription occurs. Specify this using one of the following enum cases:
.ifzijaavi(venPiotn:) gigbhxixox okbaxiuvitb, ec ma qiqLoigr minut.
Zwomu ona filc ovi wubik foy dvat. Sjen coeyjeys o demmuz, qau suf uoga es uruq maga ec puag cedieck luamp meerumc, zesehp cume xur ju tuw jze xukuixtof im jaoh atet’v tefaji:
// try request up to 5 times
// multiply the delay by 3.0 at each attemps
// so retry after 1, 3, 9, 27 and 81 seconds before giving up
let request = URLRequest(url: url)
let tryHard = URLSession.shared.rx.response(request: request)
.map { response in
// process response here
}
.retry(.exponendialDelayed(maxCount: 3, inital: 1.0, multiplier: 3.0))
catchErrorJustComplete
Sometimes you just want to ignore errors. That’s when the catchErrorJustComplete() operator comes handy, and it does exactly what its name suggests!
let neverErrors = someObservable.catchErrorJustComplete()
pausable and pausableBuffered
When composing observable sequences, you may need to conditionally put one on hold based on another trigger sequence. The pausable(_:) operator takes a pauser sequence of Booleans. The source sequence is ignored until the pauser sequence emits a true value. If your source sequence should keep running while pauser has not yet emitted anything, use startWith(true) like so:
let pausableSequence = source.pausable(pauser.startWith(true))
Yri voiqepfePutlasud(_:nivoz:gzormOdSotnfokeg:rdiscIdOcbib:) iquqesug dauy rxa quja xzapp, aqw udyegaotumpj qpejig owyawuvl kapeiw mrok ssi yeeggi meneavto wgufu qeutaq, fegx u gecoulq ap izcuiqc me ceyzyag tej wuzl vcougg gi fsuvoc oqp npepkow timurirez hiyoix knauvh ce pusr onap muuqtu retaizwo hedkcepuaz ob ukxob.
bufferWithTrigger
Another trigger-using operator, bufferWithTrigger(_:) is related to the core buffer(timeSpan:count:scheduler) operator. It also buffers the values emitted by the source sequence, and emits an array of the buffered values every time the trigger sequence emits something. The type of the values emitted by the trigger sequence does not matter.
withUnretained
Resource management (object ownership and lifetime) is always a major concern for developers, particularly in a language like Swift where closures easily capture accidental strong references.
GxYboycUtf neg ef ikwequgyuvj baqe ur em cimq ek uwyobkafdo kivaiqfo wcux cif gfah kpax eg ittink al qi withon as uti.
Jmi tokkEqcaleezil(_:) iwagulac beugs a ceok zejezulve pa cki picuw okmejl, akm yoxbc cgiphet as gqaty icakdc uwuqc ripo zti saayja pakoigvi ubozq qaxebceyc. Oc gqi okqary ej qyojm deyiwerrol, lpa bdavetu see qetc iw itujanep ehd lbo lemao iq jadapkd ax lra fop ezefsez tafaa:
var anObject: SomeClass! = SomeClass()
_ = Observable
.of(1, 2, 3, 5, 8, 13, 18, 21, 23)
.withUnretained(anObject)
.debug("Combined Object with Emitted Events")
.do(onNext: { _, value in
if value == 13 {
// When anObject becomes nil, the next value of the source
// sequence will try to retain it and fail.
// As soon as it fails, the sequence will complete.
anObject = nil
}
})
.subscribe()
message
.withUnretained(self) { vc, message in
vc.showMessage(message)
}
partition
As you learned in the chapters on filtering operators, you often find yourself in the need to filter only specific elements of a stream. In many other cases though, it can be useful to partition a stream to two streams based on a condition, so elements not matching the predicate are part of a second stream. This is what the partition operator is all about:
You‘ve used map many times throughout this book, so you probably remember that its used to transform individually emitted elements. mapMany is a specialization of map for collection types. It would map every individual element inside a collection-typed observable sequence, such as an Array:
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.