Introducing Application coordinator Andrey Panov
Introducing Application coordinator
Andrey Panov
Polytechnic University
iOS projects: • TopFace • KidBook • VoxClub • ToMesto • SmartMuseum
2
[Team]Main team ProTools team
Avito
ActiAgent
ActiDealer
Services
3
presentation.start()• navigation flow in the apps
• usual way
• changes…
• coordinators 😍
• new way
• tips&tricks
4
Flow…
5
Auth flow
6
didSelectRowAtIndexPath:func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if 🍌🍌 { router.openControllerA() } else if 🍌🍌🍌 { router.openControllerB() } else if 🍐 { router.openControllerC() } else if 🍐🍐 { router.openControllerD() } }
7
ViewController
Router
Open it!
8
Controller A Controller B Controller C Send
[String:AnyObject]String
[Options]
[String:AnyObject] [String:AnyObject]String
9
prepareForSegue:override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) { if segue.identifier == "openControllerA"{ let vc = segue.destinationViewController as! ControllerA vc.monkey = 🐒 } else if segue.identifier == "openControllerB"{ let vc = segue.destinationViewController as! ControllerB vc.tiger = 🐯 } else if segue.identifier == "openControllerC"{ let vc = segue.destinationViewController as! ControllerC vc.frog = 🐸 } else if segue.identifier == "openControllerD"{ let vc = segue.destinationViewController as! ControllerD vc.snake = 🐍 } }
10
SignUp&Auth flow (Services)
11
12
coordinator.start()
• hard to reuse
• every controller knows about other controllers
• hard to change flow
• hard to test
13
Application Coordinators
14
–Soroush Khanlou
«So what is a coordinator? A coordinator is an object that bosses view controllers
around. Taking all of the driving logic out of your view controllers, and moving that stuff one layer up is gonna make your life a
lot more awesome.»
15
Auth flow
16
Controller A Controller B Controller C Send
[String: AnyObject] String
[Options]
Coordinator
String
[String: AnyObject]
17
Consists of…
18
Routerable 🙈protocol Router { weak var presenter:UINavigationController? {get} func present(controller:UIViewController, animated: Bool) func push(controller:UIViewController, animated: Bool) func popController(animated: Bool) func dismissController(animated: Bool) }
extension Router { func present(controller:UIViewController, animated: Bool = true) { presenter?.presentViewController(controller, animated: animated, completion: nil) } func push(controller:UIViewController, animated: Bool = true) { presenter?.pushViewController(controller, animated: animated) } func popController(animated: Bool = true) { presenter?.popViewControllerAnimated(animated) } func dismissController(animated: Bool = true) { presenter?.dismissViewControllerAnimated(true, completion: nil) } }
19
Protocols
protocol Coordinatable: class, Router { var flowCompletionHandler: CoordinatorHandler? {get set} func start() }
protocol FlowController { associatedtype T //enum Actions type var completionHandler: (T -> ())? {get set} }
typealias CoordinatorHandler = () -> ()
20
Enumenum ItemListActions {
case ItemSelect(ItemList) case New case VasSelect(ItemDetail) case Edit(ItemDetail) case VasApply }
21
enum ItemListActions { case ItemSelect(ItemList), Create }
class ItemCoordinator: Coordinatable {
var flowCompletionHandler:CoordinatorHandler? var factory: ItemFactory var coordinatorFactory: CoordinatorFactory private(set) weak var presenter: UINavigationController? var childCoordinators: [Coordinatable] = [] init(presenter: UINavigationController) { self.presenter = presenter factory = ItemFactory() coordinatorFactory = CoordinatorFactory() super.init() } func start() { showItemList() }
22
func showItemList() {
let itemListController = factory.createItemListController() itemListController.completionHandler = { [weak self] result in if case let ItemListActions.ItemSelect(list) = result { self?.showItemDetail(list) } else if case ItemListActions.New = result { self?.runCreationCoordinator() } } push(itemListController) }
ItemCoordinator.swift
23
func showItemList() {
let itemListController = factory.createItemListController() itemListController.completionHandler = { [weak self] result in if case let ItemListActions.ItemSelect(list) = result { self?.showItemDetail(list) } else if case ItemListActions.New = result { self?.runCreationCoordinator() } } push(itemListController, animated: false) }
24
Switching from one flow to another
25
Items Coordinator
ItemsControllerItemController
ItemDetailController
ItemFlow
Edit CoordinatorEditController
VasController
EditFlow
?
26
class CoordinatorFactory { func createAuthCoordinatorTyple() -> (authCoordinator: AuthCoordinator, presenter: UINavigationController) { let presenter = createNavigationController() return (AuthCoordinator(presenter: presenter), presenter) } private func createNavigationController() -> UINavigationController { return UINavigationController.controllerFromStoryboard(.Main) } }
27
func runAuthCoordinator() { let authTuple = coordinatorFactory.createAuthCoordinatorTuple() let authCoordinator = authTuple.authCoordinator authCoordinator.flowCompletionHandler = { [unowned self] in self.dismissController() self.removeDependancy(authCoordinator) //continue the flow… } authCoordinator.start() addDependancy(authCoordinator) present(authTuple.presenter) }
28
AppDelegate.swiftclass AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
private lazy var applicationCoordinator: ApplicationCoordinator = { return ApplicationCoordinator(rootController: self.window!.rootViewController as! UINavigationController)
}()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { applicationCoordinator.start() return true } }
29
30
How to integrate in the project?
31
32
Coordinators in project
33
34
coordinator.finish()• controllers know nothing about other controllers
• controllers can be easy integrated in different flows
• controllers don’t send data to the others
• easy to reuse
• simplified testing
35
Application coordinator project
/AndreyPanov/ApplicationCoordinator
questions?.ask() 🙏
/panovdev