Tìm hiểu RxSwift bài 2 - các khái niệm cơ bản trong Rx
Hôm nay chúng ta sẽ đi tìm hiểu tiếp về Rx cũng như thư viện Rx swift sau loạt bài giới thiệu trước . Hãy tìm hiểu xem Rx là cái chi chi. In computing, reactive programming is a programming paradigm oriented around data flows and the propagation of change. This means that it should be possible ...
Hôm nay chúng ta sẽ đi tìm hiểu tiếp về Rx cũng như thư viện Rx swift sau loạt bài giới thiệu trước . Hãy tìm hiểu xem Rx là cái chi chi.
In computing, reactive programming is a programming paradigm oriented around data flows and the propagation of change. This means that it should be possible to express static or dynamic data flows with ease in the programming languages used, and that the underlying execution model will automatically propagate changes through the data flow. — Wikipedia
Sau khi đọc đoạn trên, chúng ta còn rất mơ hồ về cái gì gọi là react programing. Vậy hãy đi vào chi tiết để biết xem như thế nào là react và cách implement nó trong ứng dụng iOS như thế nào.
1. Observable Sequences
Việc đầu tiên chúng ta cần tìm hiểu về RxSwift là observable sequence hay những thứ hoạt động, đăng ký theo dõi các sự kiện được phát ra bởi observable sequence (nghe rất giống notification nhỉ). Array, String, Dictionary trong RxSwift sẽ được converte sang observable sequence. Chúng ta có thể tự tạo observable sequence của bất cứ object nào conform Sequence protocol. Hãy thử tạo vài sequence xem.
let helloSequence = Observable.just("Hello Rx") let fibonacciSequence = Observable.from([0,1,1,2,3,5,8]) let dictSequence = Observable.from([1:"Hello",2:"World"])
Sau khi có các Observable sequence, ta có thể hiến hành subscrible chúng bằng cách gọi
subscribe(on:(Event<T>)-> ())
block sẽ nhận tất cả event của sequence được observer.
let helloSequence = Observable.of("Hello Rx") let subscription = helloSequence.subscribe { event in print(event) } OUTPUT: next("Hello Rx") completed
Trong vòng đời của một Observable sequences, nó có thể không phát ra hoặc phát ra nhiều event. Trong RxSwift, một event là một enum type với 3 trạng thái:
- .next(value: T) Khi một giá trị hoặc một tập hợp các giá trị được thêm vào observable sequence, nó sẽ tự động gởi next event đến các subcriber của nó. Value trong associated value sẽ chứa giá trị từ sequence.
- . error(error: Error) Nếu một lỗi bị gặp phải trong quá trình thực thi, một error event sẽ được phát đi. Điều này cũng sẽ ngắt luôn sequence.
- .completed Nếu một sequence kết thúc bình thường, nó sẽ gởi completed event đến subcriber.
let helloSequence = Observable.from(["H","e","l","l","o"]) let subscription = helloSequence.subscribe { event in switch event { case .next(let value): print(value) case .error(let error): print(error) case .completed: print("completed") } } OUTPUT: H e l l o completed
Nếu muốn cancel một subcription, chúng ta có thể gọi dispose trên nó hoặc chúng ta thêm subcription vào DisposeBag, DisposeBag sẽ cencal subcription một cách tự động trong phương thức deinit. Bên cạnh đó, chúng ta có thể subcribe một event cụ thể nào đó như error để show lỗi. Bạn hãy xem codesnipe sau để hiểu hơn về DisposeBag:
// Creating a DisposeBag so subscribtion will be cancelled correctly let bag = DisposeBag() // Creating an Observable Sequence that emits a String value let observable = Observable.just("Hello Rx!") // Creating a subscription just for next events let subscription = observable.subscribe (onNext:{ print($0) }) // Adding the Subscription to a Dispose Bag subscription.addDisposableTo(bag)
2. Subject
Một subject là một trạng thái của Observable Sequence, chúng ta có thể subcrible và thêm các element vào nó một cách linh hoạt. Trong RxSwift sẽ có 4 loại Subject.
- PublicSubject: Nhận được tất cả event xảy ra sau khi subcribe.
// Add you subscription to a bag so it will automatically destroyed on deinit let bag = DisposeBag() // Lets create a Publishsubject var publishSubject = PublishSubject<String>() // This will not affect the Subscription publishSubject.onNext("Hello") publishSubject.onNext("World") let subscription1 = publishSubject.subscribe(onNext:{ print(#line,$0) }) subscription1.addDisposableTo(bag) // This will emit to subscription1 publishSubject.onNext("Hello") publishSubject.onNext("Again") let subscription2 = publishSubject.subscribe(onNext:{ print(#line,$0) }) publishSubject.onNext("Both will receive") //Dispose subscription 1 so just subscription 2 will receive the elements subscription1.dispose() publishSubject.onNext("Subscription2 will receive") Output: Start 21 Hello 21 Again 21 Both will receive 31 Both will receive 31 Subscription2 will receive
- BehaviourSubjet: Một behavior subjet sẽ gởi element mới nhất đến bất kỳ subcribe nào sau khi tiến hành subcribe nó.
// Lets create a BeviourSubject // A behaviour Subject needs a start value var behaviourSubject = BehaviorSubject<String>(value:"Just Sub 1") // This subscription will receive the Start Event let subscription1 = behaviourSubject.subscribe { print(#line,$0) }.addDisposableTo(bag) behaviourSubject.onNext("Also just Sub 1") behaviourSubject.onNext("Sub 1 & Sub 2") let subscription2 = behaviourSubject.subscribe{ print(#line,$0) }.addDisposableTo(bag) // This will emit to both subscriptions behaviourSubject.onNext("Sub 1 & Sub 2 again") // Subscription 3 will start with this event because it is the most recent behaviourSubject.onNext("Sub 1 & Sub 2 & Sub 3") let subscription3 = behaviourSubject.subscribe { print(#line,$0) }.addDisposableTo(bag) behaviourSubject.onNext("Everyone will receive this one") Output: 18 next(Just Sub 1) 18 next(Also just Sub 1) 18 next(Sub 1 & Sub 2) 25 next(Sub 1 & Sub 2) 18 next(Sub 1 & Sub 2 again) 25 next(Sub 1 & Sub 2 again) 18 next(Sub 1 & Sub 2 & Sub 3) 25 next(Sub 1 & Sub 2 & Sub 3) 35 next(Sub 1 & Sub 2 & Sub 3) 18 next(Everyone will receive this one) 35 next(Everyone will receive this one) 25 next(Everyone will receive this one)
- ReplySubjet: Với replay subject, bạn có thể lấy được nhiều hơn một trạng thái của sequance, bạn có thể định nghĩa bao nhiêu trạng thái change mình sẽ nhận được sau khi subcribe.
// Lets create a BeviourSubject // A replay Subject with a cache size of 3, so it will replay the last 2 events to each subscriber var replaySubject = ReplaySubject<String>.create(bufferSize: 2) let sub1 = replaySubject.subscribe { print("sub1",$0) }.addDisposableTo(bag) replaySubject.onNext("Will be received by subscription 1 but not by subscription 2") replaySubject.onNext("Will be received by subscription 1 and 2") replaySubject.onNext("Will also be received by subscription 1 and 2") let sub2 = replaySubject.subscribe { print("sub2",$0) }.addDisposableTo(bag) Output: sub1 next(Will be received by subscription 1 but not by subscription 2) sub1 next(Will be received by subscription 1 and 2) sub1 next(Will also be received by subscription 1 and 2) sub2 next(Will be received by subscription 1 and 2) sub2 next(Will also be received by subscription 1 and 2)
- Variable: Là behaviour subject được gói lại để các lập trình viên mới làm quen với react có thể dễ tiếp cận hơn.
// A variable is just a wrapper for a behaviour subject // So subscribers will get the most recent value let variable = Variable("A") variable.value = "Recent Value" variable.asObservable().subscribe(onNext:{ print($0) }) variable.value = "B" variable.asObservable().subscribe(onNext:{ print("new",$0) }) Output: Recent Value B new B
Đến đây thì chúng ta có thể nhận ra được hình hài của RxSwift và cách nó hoạt động phần nào. Vẫn còn rất nhiều thứ để tìm hiểu nhưng các thứ khác sẽ dựa trên những gì chúng ta vừa tìm hiểu qua. Bạn có thể tìm hiểu
3. Marble Diagrams
Marble diagram thể hiện quá trình chuyển hoá (transformation) của một observable sequence. Nó gồm input stream ở đầu và output stream ở cuối, phần giữa là các transformation function. Lấy ví dụ, ta cho delay việc gởi đi các event từ một observable sequence một khoảng 150 ms. Ta sẽ có marble diagram như sau: Các bạn có thể tìm hiểu về marble diagram nhiều hơn qua các project sau: Web-App: http://rxmarbles.com iOS-App: https://itunes.apple.com/com/app/rxmarbles/id1087272442 Android: https://goo.gl/b5YD8K
4. Transformations
Chúng ta có thể biến đổi một observable sequence thành nhiều dạng khác nhau để phù hợp với nhu cầu sử dụng. Mình sẽ đi qua một số transformation operator cơ bản.
4.1 Map
Để transform object gởi ra từ một observable sequence, trước khi gởi đến subcriber, bạn dùng map operation. Xem marble diagram sau để biết rõ hơn:
Observable<Int>.of(1,2,3,4).map { value in return value * 10 }.subscribe(onNext:{ print($0) }) OUTPUT: 10 20 30 40
4.2 FlatMap
Nếu đã quen với FlatMap trong array thì bạn sẽ không mấy bỡ ngỡ khi sử dụng Flatmap trong trường hợp này. FlatMap sẽ gộp kết quả 2 observable sequence.
let sequence1 = Observable<Int>.of(1,2) let sequence2 = Observable<Int>.of(1,2) let sequenceOfSequences = Observable.of(sequence1,sequence2) sequenceOfSequences.flatMap{ return $0 }.subscribe(onNext:{ print($0) }) OUTPUT: 1 2 1 2
5. Filter
Nếu bạn chỉ muốn xử lý thêm điều kiện khi event gởi về thì bạn có thể dùng filter ở đây.
5.1 Filter
Filter operation sẽ hoạt động tương tự swift. Chúng ta định nghĩa điều kiện và khi nào điều kiện thoả mãn thì event sẽ trả về cho subcriber.
Observable.of(2,30,22,5,60,1).filter{$0 > 10}.subscribe(onNext:{ print($0) }) OUTPUT: 30 22 60
5.2 DistinctUntilChanged
Nếu bạn chỉ muốn nhận giá trị mỗi khi nó thay đổi thì có thể dùng distinctUntilChanged.
Observable.of(1,2,2,1,3).distinctUntilChanged().subscribe(onNext:{ print($0) }) OUTPUT: 1 2 1 3
Ngoài 2 loại trên còn các filter operation khác như:
- Debounce
- TakeDuration
- Skip Trên đây là một vài bước cơ bản để bắt đầu với RxSwift. Kì sau chúng ta sẽ tìm hiểu tiếp thư viện này.