Reactive programming by examples - Episode 1
Reactive World
Is almost one year that I have started to learn the reactive world, I began from its manifesto and i continued applying it in development of my Open Source projects and in particular through the languages JavaScript (and then RxJS), Java (and then RxJava) and Swift (and then RxSwift)
Why Reactive ?
Initially I approached reactive paradigm like a kid that open the gift box with a new and unknown toy, mostly fascinated by the technological wave that carried with it a lot of “new” stuff like: functional programming, stream/event oriented programming, backpressure, “callback hell” solver.
But as always “all that glitters ain't gold!” and our capacity to understand context of problems make the real difference and help us to choose the right approach, language, paradigm and frameworks to put on the table.
So I would share with you some problems in particular contexts that I have dealt with reactive programming hoping that, like for myself, the approach by example can be the most meaningful one
Assuming that you have acquired the basic concepts of Reactive Programming like Observer, Observable, Operators, Scheduler … let’s start
Context:
TV Application that presents a slide deck
Problem:
Show Slides in automatic way but, whatever user event (related to navigation) occurs, the show should terminate
Example:
The example below is developed with Swift3 and works on Apple tvOS.
playPauseSlideShow =
Observable<Int>.interval(waitTimeInSeconds, scheduler: MainScheduler.instance)
.map({ (index:Int) -> Int in
guard let prevIndex = self._indexPathForPreferredFocusedView else {
return index + 1
}
return prevIndex.row + 1
})
.takeWhile({ (slide:Int) -> Bool in
return slide < self.doc?.pagesCount
})
.takeUntil( pressesSubject.filter { (press:UIPress) -> Bool in
press.type != UIPressType.playPause
})
.do( onCompleted:{
self.playPauseSlideShow?.dispose()
self.playPauseSlideShow = nil
})
.subscribe( onNext: { (slide:Int) in
let i = IndexPath(row: slide, section: 0)
self.showSlide(at: UInt(i.row))
self._indexPathForPreferredFocusedView = i
self.pagesView.selectItem(at: i, animated: false,
scrollPosition: UICollectionViewScrollPosition())
})
|
The code above is part of my application SlidesOnTV and it is a “one statement” that solve the problem in reactive way.
Yes it is just one statement because the reactive frameworks typical use the “Builder Pattern” to compose the “stream processing chain”.
Let’s explain the code
Below, I will explain each link in the processing chain. Take note that such explanation don’t want cover the functional code but just the rules & responsibility of reactive operations.
Step 1
playPauseSlideShow =
Observable<Int>.interval(waitTimeInSeconds, scheduler: MainScheduler.instance)
|
This create an Observable that emit a tick ( ever tick is an integer incremented by one) every waitTimeSeconds on main thread
|
Step 2
.map({ (index:Int) -> Int in
guard let prevIndex = self._indexPathForPreferredFocusedView else {
return index + 1
}
return prevIndex.row + 1
})
|
This map operator transform each interval’s tick in a consistent slide number getting information from focused view
|
Step 3
.takeWhile({ (slide:Int) -> Bool in
return slide < self.doc?.pagesCount
})
|
takeWhile operator takes emitted items whether the current slide number is still valid otherwise terminate
|
Step 4
.takeUntil( pressesSubject.filter { (press:UIPress) -> Bool in
press.type != UIPressType.playPause
})
|
takeUntil operator allows to evaluate emitted items until a second Observable emits or terminates. This is a bit more complicated but in the same time very powerful and expressive. In this case the second observable “pressesSubject" emits every press on remote, filtered for all pressures not equal to "Play" or "Pause". (the code related to “pressesSubject” is out of scope)
This means that the slide show will continue until some action is performed by user through remote control
|
Step 5
.do( onCompleted:{
self.playPauseSlideShow?.dispose()
self.playPauseSlideShow = nil
})
|
do operator allows to perform an action during lifecycle of items’ processing. In this case when the entire process is completed the observer will be cancelled
|
Step 6 (end)
.subscribe( onNext: { (slide:Int) in
let i = IndexPath(row: slide, section: 0)
self.showSlide(at: UInt(i.row))
self._indexPathForPreferredFocusedView = i
self.pagesView.selectItem(at: i,
animated: false,
scrollPosition: UICollectionViewScrollPosition())
})
|
Finally the subscribe method actives the reactive chain and is here we have to implement the business case for each given item. In this case the image related to the slide will be shown
|
Conclusion
That’s all for this first episode. This is an overview and for further details you have to go in deep into code but again, I hope that this gives an idea about this new paradigm and if it is suitable for your cases
* * *
The original article is on Google Docs
Thanks for sharing this informative content , Great work
ReplyDeleteLeanpitch provides online training in Advanced Scrum Master during this lockdown period everyone can use it wisely.
Advanced Scrum Master Training Online