35
Удобный и расширяемый роутинг в iOS-приложении Юсипов Тимур

"Marshroute: удобный и расширяемый роутинг в iOS-приложении" Тимур Юсипов (Avito)

Embed Size (px)

Citation preview

Удобный и расширяемый роутинг в iOS-приложении

Юсипов Тимур

Историческая справка(2011 .. 2014) Outsource(2014 .. 2016) In-house

VIPER

121 662 70 187

lines of code

22.09.201629.05.2016

39 051 24 334lines of code

Введем понятие Router

Как будет проходить презентация?

Рассмотрим необычные задачи Routing’а

Попробуем написать RouterАдаптируем Router под iPadСформулируем общие архитектурные правила Routing’а

Посмотрим демо

Что такое Module?

Router

View Controller

MVC

Model View View Interactor

Router

Presenter Entity

VIPER

RouterRouter

View Controller

MVC

Model View View InteractorPresenter Entity

VIPER

Что такое DeepLink?

ru.navigation.demo://categories?categoryId=182

ru.navigation.demo://authorize

ru.navigation.demo://search?categoryId=182&query=mazda

presentation.start()

final class ViewController: UIViewController { @objc private func onAuthorizationButtonTap(sender: UIBarButtonItem) { let authorizationController = AuthorizationViewController()

let navigationController = UINavigationController( rootViewController: authorizationController ) presentViewController(navigationController, animated: true, completion: nil) } }

Что не так с этим кодом?final class ViewController: UIViewController { @objc private func onAuthorizationButtonTap(sender: UIBarButtonItem) { let authorizationController = AuthorizationViewController()

let navigationController = UINavigationController( rootViewController: authorizationController ) presentViewController(navigationController, animated: true, completion: nil) } }

final class ViewController: UIViewController { @objc private func onAuthorizationButtonTap(sender: UIBarButtonItem) { let authorizationController = AuthorizationViewController()

let navigationController = UINavigationController( rootViewController: authorizationController ) presentViewController(navigationController, animated: true, completion: nil) } }

final class ViewController: UIViewController { @objc private func onAuthorizationButtonTap(sender: UIBarButtonItem) { let authorizationController = AuthorizationViewController()

let navigationController = UINavigationController( rootViewController: authorizationController ) presentViewController(navigationController, animated: true, completion: nil) } }

final class ViewController: UIViewController { @objc private func onAuthorizationButtonTap(sender: UIBarButtonItem) { let authorizationController = AuthorizationViewController()

let navigationController = UINavigationController( rootViewController: authorizationController ) presentViewController(navigationController, animated: true, completion: nil) } }

Добавим слой Router

protocol RouterProtocol: class { func showAuthorization() }

Пробуем написать Routerprotocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? func showAuthorization() { let authorizationController = AuthorizationViewController() let router = AuthorizationRouterImpl() router.navigationController = navigationController router.rootViewController = authorizationController authorizationController.router = router navigationController?.pushViewController(authorizationController, animated: true) } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? func showAuthorization() { let authorizationController = AuthorizationViewController() let router = AuthorizationRouterImpl() router.navigationController = navigationController router.rootViewController = authorizationController authorizationController.router = router navigationController?.pushViewController(authorizationController, animated: true) } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? func showAuthorization() { let authorizationController = AuthorizationViewController() let router = AuthorizationRouterImpl() router.navigationController = navigationController router.rootViewController = authorizationController authorizationController.router = router navigationController?.pushViewController(authorizationController, animated: true) } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? func showAuthorization() { let authorizationController = AuthorizationViewController() let router = AuthorizationRouterImpl() router.navigationController = navigationController router.rootViewController = authorizationController authorizationController.router = router navigationController?.pushViewController(authorizationController, animated: true) } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? func showAuthorization() { let authorizationController = AuthorizationViewController() let router = AuthorizationRouterImpl() router.navigationController = navigationController router.rootViewController = authorizationController authorizationController.router = router navigationController?.pushViewController(authorizationController, animated: true) } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? func showAuthorization() { let authorizationController = AuthorizationViewController() let router = AuthorizationRouterImpl() router.navigationController = navigationController router.rootViewController = authorizationController authorizationController.router = router navigationController?.pushViewController(authorizationController, animated: true) } }

Добавим слой Assembly

Добавляем слой Assemblyprotocol AssemblyFactory: class { func authorizationAssembly() -> AuthorizationAssembly }

protocol AssemblyFactory: class { func authorizationAssembly() -> AuthorizationAssembly }

protocol AuthorizationAssembly: class { func module(navigationController: UINavigationController) -> UIViewController }

protocol AssemblyFactory: class { func authorizationAssembly() -> AuthorizationAssembly }

protocol AuthorizationAssembly: class { func module(navigationController: UINavigationController) -> UIViewController }

Пробуем написать Router c Assemblyprotocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? let assemblyFactory: AssemblyFactory

func showAuthorization() { let authorizationController = AuthorizationViewController() let router = AuthorizationRouterImpl() router.navigationController = navigationController router.rootViewController = authorizationController authorizationController.router = router navigationController?.pushViewController(authorizationController, animated: true) } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? let assemblyFactory: AssemblyFactory func showAuthorization() { let authorizationController = AuthorizationViewController() let router = AuthorizationRouterImpl() router.navigationController = navigationController router.rootViewController = authorizationController authorizationController.router = router navigationController?.pushViewController(authorizationController, animated: true) } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? let assemblyFactory: AssemblyFactory

func showAuthorization() { if let navigationController = navigationController { let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController) navigationController.pushViewController(authorizationController, animated: true) } } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? let assemblyFactory: AssemblyFactory func showAuthorization() { if let navigationController = navigationController { let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController) navigationController.pushViewController(authorizationController, animated: true) } } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? let assemblyFactory: AssemblyFactory func showAuthorization() { if let navigationController = navigationController { let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController) navigationController.pushViewController(authorizationController, animated: true) } } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? let assemblyFactory: AssemblyFactory func showAuthorization() { if let navigationController = navigationController { let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController) navigationController.pushViewController(authorizationController, animated: true) } } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? let assemblyFactory: AssemblyFactory func showAuthorization() { if let navigationController = navigationController { let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController) navigationController.pushViewController(authorizationController, animated: true) } } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? let assemblyFactory: AssemblyFactory func showAuthorization() { if let navigationController = navigationController { let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController) navigationController.pushViewController(authorizationController, animated: true) } } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? let assemblyFactory: AssemblyFactory func showAuthorization() { if let navigationController = navigationController { let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController) navigationController.pushViewController(authorizationController, animated: true) } } }

Добавим базовый класс

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: RouterProtocol { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? let assemblyFactory: AssemblyFactory func showAuthorization() { if let navigationController = navigationController { let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController) navigationController.pushViewController(authorizationController, animated: true) } } }

Пробуем написать Router c Assembly

Добавим базовый класс

Пробуем написать Router с базовым классомprotocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: BaseRouter, RouterProtocol { func showAuthorization() { if let navigationController = navigationController { let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController) navigationController.pushViewController(authorizationController, animated: true) } } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: BaseRouter, RouterProtocol { func showAuthorization() { if let navigationController = navigationController { let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController) navigationController.pushViewController(authorizationController, animated: true) } } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: BaseRouter, RouterProtocol { func showAuthorization() { if let navigationController = navigationController { let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController) navigationController.pushViewController(authorizationController, animated: true) } } }

Вынесем в базовый класс

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: BaseRouter, RouterProtocol { func showAuthorization() { if let navigationController = navigationController { let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController) navigationController.pushViewController(authorizationController, animated: true) } } }

Вынесем в базовый класс

Пробуем написать Router с базовым классомprotocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: BaseRouter, RouterProtocol { func showAuthorization() { pushViewControllerDerivedFrom { navigationController -> UIViewController in let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController)

return authorizationController } } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: BaseRouter, RouterProtocol { func showAuthorization() { pushViewControllerDerivedFrom { navigationController -> UIViewController in let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController)

return authorizationController } } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: BaseRouter, RouterProtocol { func showAuthorization() { pushViewControllerDerivedFrom { navigationController -> UIViewController in let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController)

return authorizationController } } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: BaseRouter, RouterProtocol { func showAuthorization() { pushViewControllerDerivedFrom { navigationController -> UIViewController in let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController)

return authorizationController } } }

protocol RouterProtocol: class { func showAuthorization() }

final class RouterProtocolImpl: BaseRouter, RouterProtocol { func showAuthorization() { presentModalViewControllerDerivedFrom { navigationController -> UIViewController in let authorizationAssembly = assemblyFactory.authorizationAssembly() let authorizationController = authorizationAssembly.module(navigationController)

return authorizationController } } }

Хороший фасад

Базовый классclass BaseRouter { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? func pushViewControllerDerivedFrom(deriveViewController: UINavigationController -> UIViewController) { if let navigationController = navigationController { let viewController = deriveViewController(navigationController) navigationController.pushViewController(viewController, animated: true) } } }

class BaseRouter { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? func pushViewControllerDerivedFrom(deriveViewController: UINavigationController -> UIViewController) { if let navigationController = navigationController { let viewController = deriveViewController(navigationController) navigationController.pushViewController(viewController, animated: true) } } }

Что делать с Master-detail модулем?

Для Master-detail нужен второй навигационный контроллер

Второй базовый классclass BaseMasterDetailRouter { weak var masterNavigationController: UINavigationController? weak var detailNavigationController: UINavigationController? weak var rootViewController: UIViewController? func pushMasterViewControllerDerivedFrom(deriveViewController: UINavigationController -> UIViewController) { if let navigationController = masterNavigationController { let viewController = deriveViewController(navigationController) navigationController.pushViewController(viewController, animated: true) } } }

class BaseMasterDetailRouter { weak var masterNavigationController: UINavigationController? weak var detailNavigationController: UINavigationController? weak var rootViewController: UIViewController? func pushMasterViewControllerDerivedFrom(deriveViewController: UINavigationController -> UIViewController) { if let navigationController = masterNavigationController { let viewController = deriveViewController(navigationController) navigationController.pushViewController(viewController, animated: true) } } }

class BaseMasterDetailRouter { weak var masterNavigationController: UINavigationController? weak var detailNavigationController: UINavigationController? weak var rootViewController: UIViewController? func pushMasterViewControllerDerivedFrom(deriveViewController: UINavigationController -> UIViewController) { if let navigationController = masterNavigationController { let viewController = deriveViewController(navigationController) navigationController.pushViewController(viewController, animated: true) } } }

class BaseMasterDetailRouter { weak var masterNavigationController: UINavigationController? weak var detailNavigationController: UINavigationController? weak var rootViewController: UIViewController? func pushMasterViewControllerDerivedFrom(deriveViewController: UINavigationController -> UIViewController) { if let navigationController = masterNavigationController { let viewController = deriveViewController(navigationController) navigationController.pushViewController(viewController, animated: true) } } }

class BaseMasterDetailRouter { weak var masterNavigationController: UINavigationController? weak var detailNavigationController: UINavigationController? weak var rootViewController: UIViewController? func pushMasterViewControllerDerivedFrom(deriveViewController: UINavigationController -> UIViewController) { if let navigationController = masterNavigationController { let viewController = deriveViewController(navigationController) navigationController.pushViewController(viewController, animated: true) } } }

class BaseMasterDetailRouter { weak var masterNavigationController: UINavigationController? weak var detailNavigationController: UINavigationController? weak var rootViewController: UIViewController? func pushMasterViewControllerDerivedFrom(deriveViewController: UINavigationController -> UIViewController) { if let navigationController = masterNavigationController { let viewController = deriveViewController(navigationController) navigationController.pushViewController(viewController, animated: true) } } }

Добавим структурку для передачи

всех нужных роутерупараметров

Добавляем структуркиstruct RouterSeed { let navigationController: UINavigationController }

struct RouterSeed { let navigationController: UINavigationController }

struct MasterDetailRouterSeed { let masterNavigationController: UINavigationController let detailNavigationController: UINavigationController }

Теперь рефакторитьбудет удобней

Улучшенный фасад pushViewControllerDerivedFrom { routerSeed -> UIViewController in pushViewControllerDerivedFrom { routerSeed -> UIViewController in

pushMasterViewControllerDerivedFrom { routerSeed -> UIViewController in

pushViewControllerDerivedFrom { routerSeed -> UIViewController in

pushMasterViewControllerDerivedFrom { routerSeed -> UIViewController in

setDetailViewControllerDerivedFrom { routerSeed -> UIViewController in

pushViewControllerDerivedFrom { routerSeed -> UIViewController in

pushMasterViewControllerDerivedFrom { routerSeed -> UIViewController in

setDetailViewControllerDerivedFrom { routerSeed -> UIViewController in

presentModalNavigationControllerWithRootViewControllerDerivedFrom { routerSeed -> UIViewController in

pushViewControllerDerivedFrom { routerSeed -> UIViewController in

pushMasterViewControllerDerivedFrom { routerSeed -> UIViewController in

setDetailViewControllerDerivedFrom { routerSeed -> UIViewController in

presentModalNavigationControllerWithRootViewControllerDerivedFrom { routerSeed -> UIViewController in

presentPopoverWithNavigationControllerFromBarButtonItem(buttonItem) { routerSeed -> UIViewController in

So far, so goodМодуль авторизации из всех модулейПоддержка DeepLinksBonus: (Push’ы, Alert’ы)

Нужно научить базовые роутеры

искать верхний модуль

So far, so good, но что если

Поиск верхнего модуляprotocol TopViewControllerFinder: class { func topViewController() -> UIViewController? }

final class TopViewControllerFinderImpl: TopViewControllerFinder { weak var rootViewController: UIViewController? }

final class TopViewControllerFinderImpl: TopViewControllerFinder { weak var rootViewController: UIViewController? func topViewController() -> UIViewController? { var result = rootViewController while let presentedViewController = result?.presentedViewController { result = presentedViewController } return result } }

final class TopViewControllerFinderImpl: TopViewControllerFinder { weak var rootViewController: UIViewController? func topViewController() -> UIViewController? { var result = rootViewController while let presentedViewController = result?.presentedViewController { result = presentedViewController } return result } }

final class TopViewControllerFinderImpl: TopViewControllerFinder { weak var rootViewController: UIViewController? func topViewController() -> UIViewController? { var result = rootViewController while let presentedViewController = result?.presentedViewController { result = presentedViewController } if let selectedTabController = (result as? UITabBarController)?.selectedViewController { if let detailController = (selectedTabController as? UISplitViewController)?.viewControllers.last { if let detailNavigationController = detailController as? UINavigationController { result = detailNavigationController.viewControllers.last } else { result = detailController } } else { result = selectedTabController } } return result } }

final class TopViewControllerFinderImpl: TopViewControllerFinder { weak var rootViewController: UIViewController? func topViewController() -> UIViewController? { var result = rootViewController while let presentedViewController = result?.presentedViewController { result = presentedViewController } if let selectedTabController = (result as? UITabBarController)?.selectedViewController { if let detailController = (selectedTabController as? UISplitViewController)?.viewControllers.last { if let detailNavigationController = detailController as? UINavigationController { result = detailNavigationController.viewControllers.last } else { result = detailController } } else { result = selectedTabController } } return result } }

Нужна своя система навигации

Зачем нужна своя система навигации?Хранение истории переходов

Поддержка Third-party контроллеров

Bonus: проверка, что модуль был на экранеBonus: расстояние между модулями

Поиск верхнего модуля

Обертка над UIKit

Реакция на изменение SDK

Свежий взгляд на базовый Routerclass BaseRouter { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? }

class BaseRouter { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? }

Нужно абстрагировать Router от UIKit

Не у каждого роутера будет UINavigationController

Код вида .pushViewController() сильно завязывает Router на UIKit

Для каждого ThirdPartyNavigationController нужна будет своя пара базовых Router’ов

Абстрагируем Router от UIKit

protocol TransitionsHandler: class { }

typealias TransitionId = String Идентификатор перехода

Возвращение на модуль

Закрытие модуля

Отменяемый переходprotocol TransitionsHandler: class { func performTransition(context context: PresentationTransitionContext) }

Неотменяемый переход

protocol TransitionsHandler: class { func performTransition(context context: PresentationTransitionContext)

func resetWithTransition(context context: ResettingTransitionContext) }

protocol TransitionsHandler: class { func performTransition(context context: PresentationTransitionContext)

func resetWithTransition(context context: ResettingTransitionContext)

func undoTransitionsAfter(transitionId transitionId: TransitionId) }

protocol TransitionsHandler: class { func performTransition(context context: PresentationTransitionContext)

func resetWithTransition(context context: ResettingTransitionContext)

func undoTransitionsAfter(transitionId transitionId: TransitionId)

func undoTransitionWith(transitionId transitionId: TransitionId) }

Router общается с обработчиком переходов

Обработчик переходов оборачивает UIViewController

Виды модулей Анимирующие

AnimatingTransitionsHandlerImpl

NavigationTransitionsHandlerImpl

ContainerTransitionsHandlerImpl

SplitViewTransitionsHandlerImpl

TabBarTransitionsHandlerImpl

Контейнеры

pushViewController(_:animated:)presentViewController(_:animated:completion:)

visibleAnimatingTransitionsHandlers()allAnimatingTransitionsHandlers()

Легко добавить Third-party контроллер

class BaseRouter { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? }

Новый базовый Routerclass BaseRouter { let transitionsHandlerBox: TransitionsHandlerBox // weak var navigationController: UINavigationController? let transitionId: TransitionId // weak var rootViewController: UIViewController? }

class BaseRouter { let transitionsHandlerBox: TransitionsHandlerBox // weak var navigationController: UINavigationController? let transitionId: TransitionId // weak var rootViewController: UIViewController? }

class BaseRouter { let transitionsHandlerBox: TransitionsHandlerBox // weak var navigationController: UINavigationController? let transitionId: TransitionId // weak var rootViewController: UIViewController? }

class BaseRouter { let transitionsHandlerBox: TransitionsHandlerBox // weak var navigationController: UINavigationController? let transitionId: TransitionId // weak var rootViewController: UIViewController? }

enum TransitionsHandlerBox { case Animating(AnimatingTransitionsHandlerImpl) case Containing(ContainingTransitionsHandlerImpl) }

Такой Router можно использовать

с любым UIViewController’ом

Схема выполнения отменяемых переходов

Transitions handlerbox

выполни отменяемый переход

Router

presentation context

transitions handler

box Transitions Coordinator

Top animating transitions handler запусти

анимацию

presentation context

Взглянем еще раз на новый базовый Routerclass BaseRouter { let transitionsHandlerBox: TransitionsHandlerBox // weak var navigationController: UINavigationController? let transitionId: TransitionId // weak var rootViewController: UIViewController? }

Нужна ссылка на обработчика переходов, показавшего модуль Router’а

class BaseRouter { let transitionsHandlerBox: TransitionsHandlerBox // weak var navigationController: UINavigationController? let transitionId: TransitionId // weak var rootViewController: UIViewController? weak var presentingTransitionsHandler: TransitionsHandler? }

class BaseRouter { let transitionsHandlerBox: TransitionsHandlerBox // weak var navigationController: UINavigationController? let transitionId: TransitionId // weak var rootViewController: UIViewController? weak var presentingTransitionsHandler: TransitionsHandler? }

Теперь Router может закрывать свой модуль

Навигационная связь

Router 1

transition id 1

Transitions handler 1

Transitions handler 2

presenting transitions

handler

Вернись на модуль 1

Закрой модуль 2

Router 2

transition id 2

Что лучше: “Вернись на модуль 1”

или “Закрой модуль 2”

?

Flow

Фильтр Города

Выход из Flow

Фильтр Города

Router

закрой модуль городов

Усложненный flow

Фильтр Регионы Города

Выход из усложненного Flow

Фильтрмодуль регионов закончил

ГородаРегионымодуль городов закончил

Router

вернись на наш модуль

“Вернись на модуль” гибче, чем

“Закрой модуль”

Слой Router

Предварительные итоги

Подходы к выполнению обратных переходовПоддержка DeepLinks

Слой AssemblyБазовые классы Router, поддержка iPad, master-detailПростой Router (фасад, абстрация от UIKit)

demo.start()

Один UIViewController, много Router’ов

Выводы по демо

Проверка наличия модуля в истории (Авторизация)

Проверка isIpad()Поиск верхнего модуля (Авторизация, DeepLink’и, Push’ы)

Проверка модулей на дубликаты (🍌, 🍏)Аниматоры переходов

Проверка isIpad()

Выделите слой Router (определять стиль перехода)

Общие советы

Используйте “Вернись на модуль” вместо “Закрой модуль”

Выделите слой AssemblyАбстрагируйте Router от UIKit

Вынесите логику принятия решений в отдельный слойОписывайте переходы в декларативном стиле

One more thing

https://github.com/avito-tech/Marshroute

Исходники Докладчик: Юсипов Тимурhttps://vk.com/ma3tsa

[email protected]

[email protected]

fizmatchelskype

personal mail

work mail

vk

Marshroute

Спасибо за внимание!

presentation.finish()

https://github.com/avito-tech/Marshroute/tree/master/Example

Демо