Coordinator là một Object (kiểu Class trong Swfit) mà có trách nhiệm duy nhất đó là điều phối (coordinate) lo cái việc navigation của app (màn hình nào sẽ được show, màn hình nào sẽ xuất hiện tiếp).
Các bước Coordinator phải làm:
Coordinators làm giúp ViewController cái việc mà có vẻ không thuộc về phận sự của nó, làm cho VC nhìn gọn hơn và dễ reuse hơn. Điều này giúp tuân thủ single responsibility principle trong SOLID. Mình có viết loạt bài về SOLID, các bạn có thể đọc ở đây.
Một VC không nên biết nó đến từ đâu, đi đến đâu tiếp theo và cần phải đem theo những cái gì. Công việc của nó chỉ nên xà quần xung quanh các View/ SubViews và chỉ handle những chuyện liên quan đến UIKit. Tất cả những cái liên quan đến navigation trên VC nên để cho Coordinator handle
OK, vào code thôi. Đầu tiên các bạn tạo một lớp base Coordinator như sau:
Đây là lớp abstract ban đầu, tất cả các lớp khác muốn sử dụng Coordinator thì cũng phải kế thừa. Nếu bạn muốn tạo 1 coordinator, trước hết bạn cần subclass cái base này. Tiếp theo override 2 methods là start và finish. Sau đó bạn viết các method handle việc add hoặc remove các child coordinator’s. Rất giống với cách làm việc của UIViewController đúng không?
Ứng với mỗi màn hình or scene tuỳ tình huống nên có 1 coordinator cụ thể để quản lý. Và luôn có 1 “main” AppCoordinator sẽ được dùng ở App Delegate.
Đoạn code dưới đây là ví dụ về basic AppCoordinator. Tất cả những co-or khác đều là children của AppCo-or.
Đoạn code trên có 3 phần cơ bản:
Đây là cách bạn start app của mình từ AppDelegate sử dụng AppCoordinator.
Bạn sẽ dùng AppCoordinator như là 1 property trong AppDelegate của mình, y hệt cái cách mà Window đã giữ như mình thấy trước đây. Và bên trong hàm application sẽ khởi tạo AppCoordinator với UIWindow và call method start() với nó
Rõ ràng, app của bạn sẽ cần ít nhất 1 hoặc nhiều hơn child coordinator để handle
Your app will obviously need at least one, if not many more, child coordinator’s to all scenes trong app.
Child coordinator sẽ giống với AppCoordinator nhưng sẽ có nhiều việc hơn tuỳ vào scene của mình phức tạp như thế nào.
Phân tích code như sau:
Navigation phần quan trọng trong Coordinators. Ta sẽ add 1 extension đến coordinator để handle all navigation, có 1 method cho mỗi navigation mà có thể xảy ra.
Ở đây có 1 sự khác biệt giữa 2 func như sau:
Phần quan trọng khác của coordinator là delegate/ callbacks, ta sẽ lấy từ ViewModel’s (or ViewControllers) và những child coordinators khác.
Trước tiên, ta có thể gọi callbacks từ child coordinator của ta. Nếu 1 child coordinator phải inform ta về cái gì đó, nó sẽ có 1 delegate protocol mà ta buộc phải implement.
// MARK: - Coordinator Callback's extension SearchInputCoordinator: SearchCoordinatorDelegate { func didFinish(from coordinator: SearchCoordinator) { removeChildCoordinator(coordinator) } }
Trong trường hợp này, ta sẽ implemente the didFinishFrom: delegate method của child coordinator. Ở đây, nếu child coordinator của ta đã xong, finish method của nó sẽ được gọi. Và hàm finish nên gọi delegate của nó để báo nó biết là nó đã xong. Sau đó, ta remove child coordinator ra khỏi stack của ta. Để đảm bảo nó bị removed khỏi bộ nhớ.
// MARK: - ViewModel Callback's extension SearchInputCoordinator: SearchInputViewModelCoordinatorDelegate { func didSelectOrigin(from controller: UIViewController) { goToLocationSearch(searchType: .origin, from: controller) } func didSelectDestination(from controller: UIViewController, fromPlace place: Place) { goToLocationSearch(searchType: .destination(from: place), from: controller) } } extension SearchInputCoordinator: LocationSearchViewModelCoordinatorDelegate { func didSelect(place: TripPlace, from controller: UIViewController) { if place.type == .origin { searchInputViewModel.state.origin = place } else if place.type == .destination { searchInputViewModel.state.destination = place } controller.dismiss(animated: true, completion: nil) } func didSelectClose(from viewModel: LocationSearchViewModel, from controller: UIViewController) { controller.dismiss(animated: true, completion: nil) } }
Cuối cùng, các kiểu callbacks khác mà mình có thể có từ ViewModel của mình (hoặc ViewControllers nếu bạn không sử dụng ViewModel).
Các callbacks này cho ta biết nếu người dùng muốn navigate to phần khác của app và từ đây ta sẽ gọi các method navigation goTo khác. Để ý cái cách mà cái request ViewController này là một paremeter trong những delegate methods này. Điều này làm cho present view controllers lên trên chúng mà không bị giữ reference hoặc đi tìm View Controller trong hierarchy của ta trở nên dễ dàng hơn.
Tổng kết:
Phần coordinator này vừa dễ mà vừa khó, dễ hình dung nhưng áp dụng có vẻ không phải đơn giản. Qua bài này, hi vọng các bài có cái nhìn cơ bản về cách sử dụng MVVM-C
Tham khảo: https://medium.com/sudo-by-icalia-labs/ios-architecture-mvvm-c-coordinators-3-6-3960ad9a6d85