Tìm hiểu RxSwift bài 2 - các khái niệm cơ bản trong Rx

Mở đầu

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.