Bài 9. RxSwift – Transforming Operators
1. Transforming elements
1.1. toArray
Việc nhận từng phần tử một thì rất tốn thời gian và đôi khi bạn muốn nhận hết một lèo tất cả phần tử của 1 Observable. Thì hãy dùng toán tử này toArray(). Nó sẽ giúp bạn gom tất cả phần tử được phát đi thành 1 array. Việc này sẽ thực hiện sau khi Observable kết thúc. Ví dụ:
let bag = DisposeBag()
Observable.of(1, 2, 3, 4, 5, 6)
.toArray()
.subscribe(onSuccess: { value in
print(value)
})
.disposed(by: bag)
Thực thi code trên, bạn sẽ thấy giá trị nhận được là một Array Int. Điểm đặc biệt ở đây là với toán tử toArray
thì nó sẽ biến đổi Observable đó về thành là 1 Single. Khi đó chỉ cho phép là .onSuccess
hoặc .error
mà thôi.
1.2. map
- Biến đổi từ kiểu dữ liệu này thành kiểu dữ liệu khác cho các phần tử nhận được
- Việc biến đổi được xử lý bằng một
closure
- Sau khi biến đổi nếu bạn
subscribe
thì hãy chú ý tới kiểu dữ liệu mới đó, tránh bị nhầm lẫn.
Ví dụ:
let bag = DisposeBag()
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
Observable<Int>.of(1, 2, 3, 4, 5, 10, 999, 9999, 1000000)
.map { formatter.string(for: $0) ?? "" }
.subscribe(onNext: { string in
print(string)
})
.disposed(by: bag)
Kết quả:
one
two
three
four
five
ten
nine hundred ninety-nine
nine thousand nine hundred ninety-nine
one million
Thêm toán tử .enumerated()
lấy được index
của các phần tử .
2. Transforming inner observables
Phần này sẽ dùng chung struct sau:
struct So {
let soNguyen: BehaviorSubject<Int>
}
2.1. flatMap
- Ta có 1 Observable gốc có kiểu dữ liệu cho các element của nó là 1 kiểu thuộc Observable
- Vì các element có kiểu Observable ,thì nó có thể phát dữ liệu. Lúc này, chúng ta có rất nhiều stream phát đi
- Muốn nhận được tất cả dữ liệu từ tất cả các element của Observable gốc, thì ta dùng toán tử flatMap
- Chúng sẽ hợp thể tất cả các giá trị của các element đó phát đi thành 1 Observable ở đầu cuối. Mọi công việc subcirbe vẫn bình thường và ta không hề hay biết gì ở đây.
Ví dụ:
let soDuong = So(soNguyen: BehaviorSubject(value: 1))
let soAm = So(soNguyen: BehaviorSubject(value: -1))
let subject = PublishSubject<So>()
subject
.flatMap { $0.soNguyen }
.subscribe(onNext: { msg in
print(msg < 0 ? "Số âm: \(msg)": "Số dương: \(msg)")
})
.disposed(by: bag)
subject.onNext(soDuong)
soDuong.soNguyen.onNext(2)
soDuong.soNguyen.onNext(3)
soDuong.soNguyen.onNext(4)
soDuong.soNguyen.onNext(5)
subject.onNext(soAm)
soDuong.soNguyen.onNext(6)
soAm.soNguyen.onNext(-2)
soDuong.soNguyen.onNext(7)
soDuong.soNguyen.onNext(8)
soAm.soNguyen.onNext(-3)
Kết quả:
Số dương: 1
Số dương: 2
Số dương: 3
Số dương: 4
Số dương: 5
Số âm: -2
Số dương: 6
Số dương: 7
Số dương: 8
Số âm: -3
- Có 3 đối tượng luân phiên nhau phát dữ liệu
subject
đóng vài trò là Observable gốc, sẽ phát đi các dữ liệu là các Observable khác- soDuong & soAm sẽ phát đi mà
Int
2.2. flatMapLastest
Về cơ bản là giống như flatMap
về việc hợp nhất các Observable lại với nhau. Tuy nhiên, điểm khác là nó sẽ chỉ phát đi giá trị của Observable cuối cùng tham gia vào. Ví dụ
- Có 3 Observable là O1, O2 và O3 join vào lần lượt
flatMapLatest
sẽ biến đổi các Observable đó thành 1 Observable duy nhất- Giá trị nhận được tại một thời điểm chính ta giá trị của phần tử cuối cùng join vào lúc đó
Ví dụ:
let soDuong = So(soNguyen: BehaviorSubject(value: 1))
let soAm = So(soNguyen: BehaviorSubject(value: -1))
let subject = PublishSubject<So>()
subject
.flatMapLatest { $0.soNguyen }
.subscribe(onNext: { msg in
print(msg < 0 ? "Số âm: \(msg)": "Số dương: \(msg)")
})
.disposed(by: bag)
subject.onNext(soDuong)
soDuong.soNguyen.onNext(2)
soDuong.soNguyen.onNext(3)
soDuong.soNguyen.onNext(4)
soDuong.soNguyen.onNext(5)
subject.onNext(soAm)
soDuong.soNguyen.onNext(6)
soAm.soNguyen.onNext(-2)
soDuong.soNguyen.onNext(7)
soDuong.soNguyen.onNext(8)
soAm.soNguyen.onNext(-3)
Giống như flatMap
nhưng giờ chúng ta thay toán tử flatMap
thành flatMapLatest
và kết quả nhận được:
Số dương: 1
Số dương: 2
Số dương: 3
Số dương: 4
Số dương: 5
Số âm: -1
Số âm: -2
Số âm: -3
Kể từ khi subject.onNext(soAm)
thì soDuong
dù đã phát ra thêm giá trị 6, 7, 8 nhưng không được in ra kết quả, đó là điểm khác nhau giữa flatMap
và flatMapLatest
2.3. Error, Completed
Thử với error
hay completed
flatMapLatest
*** Error** Ví dụ:
let soDuong = So(soNguyen: BehaviorSubject(value: 1))
let soAm = So(soNguyen: BehaviorSubject(value: -1))
let subject = PublishSubject<So>()
subject
.flatMapLatest { $0.soNguyen }
.subscribe(onNext: { msg in
print(msg < 0 ? "Số âm: \(msg)": "Số dương: \(msg)")
})
.disposed(by: bag)
subject.onNext(soDuong)
soDuong.soNguyen.onNext(2)
soDuong.soNguyen.onNext(3)
soDuong.soNguyen.onNext(4)
soDuong.soNguyen.onNext(5)
soDuong.soNguyen.onError(MyError.anError)
subject.onNext(soAm)
soDuong.soNguyen.onNext(6)
soAm.soNguyen.onNext(-2)
soDuong.soNguyen.onNext(7)
soDuong.soNguyen.onNext(8)
soAm.soNguyen.onNext(-3)
Kêt quả:
Số dương: 1
Số dương: 2
Số dương: 3
Số dương: 4
Số dương: 5
Unhandled error happened: anError
Mọi thứ dùng lại ngay khi soDuong.soNguyen.onError(MyError.anError)
.
Thử tiếp một ví dụ tiếp theo:
let soDuong = So(soNguyen: BehaviorSubject(value: 1))
let soAm = So(soNguyen: BehaviorSubject(value: -1))
let subject = PublishSubject<So>()
subject
.flatMapLatest { $0.soNguyen }
.subscribe(onNext: { msg in
print(msg < 0 ? "Số âm: \(msg)": "Số dương: \(msg)")
})
.disposed(by: bag)
subject.onNext(soDuong)
soDuong.soNguyen.onNext(2)
soDuong.soNguyen.onNext(3)
soDuong.soNguyen.onNext(4)
soDuong.soNguyen.onNext(5)
subject.onNext(soAm)
soDuong.soNguyen.onError(MyError.anError)
soDuong.soNguyen.onNext(6)
soAm.soNguyen.onNext(-2)
soDuong.soNguyen.onNext(7)
soDuong.soNguyen.onNext(8)
soAm.soNguyen.onNext(-3)
Kết quả:
Số dương: 1
Số dương: 2
Số dương: 3
Số dương: 4
Số dương: 5
Số âm: -1
Số âm: -2
Số âm: -3
Vì sử dụng .flatMapLatest
, nên khi subject.onNext(soAm)
thì lúc soDuong.soNguyen.onError(MyError.anError)
đã không còn hiệu lực.
*** Completed**
Với Completed
, ví dụ soDuong.soNguyen.onCompleted()
tất cả những soDuong.soNguyen.onNext(_)
sẽ không hoạt động. Còn lại, vẫn hoạt động bình thường.
flatMap
*** Error**
Khác với .flatMapLatest
, thì khi .onError
thì .flatMap
sẽ dừng lại luôn. Bạn có thể sửa ở ví dụ để hiểu chi tiết hơn.
*** Completed**
Với Completed
tương tự như .flatMapLatest
, ví dụ soDuong.soNguyen.onCompleted()
tất cả những soDuong.soNguyen.onNext(_)
sẽ không hoạt động. Còn lại, vẫn hoạt động bình thường.
2.4. materialize
Toán tử materialize
sẽ biến đổi các event
của Observable thành một element.
Ví dụ:
let soDuong = So(soNguyen: BehaviorSubject(value: 1))
let soAm = So(soNguyen: BehaviorSubject(value: -1))
let subject = PublishSubject<So>()
subject
.flatMapLatest { $0.soNguyen.materialize() }
.subscribe(onNext: { msg in
print(msg)
})
.disposed(by: bag)
subject.onNext(soDuong)
soDuong.soNguyen.onNext(2)
soDuong.soNguyen.onNext(3)
soDuong.soNguyen.onNext(4)
soDuong.soNguyen.onError(MyError.anError)
soDuong.soNguyen.onNext(5)
subject.onNext(soAm)
soDuong.soNguyen.onNext(6)
soAm.soNguyen.onNext(-2)
soDuong.soNguyen.onNext(7)
soDuong.soNguyen.onNext(8)
soAm.soNguyen.onNext(-3)
Kết quả:
next(1)
next(2)
next(3)
next(4)
error(anError)
next(-1)
next(-2)
next(-3)
flatMapLatest không bị dừng lại khi soDuong.soNguyen.onError(MyError.anError).
2.5. dematerialize
dematerialize
thì ngược lại materialize
. Giải nén các giá trị là events để lấy giá trị thật sự trong đó.
Ví dụ:
let soDuong = So(soNguyen: BehaviorSubject(value: 1))
let soAm = So(soNguyen: BehaviorSubject(value: -1))
let subject = PublishSubject<So>()
let subjectEvent = subject
.flatMapLatest { $0.soNguyen.materialize() }
subjectEvent.filter{
guard $0.error == nil else {
print("Lỗi phát sinh: \($0.error!)")
return false
}
return true
}
.dematerialize()
.subscribe(onNext: { msg in
print(msg)
})
.disposed(by: bag)
subject.onNext(soDuong)
soDuong.soNguyen.onNext(2)
soDuong.soNguyen.onNext(3)
soDuong.soNguyen.onNext(4)
soDuong.soNguyen.onError(MyError.anError)
soDuong.soNguyen.onNext(5)
subject.onNext(soAm)
soDuong.soNguyen.onNext(6)
soAm.soNguyen.onNext(-2)
soDuong.soNguyen.onNext(7)
soDuong.soNguyen.onNext(8)
soAm.soNguyen.onNext(-3)
Kết quả:
1
2
3
4
Lỗi phát sinh: anError
-1
-2
-3
Tham khảo: https://fxstudio.dev/rxswift-transforming-operators/
All rights reserved