0

RxSwift các khái niệm cơ bản P.2

Getting Started

Trong bài viết này tôi sẽ tiếp tục nói về các khái niệm cơ bản của RxSwift

  • Operators
  • Custom operators
  • Playgrounds
  • Error handling
  • Debugging Compile Errors
  • Debugging
  • Debugging memory leaks

operators

Có rất nhiều operators trong RxSwift. Danh sách có thể được tra cứu ở đây.

Custom operators

Có 2 cách để tự tạo custom operators.

Cách dễ

Tạo một operator mới thực chất là tạo observables. Cách tạo một map operator ( chưa được tối ưu hóa) .

extension ObservableType {
    func myMap<R>(transform: E -> R) -> Observable<R> {
        return Observable.create { observer in
            let subscription = self.subscribe { e in
                    switch e {
                    case .next(let value):
                        let result = transform(value)
                        observer.on(.next(result))
                    case .error(let error):
                        observer.on(.error(error))
                    case .completed:
                        observer.on(.completed)
                    }
                }

            return subscription
        }
    }
}

Sử dụng map tự tạo:

let subscription = myInterval(0.1)
    .myMap { e in
        return "This is simply \(e)"
    }
    .subscribe(onNext: { n in
        print(n)
    })

Kết quả:
Subscribed
This is simply 0
This is simply 1
This is simply 2
This is simply 3
This is simply 4
This is simply 5
This is simply 6
This is simply 7
This is simply 8
...

Life happens

Khi gặp những vấn đề không thể giải quyết với custom operator bạn có thể thoát khỏi Rx monad, thực hiện lập trình imperative rồi đưa kết quả về Rx again Subjects..

 let magicBeings: Observable<MagicBeing> = summonFromMiddleEarth()

  magicBeings
    .subscribe(onNext: { being in     // thoát khỏi  Rx monad
        self.doSomeStateMagic(being)
    })
    .addDisposableTo(disposeBag)

  let kitten = globalParty(
    being,
    UIApplication.delegate.dataSomething.attendees
  )
  kittens.on(.next(kitten))   // gửi kết quả về  rx

  let kittens = Variable(firstKitten) // trở về Rx monad

  kittens.asObservable()
    .map { kitten in
      return kitten.purr()
    }
    // ....

Khi bạn thực hiện điều này, rất có thể có ai đó lại viết đoạn code dưới

 kittens
    .subscribe(onNext: { kitten in
      // so something with kitten
    })
    .addDisposableTo(disposeBag)

Vì vậy, tốt nhất bạn không nên thực hiện.

Playgrounds

Nếu không chắc chắn về cách thức hoạt động của một operators, bạn có thể sử dụng playgrounds để kiểm tra lại.

Để sử dụng playgrounds hãy mở Rx.xcworkspace, build RxSwift-OSX scheme sau đó mở playgrounds trong Rx.xcworkspace.

Để hiển thị kết quả hãy mở Assistant Editor bằng cách click vào View > Assistant Editor > Show Assistant Editor

Error handling

Có 2 cơ chế quản lý lỗi.

  • Cơ chế Asynchronous error handling trong observables

Nếu một sequence kết thúc với lỗi, tất cả các sequence phụ thuộc cũng bị lỗi theo.

Để phục hồi lỗi của observable ta sử dụng catch operator. Khi sequence bị lỗi có thể sử dụng retry operator để thực hiện lại.

  • Debugging Compile Errors

Khi viết RxSwift/RxCocoa code, bạn có thể sử dụng compiler để suy ra loại của Observables. Đây là điểm hấp dẫn của Swift, nhưng đôi khi nó cũng gây ra phiền toái

images = word
    .filter { $0.containsString("important") }
    .flatMap { word in
        return self.api.loadFlickrFeed("karate")
            .catchError { error in
                return just(JSON(1))
            }
      }

Nếu complier báo có lỗi đâu đó hãy thử thay đổi type trả về.

images = word
    .filter { s -> Bool in s.containsString("important") }
    .flatMap { word -> Observable<JSON> in
        return self.api.loadFlickrFeed("karate")
            .catchError { error -> Observable<JSON> in
                return just(JSON(1))
            }
      }

Có thể tiếp tục thay đổi type cho đến khi tìm ra lỗi.

images = word
    .filter { (s: String) -> Bool in s.containsString("important") }
    .flatMap { (word: String) -> Observable<JSON> in
        return self.api.loadFlickrFeed("karate")
            .catchError { (error: NSError) -> Observable<JSON> in
                return just(JSON(1))
            }

Sau khi tìm ra lỗi hãy xóa type trả về này.

Debugging

Hãy sử dụng kết hợp debuger và debug operator. debug operator sẽ in tất cả các sự kiện và bạn còn có thể gắn nhãn cho các sự kiện này. Cách sử dụng debug:

let subscription = myInterval(0.1)
    .debug("my probe")
    .map { e in
        return "This is simply \(e)"
    }
    .subscribe(onNext: { n in
        print(n)
    })
NSThread.sleepForTimeInterval(0.5)

subscription.dispose()

Kết quả:
[my probe] subscribed
Subscribed
[my probe] -> Event next(Box(0))
This is simply 0
[my probe] -> Event next(Box(1))
This is simply 1
[my probe] -> Event next(Box(2))
This is simply 2
[my probe] -> Event next(Box(3))
This is simply 3
[my probe] -> Event next(Box(4))
This is simply 4
[my probe] dispose
Disposed

Bạn cũng có thể tự tạo debug operator.

extension ObservableType {
    public func myDebug(identifier: String) -> Observable<Self.E> {
        return Observable.create { observer in
            print("subscribed \(identifier)")
            let subscription = self.subscribe { e in
                print("event \(identifier)  \(e)")
                switch e {
                case .next(let value):
                    observer.on(.next(value))

                case .error(let error):
                    observer.on(.error(error))

                case .completed:
                    observer.on(.completed)
                }
            }
            return Disposables.create {
                   print("disposing \(identifier)")
                   subscription.dispose()
            }
        }
    }
 }

Debugging memory leaks

Trong chế độ debug Rx sẽ ghi lại tất cả tài nguyên được cấp phát trong một global variable resourceCount.

Trong trường hợp muốn tìm kiếm resource leak cách đơn giản nhất là in ra

RxSwift.resourceCount theo chu kỳ.
   /* add somewhere in
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
    */
    _ = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        .subscribe(onNext: { _ in
            print("Resource count \(RxSwift.resourceCount)")
        })

Cách tốt nhất để test memory leaks:

  • Di chuyển tới màn hình test
  • Quay lại trước đó
  • Ghi lại giá trị resource count ban đầu
  • Di chuyển lần thứ 2 tới màn hình test.
  • Quay lại trước đó
  • Ghi lại giá trị resource count lần 2

Trong trường hợp có sự khác biệt của resource count giữa lần thứ nhất và lần thứ hai, có thể có memory leak.

Nguồn : RxSwift


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí