Contents
Chào bạn đến với Fx Studio. Chúng ta lại tiếp tục khám phá thêm miền bugs mới. Bài viết lần này sẽ trình bày cách tương tác dữ liệu giữa 2 ViewController trong thế giới RxSwift.
Nếu bạn đã bỏ qua bài Hello ViewController bằng RxSwift, thì có thể tham khảo lại link sau:
Và mọi thứ đã ổn rồi, thì …
Bắt đầu thôi!
Chuẩn bị
-
- Xcode 11
- Swift 5.x
Chúng ta vẫn sử dụng project ở bài trước. Nếu bạn xây dựng một project ngay từ đầu thì phải thêm 1 ViewController nữa. Ngoài ra, dành cho các bạn chưa biết checkout ở đâu, thì hãy tham khảo link sau:
-
- Link: checkout
- Thư mục:
/Examples/BasicRxSwift
Bạn hãy thêm một màn hình nữa, để phục vụ cho việc lựa chọn Avatar của User lúc đăng ký tài khoản. Nó có tên là ChangeAvatarViewController
. Nó bao gồm:
- 1 CollectionView với cell hiển thị ảnh
- Select vào mỗi cell sẽ chọn ảnh
Ta edit lại một chút chỗ function changeAvatar
ở RegisterViewController để hiển thị được ChangeAvatartViewController khi người dùng nhấn vào.
@objc func changeAvatar() { let vc = ChangeAvatarViewController() self.navigationController?.pushViewController(vc, animated: true) }
Okay! xong phần dạo đầu. Chúng ta tiến vào phần chính.
1. Setup
1.1. ViewController
Tất nhiên, công việc đầu tiên là cài đặt cho ChangeAvatarViewController cũng có được khả năng RxSwift. Mở file ViewController đó lên và import vào:
import RxSwift import RxCocoa
Hai thư viện hàng đầu của chúng ta. Giờ thêm tiếp Túi rác quốc dân vào nữa.
private let bag = DisposeBag()
Có thể chúng ta không dùng tới nó, nhưng để cho chắc chắn thì bạn hãy thêm nó vào. Và tập thành thói quen sau này.
1.2. Observable Properties
Tiếp tục với phần thêm các properties của ViewController là các Observable nào.
private let selectedPhotosSubject = PublishSubject<UIImage>() var selectedPhotos: Observable<UIImage> { return selectedPhotosSubject.asObservable() }
Ta thấy có tới 2 Observables
selectedPhotosSubject
là một Subject, nó sẽemit
dữ liệu và không cần thiết phải khai báo thêm giá trị ban đầu cho nó.- Vì lúc hiển thị ViewController lên, thì người dùng chưa có sự lựa chọn nào hết.
- Quan trọng nhất là nó
private
selectedPhotos
đây là phát ngôn viên của ViewController.- Để bên ngoài (các đối tượng khác) có thể
subscribe
tới thuộc tính này của ViewController. - Thực chất là việc biến đổi
subject
kia thành 1observable
- Để bên ngoài (các đối tượng khác) có thể
Và cả 2 đều có kiểu dữ liệu Output khi emit
là UIImage
.
2. Emit data
2.1. Vấn đề
Vâng, chúng ta đã sử dụng được rất rất là cơ bản RxSwift trong một UIViewController rồi. Để giải quyết 2 vấn đề là:
- Update dữ liệu lên UI Control
- Xử lý sự kiện người dùng
Bài toán của chúng ta tiếp tục với việc tương tác với một ViewController khác. Giờ project chúng ta đã có 2 ViewController. Nhiệm vụ quan trọng trong bài này chính là:
Truyền dữ liệu giữa các ViewController
Trong việc tương tác này, chúng ta chỉ cần quan tâm tới việc truyền dữ liệu theo chiều ngược lại. Và các phương pháp trước đây được sử dụng là:
- Protocol Delegate
- Closure
- Notification
Và bây giờ, chúng ta sẽ dùng RxSwift để thêm một cách nữa trong việc truyền dữ liệu này.
2.2. Emit
Sau khi đã cài đặt các Observable (bước setup), công việc tiếp theo là phát dữ liệu đi. Lúc này, chúng ta không quan tâm là dữ liệu phát đi thì ai sẽ nhận được.
Phát dữ liệu thì trong RxSwift sẽ gọi là
emit
.
Với collectionview tại delegate của nó, chúng ta sẽ bắt được sự kiện người dùng chọn vào cell
. Từ đó tiến hành emit
dữ liệu.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { selectIndex = indexPath.row collectionView.reloadData() //emit UIImage if let image = UIImage(named: "avatar_\(indexPath.row + 1)") { selectedPhotosSubject.onNext(image) } }
Toán tử .onNext
khi được Subject thực thi, thì dữ liệu truyền vào đó sẽ được gởi đi. Chỉ có vậy thôi, coi như chúng ta đã xong phần của ChangeAvatarViewController rồi.
3. Observing Observables
Đã xong việc, chúng ta có bên phát đi rồi và đối nghịch nó là bên nhận. Chúng ta lại quay về RegisterViewController để làm công việc này.
Nhận dữ liệu từ ViewController khác.
Tại function changeAvatar
, tiến hành subscribe
tới các Observable của ChangeAvatarViewController. Ta thêm đoạn code này vào.
@objc func changeAvatar() { let vc = ChangeAvatarViewController() self.navigationController?.pushViewController(vc, animated: true) //subscribe vc.selectedPhotos .subscribe(onNext: { img in self.image.accept(img) }, onDisposed: { print("Complete changed Avatar") }) .disposed(by: bag) }
Trong đó
.selectedPhotos
là đối tượng Observable mà chúng ta đã tạo bên ViewController kia. Nó là phát ngôn viên của nó.subscribe
để nhận dữ liệu từ ViewController kia phát ra..onNext
handle việc nhận dữ liệu. Từ đó, chúng ta dùngBehaviorRelay
đểaccept
dữ liệu mới lên UI của ViewController bên này..onDispose
để xem việc đăng kí này có kết thúc hay là không. Điều này rất quan trọng.
Giờ bạn có thể build app và test lại mọi thứ đã hoạt động tốt hay không.
4. Disposing subscriptions
Nếu bạn để ý kĩ đoạn code này tại function changeAvatar
ở RegisterViewController sẽ không bao giờ chạy.
onDisposed: { print("Complete changed Avatar") }
Đây thực sự là rất nguy hiểm.
Nguyên nhân vì sao?
- Khi bạn đăng ký tới 1 Observable và nén nó vào
disposeBag
, khi đó subscription sẽ được lưu giữ lại tạidisposeBag
. - Subscription sẽ giải phóng khi nào
disposeBag
giải phóng. - Tuy nhiên, RegisterViewController là rootView của ứng dụng lúc này. Có nghĩa là nó tồn tại vĩnh viễn với thời gian.
Các subscription khi chúng ta đăng ký tới vẫn còn tồn tại. Bộ nhớ vẫn bị chiếm giữ. Mà chúng ta không làm gì được hết ở đây.
Cách giải quyết sẽ là:
Nếu
disposeBag
không tự kết liễu, thì sẽ cho kết thúcobservable
.
Vâng, quay lại ChangeAvatarViewController, thêm function này vào.
override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) selectedPhotosSubject.onCompleted() }
Với toán tử .onCompleted()
thì subject sẽ kết thúc. Và không bao giờ emit
ra nữa. Đồng nghĩa với các đăng ký từ các subscriber tới nó cũng bị huỷ. Test lại app, chúng ta đã thấy xuất hiện dòng print
ở onDispose rồi.
OKAY! Tới đây thì mình xin kết thúc bài viết. Nếu có gì thắc mắc hay góp ý cho mình thì bạn có thể để lại bình luận hoặc gởi email theo trang Contact.
Tạm kết
- Mục đích bài này là truyền dữ liệu qua lại giữa 2 ViewController
- Dễ hiểu hơn là viết lại
delegate
bằng RxSwift một cách đơn giản. Ta không cần quan tâm việc tạo các protocol và implement các function đó.
Cảm ơn bạn đã đọc bài viết này!
Related Posts:
Written by chuotfx
Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!
Leave a Reply Cancel reply
Fan page
Tags
Recent Posts
- Charles Proxy – Phần 1 : Giới thiệu, cài đặt và cấu hình
- Complete Concurrency với Swift 6
- 300 Bài code thiếu nhi bằng Python – Ebook
- Builder Pattern trong 10 phút
- Observer Pattern trong 10 phút
- Memento Pattern trong 10 phút
- Strategy Pattern trong 10 phút
- Automatic Reference Counting (ARC) trong 10 phút
- Autoresizing Masks trong 10 phút
- Regular Expression (Regex) trong Swift
You may also like:
Archives
- September 2024 (1)
- July 2024 (1)
- June 2024 (1)
- May 2024 (4)
- April 2024 (2)
- March 2024 (5)
- January 2024 (4)
- February 2023 (1)
- January 2023 (2)
- November 2022 (2)
- October 2022 (1)
- September 2022 (5)
- August 2022 (6)
- July 2022 (7)
- June 2022 (8)
- May 2022 (5)
- April 2022 (1)
- March 2022 (3)
- February 2022 (5)
- January 2022 (4)
- December 2021 (6)
- November 2021 (8)
- October 2021 (8)
- September 2021 (8)
- August 2021 (8)
- July 2021 (9)
- June 2021 (8)
- May 2021 (7)
- April 2021 (11)
- March 2021 (12)
- February 2021 (3)
- January 2021 (3)
- December 2020 (3)
- November 2020 (9)
- October 2020 (7)
- September 2020 (17)
- August 2020 (1)
- July 2020 (3)
- June 2020 (1)
- May 2020 (2)
- April 2020 (3)
- March 2020 (20)
- February 2020 (5)
- January 2020 (2)
- December 2019 (12)
- November 2019 (12)
- October 2019 (19)
- September 2019 (17)
- August 2019 (10)