Schedules
Is just observe & react to changes ?
// java
class Activity {
int clickCount = 0;
function onCreate(){
button.setOnClickListener(() -> {
clickCount += 1;
doSomething();
});
}
function doSomething(){ /* implementation */ }
}
No!!!!
Programming with Event-Driven model?
// java
class Activity {
int clickCount = 0;
function onCreate(){
button.setOnClickListener(() -> {
EventHub.publish(AddClickCountEvent());
EventHub.publish(DoSomethingEvent());
});
}
function onReceive(AddClickCountEvent event) {
clickCount += 1;
}
function onReceive(DoSomethingEvent event) { /* implementation */ }
}
No!!!!
Programming with Asynchronous data stream
// java
class Activity {
Observable<Void> clickStream;
Observable<Integer> clickCountStream;
function onCreate(){
// create a click data stream
clickStream = Observable.create(observer -> {
button.setOnClickListener(() -> {
observer.onNext()
});
});
// transform every click to a integer 1
// accumulate on every integer `x`
clickCountStream = clickStream
.map(() -> 1)
.scan((accu, x) -> accu + x);
}
}
Programming with Asynchronous data stream
clickCountStream = clickStream
.map(() -> 1)
.scan((accu, x) -> accu + x);
Click
ClickCount
1
1
1
1
2
3
map( x -> 1 )
scan(accu,x -> accu + x )
Programming with Asynchronous data stream
OOP, FP has its own focus. How about RP ?
How can we getting start with RP?
ReactiveX is...
ReactiveX 101
Hardest part on adopting ReactiveX
Simple Combination of Stream
// Swift
let a = BehaviourSubject<Int>(value: 1)
let b = BehaviourSubject<Int>(value: 2)
let c = Observable.combineLatest(a,b) { a,b -> a + b }
XCTAssert(c.last() == 3)
a.onNext(10)
XCTAssert(c.last() == 12)
b.onNext(50)
XCTAssert(c.last() == 60)
a.onNext(1)
a.onNext(2)
a.onNext(3)
XCTAssert(c.last() == 53)
Throttling user's input
// version 1
func fetchGoogleMap(word: String, success:(Result) -> () ){/* Implementation */}
func onTextChanges(word: String) {
fetchGoogleMap(word, success: { result in
self.displayResult(result)
})
}
Problems?
Every single input will trigger the API Call...
But each API Call response time is different,
we can't guarantee the last API response is come from the last input
Throttling user's input
// version 2
private var lastRequest: NSURLSessionTask?
func fetchGoogleMap(word: String, success:(Result) -> ())
-> NSURLSessionTask {/* Implementation */}
func onTextChanges(word: String) {
lastRequest?.cancel()
lastRequest = nil
lastRequest = fetchGoogleMap(word, success: { result in
self.displayResult(result)
})
}
Problems?
Every single user input will trigger the API Call... and Cancel it..
Throttling user's input
// version 3
private var lastScheduledWord: dispatch_cancelable_block_t?
private var lastRequest: NSURLSessionTask?
func fetchGoogleMap(word: String, success:(Result) -> ())
-> NSURLSessionTask {/* Implementation */}
func onTextChanges(word: String) {
cancel_block(lastScheduledWord)
lastRequest?.cancel()
lastRequest = nil
// throttle 0.5 second first
lastScheduledWord = dispatch_after_delay(0.5) {
self.lastRequest = fetchGoogleMap(word, success: { result in
self.displayResult(result)
})
}
}
Let see how ReactiveX solve it
Throttling user's input
// version 1 , RxSwift
func fetchGoogleMap(word: String) -> Observable<Result> {/* Implementation */}
func viewDidLoad(){
input.rx.text
.startWith(nil)
.debounce(0.5, scheduler: MainScheduler.instance)
.flatMap(fetchGoogleMap)
.observeOn(MainScheduler.instance)
.subscribe(onNext:{ result in
self.displayResult(result)
})
}
1. Everything is a Stream
API Call
User's Input
User's Action
View's Event
View's State
Async Callback
2. Study Operator hardly
map
flatMap
flatMapLatest
scan
replay
startWith
switchMap
debounce
sample
combineLatest
zip
merge
...
3. Learning Functional Programming
Practise make perfect