07/09/2018, 17:08

RxSwift: Bài 5 - Observables and Subjects in Practice (Part 1)

RxSwift: Bài 5 - Observables and Subjects in Practice (Part 1) Đến bài này, có lẽ mn đã hiểu observables và các loại subjects. Tuy nhiên thực tế để áp dụng như binding UI đến data model hay đưa ra một new controller và gán ngược output lại nó lại rất khó khăn chứ không đơn giản. Bây giờ mình ...

RxSwift: Bài 5 - Observables and Subjects in Practice (Part 1)

Đến bài này, có lẽ mn đã hiểu observables và các loại subjects. Tuy nhiên thực tế để áp dụng như binding UI đến data model hay đưa ra một new controller và gán ngược output lại nó lại rất khó khăn chứ không đơn giản.

Bây giờ mình sẽ đi tạo project thực tế. Mình xin lỗi trước vì mình sẽ không hướng dẫn cách import RxSwift ở đây, các bạn có thể lên mạng search cách import RxSwift và RxCocoa.

Ok, bắt tay nào, ta dựng 1 project như sau:
alt text

Đây là app cắt ghép ảnh rồi dán vào chung thành 1 bức ảnh, link source code ở đây: https://github.com/christopherkmoore/Combinestagram

Step 1: Sử dụng variable trong một view controller
alt text

Mình có ba vấn đề cần nhấn mạnh ở đây:

  1. Bắt đầu add 1 biến Variable vào ViewController(VC) và lưu trữ những photos được chọn trong các giá trị của nó. Variable ở đây sử dụng như các variable bình thường bạn hay dùng: bạn có thể thay đổi giá trị của nó bất cứ khi nào bạn muốn (bằng cách truy xuất vào property
  2. Vì bag và images chỉ dùng trong lớp này, các lớp khác không dùng nên ta đặt là private, tương tự nếu class của bạn không có ai kế thừa nữa thì đặt final
  3. Vì disposeBag thuộc quyền sở hữu của ViewController nên ngay khi VC được giải phóng thì tất cả các observable mà được add disposeBag cũng được xử lý luôn. Bạn xem hình sau: alt text Khá dễ hiểu, điều này sẽ làm cho việc quản lý bộ nhớ của các subscription Rx dễ dàng hơn: đơn giản chỉ là ném hết bọn chúng vào một cái túi rồi chúng sẽ bị xoá khi VC bị dealloc.

Tuy nhiên, điều này sẽ không xảy ra với VC này, bởi vì đây là root view controller và nó sẽ không bị xoá trước khi app quit. Bạn sẽ thấy cơ chế dispose dựa trên dealloc ở phần sau.

Tiếp theo bạn nối IBAction của btnAdd, đặt tên là actionAdd() hoặc gì cũng được, và đơn giản là thêm 1 bức hình trong Assets. Sẵn tiện các bạn tạo luôn 2 action Save và Clear luôn. Tiếp theo, để add thử 1 hình vào trong Variable images, các bạn làm như sau:
alt text
Để ý rằng bạn thay đổi giá trị hiện tại của images cũng như khi bạn làm với variable bình thường. Lớp Variable của Rx sẽ tự động sản xuất ra 1 observable sequence của tất cả những giá trị rời rạc mà bạn đã gắn cho property value của nó. (Tức là mỗi lần bạn variable.value = a là bạn đang gắn từng biến rời rạc đó, sau đó Rx sẽ đưa tất cả những giá trị rời rạc này vào dòng chảy của nó).

Giá trị khởi tạo ban đầu của Variable images là 1 mảng rỗng và mỗi lần user bấm button Add (+). Cái observable sequence mà được tạo ra bởi images sẽ phát ra .next event mới với 1 mảng array mới như là 1 phần tử. (Lưu ý cứ mỗi lần mình add 1 phần từ nó sẽ trả về 1 mảng mới chứ không phải trả về mảng cũ với phần tử mới được add)

Để cho phép người dùng xoá lựa chọn hiện tại, add images.value = [], tức là mình gắn một giá trị mới là mảng rỗng. Như vậy với vài dòng code, bạn đã xử lý user input 1 cách gọn gàng. Nhắc lại, variable có 2 nhiệm vụ, nhận tín hiệu và phát tín hiệu, phần trên đã xử lý công việc nhận tín hiệu thông qua property value của Variable.

Step 2: Adding photos to the collage
Tiếp theo, ta xử lý phần phát tín hiệu, tức là observing images (sau đó hiển thị lên screen). Các bạn để phần code này bên trong viewDidload() như sau:
alt text

Bạn có thể quan sát sự thay đổi này và cập nhập collage preview một cách tương ứng. Vì đây là 1 Variable nên bạn cần dùng Observable của nó để subscribe đến nó

Bạn subscribe cái .next events được phát ra bởi images và mỗi event bạn tạo ra 1 collage với hàm UIImage.collage(images:size:) mà ta extension trong class UIImage+Collage (mình sẽ không giải thích lớp này ở đây). Cuối cùng add subscription này vào cái túi của VC.

Ở lúc này, bạn subscribe cái observables trong viewDidLoad(). Ở những phần tới, ta sẽ làm chuyện này ở trong ViewModel trong MVVM. Bây giờ run app để thấy được cái hay.alt text

Step 3: Driving a complex view controller UI
Bây giờ ta chỉnh app ta thông minh 1 chút như sau:

• Disable Clear/ Save button nếu không có photos
• Disable Clear/ Save button sau khi vừa bấm Clear
• Nếu hình là số lẻ thì Save bị disable (vì bị trống)
• Hạn chế số hình là 6, nếu hơn rất xấu
• Title update số hình hiện tại.

Nếu theo hướng non-reactive thì bạn sẽ thấy những cái này implement là hơi bị cực, còn với RxSwift, bạn chỉ cần subscribe images 1 lần rồi update cái UI tại 1 chỗ duy nhất, khỏi phải lòng vòng. Add thêm cái subscription này bên trong viewDidLoad():
alt text

updateUI() là một hàm private ta sẽ tạo sau đây, tuy nhiên, ở đây mình xin chia sẻ thêm chỗ này, giả sử bạn chưa tạo hàm updateUI() thì compiler thì báo lỗi nhìn chả liên quan gì cả:
alt text

Lỗi là: "Extraneous argument label 'onNext:' in call". Thực sự nếu sau này bạn gặp lỗi này tức là bạn đã gặp lỗi bên trong closure onNext, cái bạn cần fix chính là bên trong này chứ không phải là onNext function. Ở đây, mình gặp issue là mình chưa tạo func updateUI().

Thôi bây giờ ta code phần updateUI():
alt text

Trong phần code trên, các logic mình chắc không cần giải thích nhiều, khá rõ ràng và dễ hiểu. Cái hay ở đây là toàn bộ logic code được đặt vào chỉ một chỗ, chỉ một vài dòng code đơn giản mà nắm toàn bộ app. Quá đỉnh!

0