rxswift

Qua 5 bài trước, chúng ta đã hiểu các khái niệm cơ bản về RxSwift. Và bây giờ, chúng ta sẽ bắt đầu làm quen với Observables, khái niệm quan trọng của RxSwift. Dịch sát nghĩa là có thể quan sát được. Ở Rx, nó là 1 lớp cho phép bạn quan sát bất cứ thứ gì mà có sự thay đổi về trạng thái, như trạng thái bấm nút, ô text,..

Chuẩn bị cho thực hành

Đầu tiên mở Xcode và tạo mới 1 playground và thêm đoạn code như sau:

public func example(of description: String, action: () -> Void) {
  print("\n--- Example of:", description, "---")
action() }

Nếu như bạn chưa biết cách cài RxSwift cho project của mình, vui lòng xem lại bài 5. Hoặc bạn có thể tải source code ở đây:

https://github.com/lexuanquynh/RxLearning

Sau gõ mở terminal, cd vào thư mục source code và gõ:

pod install

để cài đặt RxSwift cho project.

Vậy 1 observable là gì?

observable là trái tim của RxSwift. Chúng ta sẽ giành thời gian để nghiên cứu, thử nghiệm với nó.

Bạn sẽ nghe các khái niệm observable, observable sequence hay sequence trong Rx. Thậm chí bạn sẽ nghe các khái niệm stream từ các developer khác bằng các khái niệm ở môi trường phát triển khác tương tự. Tuy nhiên ở Rx nó không phải là stream, mà mọi thứ đều là sequence.

Nghĩa là trong Rx mọi thứ đều là luồng quan sát – dịch sát nghĩa là đối tượng quan sát sự kiện có thứ tự.

  • stream: sự kiện phát liên tục
  • sequence: sự kiện phát không đồng bộ, tùy ý
Sự kiện luồng phát 3 sự kiện 1, 2, 3 trong thời gian thực

Mũi tên từ trái sang phải mô tả thời gian, vòng tròn đánh số thể hiện sự kiện phát ra. Đầu tiên sự kiện 1 phát ra, tiếp theo lần lượt sự kiện 2, và cuối cùng là 3. Vậy lúc nào thì phát ra sự kiện? Nó có thể bất kỳ lúc nào ở trong vòng đời của 1 observable.

Vòng đời của 1 obserbvable

Sự kiện bấm vào nút

Những thứ có thể quan sát được và phát ra các sự kiện như nút bấm, được thể hiện bằng sơ đồ trên. Nút được phát ra 3 lần bấm, và kết thúc.

Tuy nhiên đôi khi sự kết thúc có thể trước 3 lần, do trục trặc lỗi.

Sự kiến phát ra có lỗi thể hiện màu đỏ

Khi 1 sự kiện lỗi phát ra, nó kết thúc luôn vòng đời của observable.

  • Một observable có thể quan sát và phát ra các sự kiện tiếp theo cho đến khi tạo ra một sự kiện lỗi và kết thúc
  • Hoặc phát ra 1 sự kiện hoàn thành và kết thúc
  • Một khi observable kết thúc, nó sẽ không thể phát ra thêm sự kiện nào nữa

Ví dụ trực tiếp từ mã nguồn RxSwift:

/// Represents a sequence event.
///
/// Sequence grammar:
/// **next\* (error | completed)**
public enum Event<Element> {
    /// Next element is produced.
    case next(Element)
    /// Sequence terminated with an error.
    case error(Swift.Error)
  /// Sequence completed successfully.
    case completed
}

Như ta thấy ở đây:

  • Sự kiện Next chứa instance của Element
  • Sự kiện error chứa lỗi Swift.Error
  • Sự kiện completed đơn giản là dừng và không có bất kỳ dữ liệu nào

Tạo observables

Quay lại với project các bạn vừa tạo ở đầu bài, chúng ta thêm đoạn code sau vào file playground:

example(of: "just, of, from") {
// 1
  let one = 1
  let two = 2
  let three = 3
// 2
  let observable: Observable<Int> = Observable<Int>.just(one)
}

Ở đoạn code trên:

  • Tạo 3 biến one, two, three
  • Tạo 1 observable và phát ra sự kiện one – tức là phát ra số nguyên one

just là hàm phát ra 1 sự kiện, nó là phương thức của observable. Tuy nhiên trong Rx, nó được gọi là toán tử.

Thêm tiếp mã sau vào cuối chương trình:

let observable2 = Observable.of(one, two, three)

Lần này, chúng ta không chỉ định rõ ràng kiểu cho observable, tuy nhiên chúng ta hiểu đây là 1 observable kiểu int, và đây không phải là mảng. Bấm Option + click chuột để xem:

observable tự chuyển về kiểu int

Nếu bạn muốn tạo 1 observable kiểu mảng, chúng ta dùng toán tử of như sau:

let observable3 = Observable.of([one, two, three])

Còn khi dùng toán tử from, chúng ta tạo 1 observable như sau:

let observable4 = Observable.from([one, two, three])

Toán tử from nhận các giá trị từ mảng, và tạo 1 observable riêng rẽ, nó có kiểu int, chứ không phải kiểu mảng [int].

from tạo ra 1 observable kiểu int, từ 1 mảng kiểu int

Subscribing tới 1 observable

Là một nhà phát triển IOS, chắc các bạn đã quen thuộc với NotificationCenter, nó phát ra 1 notification tới observers. Dưới đây là ví dụ về xử lý sự kiện UIKeyboardDidChangeFrame, được xử lý ở hàm closure:

let observer = NotificationCenter.default.addObserver(
  forName: .UIKeyboardDidChangeFrame,
  object: nil,
  queue: nil
) { notification in
  // Handle receiving notification
}

Việc đăng ký – subscribing tới 1 observable cũng tương tự. Thay vì dùng hàm addObserver(), thì ta dùng hàm subscribe().

Tuy nhiên việc đăng ký các observable hợp lý hơn. Như đã trình bày, các observable phát ra các sự kiện next, completed và error. Để hiểu hơn, chúng ta thêm ví dụ sau vào cuối chương trình:

example(of: "subscribe") {
    let one = 1
    let two = 2
    let three = 3
    let observable = Observable.of(one, two, three)
    observable.subscribe { event in
        print(event)
    }
}

Kết quả:

--- Example of: subscribe ---
next(1)
next(2)
next(3)
completed

Observable phát ra các sự kiện 1, 2, 3 và completed để kết thúc.

Thường chúng ta sẽ quan tâm giá trị phát ra từ các sự kiện đó hơn là chính nó. Chúng ta sửa ví dụ trên sau:

observable.subscribe { event in
  if let element = event.element {
    print(element)
  }
}

Kết quả:

--- Example of: subscribe ---
1
2
3

Sự kiện phát ra chứa giá trị, và là 1 giá trị optional. Vì vấy chúng ta check trước giá trị đó để tránh nil. Các sự kiện không chứa phần tử(ở đây là completed) sẽ không được in ra. Như ta đã biết, các sự kiện onNext đều có phần tử.

Thay thế đoạn code trên như sau:

observable.subscribe(onNext: { element in
  print(element)
})

Vậy là bạn đã biết cách tạo các observable có phần tử. Vậy muốn tạo 1 observable rỗng(empty) thì làm thế nào? Xem ví dụ sau:

example(of: "empty") {
  let observable = Observable<Void>.empty()
observable
  .subscribe(
// 1
    onNext: { element in
      print(element)
},
// 2
    onCompleted: {
      print("Completed")
} )
}

Một observable phải được định nghĩa 1 kiểu có thể quan sát được. Trong trường hợp này nó là kiểu void.

  1. Giống như sự kiện onNext bạn xem ở ví dụ trước
  2. sự kiện onCompleted không trả ra phần tử nào mà chỉ đơn giản là phát sự kiện hoàn thành.
--- Example of: empty ---
Completed

Nhưng mục đích của việc sử dụng 1 observable empty là gì? Nó thật sự hữu ích khi bạn muốn trả về 1 sự kiện kết thúc ngay lập tức và không trả về giá trị.

Trái với toán tử empty, toán tử never không emit ra sự kiện nào và không bao giờ kết thúc. Cùng xem ví dụ sau:

example(of: "never") {
  let observable = Observable<Any>.never()
  observable
    .subscribe(
      onNext: { element in
        print(element)
    },
      onCompleted: {
        print("Completed")
    }
) }

Nó không in ra bất cứ thứ gì kể cả Completed, vậy làm sao để biết là nó hoạt động? Hãy giữ vững tinh thần học hỏi cho đến phần Challenges sau này nhé.

Cho đến bây giờ, bạn đang làm việc với các observable có thể quan sát được. Nhưng bạn cũng có thể tạo ra observable từ 1 loạt các giá trị. Cùng xem ví dụ sinh dãy Phibonaci sau:

example(of: "range") {
    // 1
    let observable = Observable<Int>.range(start: 1, count: 10)
    observable
        .subscribe(onNext: { i in
            // 2
            let n = Double(i)
            let fibonacci = Int(((pow(1.61803, n) - pow(0.61803, n)) /
                                    2.23606).rounded())
            print(fibonacci)
        })
}
  1. Tạo 1 observable từ các số có phạm vi từ 1 đến 10
  2. Tính toán và in ra số fibonacci thứ n cho mỗi phần tử được emit ra.

hàm pow(x, y) trả về x^y.

Trên thực tế, có 1 nơi tốt hơn ngoài xử lý onNext, để đặt mã biến đổi emit sự kiện. Chúng ta sẽ tìm hiểu vấn đề này sau ở bài Transforming Operators.

Ngoài trừ ví dụ về never(), cho đến thời điểm này bạn đang làm việc với các observables có thể quan sát tự động phát ra sự kiện completed để kết thúc. Điều này giúp bạn tập trung vào cơ chế đăng ký observables, tuy nhiên lại gạt đi 1 khía cạnh quan trọng trong việc đăng ký đã xử lý như nào. Khi cấp phát thì nghĩa là cần phải giải phóng cho nó. Đã đến lúc chúng ta nghiên cứu việc giải phóng 1 observable như nào trong bài tiếp theo.

Code Toàn Bug

Code nhiều bug nhiều!

2 thoughts on “RxSwift 6: Observables

Leave a Reply

Your email address will not be published. Required fields are marked *