SlideShare a Scribd company logo
1 of 35
Download to read offline
Удобный и расширяемый
роутинг в iOS-приложении
Юсипов Тимур
Историческая справка
(2011 .. 2014) Outsource
(2014 .. 2016) In-house
VIPER
121 662
70 187
lines of code
22.09.2016
29.05.2016
39 051
24 334
lines 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()
}
Пробуем написать Router
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)
}
}
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
Добавляем слой Assembly
protocol 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 Assembly
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() {
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 inpushViewControllerDerivedFrom { 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
Модуль авторизации из всех модулей
Поддержка DeepLinks
Bonus: (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
Свежий взгляд на базовый Router
class 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?
}
Новый базовый Router
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?
}
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 handler
box
выполни
отменяемый
переход
Router
presentation
context
transitions
handler
box Transitions
Coordinator
Top animating
transitions handlerзапусти
анимацию
presentation
context
Взглянем еще раз на новый базовый Router
class 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
tyusipov@avito.ru
ykylele@gmail.com
fizmatchelskype
personal mail
work mail
vk
Marshroute
Спасибо за внимание!
presentation.finish()
https://github.com/avito-tech/Marshroute/tree/master/Example
Демо

More Related Content

Viewers also liked

Сравнение форматов и библиотек сериализации / Антон Рыжов (Qrator Labs)
Сравнение форматов и библиотек сериализации / Антон Рыжов (Qrator Labs)Сравнение форматов и библиотек сериализации / Антон Рыжов (Qrator Labs)
Сравнение форматов и библиотек сериализации / Антон Рыжов (Qrator Labs)
Ontico
 
"Секционирование без границ" Ильдар Мусин (Postgres Professional)
"Секционирование без границ" Ильдар Мусин (Postgres Professional)"Секционирование без границ" Ильдар Мусин (Postgres Professional)
"Секционирование без границ" Ильдар Мусин (Postgres Professional)
AvitoTech
 
noBackend, или Как выжить в эпоху толстеющих клиентов / Самохвалов Николай
noBackend, или Как выжить в эпоху толстеющих клиентов / Самохвалов НиколайnoBackend, или Как выжить в эпоху толстеющих клиентов / Самохвалов Николай
noBackend, или Как выжить в эпоху толстеющих клиентов / Самохвалов Николай
Ontico
 

Viewers also liked (20)

"Favicon на стероидах" Александр Амосов (Avito)
"Favicon на стероидах" Александр Амосов (Avito)"Favicon на стероидах" Александр Амосов (Avito)
"Favicon на стероидах" Александр Амосов (Avito)
 
"Деплой кода процедур" Мурат Кабилов (Avito)
"Деплой кода процедур" Мурат Кабилов (Avito)"Деплой кода процедур" Мурат Кабилов (Avito)
"Деплой кода процедур" Мурат Кабилов (Avito)
 
"Подходы, используемые в разработке iOS-клиента Viber" Кирилл Лашкевич (Viber)
"Подходы, используемые в разработке iOS-клиента Viber" Кирилл Лашкевич (Viber)"Подходы, используемые в разработке iOS-клиента Viber" Кирилл Лашкевич (Viber)
"Подходы, используемые в разработке iOS-клиента Viber" Кирилл Лашкевич (Viber)
 
"Опыт использования Sphinx в Ozon.ru" Игорь Чакрыгин (OZON.RU)
"Опыт использования Sphinx в Ozon.ru" Игорь Чакрыгин (OZON.RU)"Опыт использования Sphinx в Ozon.ru" Игорь Чакрыгин (OZON.RU)
"Опыт использования Sphinx в Ozon.ru" Игорь Чакрыгин (OZON.RU)
 
"Kotlin и rx в android" Дмитрий Воронин (Avito)
"Kotlin и rx в android" Дмитрий Воронин  (Avito)"Kotlin и rx в android" Дмитрий Воронин  (Avito)
"Kotlin и rx в android" Дмитрий Воронин (Avito)
 
"Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)
"Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)"Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)
"Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)
 
"Опыт участия в Microsoft Malware Classification Challenge" Михаил Трофимов ...
"Опыт участия в Microsoft Malware Classification Challenge"  Михаил Трофимов ..."Опыт участия в Microsoft Malware Classification Challenge"  Михаил Трофимов ...
"Опыт участия в Microsoft Malware Classification Challenge" Михаил Трофимов ...
 
"Basis.js - Production Ready SPA Framework" Сергей Мелюков (Avito)
"Basis.js - Production Ready SPA Framework" Сергей Мелюков (Avito)"Basis.js - Production Ready SPA Framework" Сергей Мелюков (Avito)
"Basis.js - Production Ready SPA Framework" Сергей Мелюков (Avito)
 
Сравнение форматов и библиотек сериализации / Антон Рыжов (Qrator Labs)
Сравнение форматов и библиотек сериализации / Антон Рыжов (Qrator Labs)Сравнение форматов и библиотек сериализации / Антон Рыжов (Qrator Labs)
Сравнение форматов и библиотек сериализации / Антон Рыжов (Qrator Labs)
 
"Быстрое внедрение Sphinx на примере проекта Фоксфорд.Учебник" Антон Ковалёв ...
"Быстрое внедрение Sphinx на примере проекта Фоксфорд.Учебник" Антон Ковалёв ..."Быстрое внедрение Sphinx на примере проекта Фоксфорд.Учебник" Антон Ковалёв ...
"Быстрое внедрение Sphinx на примере проекта Фоксфорд.Учебник" Антон Ковалёв ...
 
“Атличнаи дивчачьи каньки”: исправляем ошибки. Андрей Смирнов (Avito)
“Атличнаи дивчачьи каньки”: исправляем ошибки. Андрей Смирнов (Avito)“Атличнаи дивчачьи каньки”: исправляем ошибки. Андрей Смирнов (Avito)
“Атличнаи дивчачьи каньки”: исправляем ошибки. Андрей Смирнов (Avito)
 
"DeepLink’и в Avito" Артём Разинов (Avito)
"DeepLink’и в Avito" Артём Разинов (Avito)"DeepLink’и в Avito" Артём Разинов (Avito)
"DeepLink’и в Avito" Артём Разинов (Avito)
 
"REST-SOA-View-Controller или Проектирование сервис-ориентированной системы с...
"REST-SOA-View-Controller или Проектирование сервис-ориентированной системы с..."REST-SOA-View-Controller или Проектирование сервис-ориентированной системы с...
"REST-SOA-View-Controller или Проектирование сервис-ориентированной системы с...
 
"Секционирование без границ" Ильдар Мусин (Postgres Professional)
"Секционирование без границ" Ильдар Мусин (Postgres Professional)"Секционирование без границ" Ильдар Мусин (Postgres Professional)
"Секционирование без границ" Ильдар Мусин (Postgres Professional)
 
"Распознавание марки и модели автомашин на изображениях" Евгений Нижибицкий (...
"Распознавание марки и модели автомашин на изображениях" Евгений Нижибицкий (..."Распознавание марки и модели автомашин на изображениях" Евгений Нижибицкий (...
"Распознавание марки и модели автомашин на изображениях" Евгений Нижибицкий (...
 
noBackend, или Как выжить в эпоху толстеющих клиентов / Самохвалов Николай
noBackend, или Как выжить в эпоху толстеющих клиентов / Самохвалов НиколайnoBackend, или Как выжить в эпоху толстеющих клиентов / Самохвалов Николай
noBackend, или Как выжить в эпоху толстеющих клиентов / Самохвалов Николай
 
"Погружение в Robolectric" Дмитрий Костырев (Avito)
"Погружение в Robolectric"  Дмитрий Костырев (Avito)"Погружение в Robolectric"  Дмитрий Костырев (Avito)
"Погружение в Robolectric" Дмитрий Костырев (Avito)
 
"Распознавание категории объявления по изображениям" Артур Кузин (МФТИ)
"Распознавание категории объявления по изображениям" Артур Кузин (МФТИ)"Распознавание категории объявления по изображениям" Артур Кузин (МФТИ)
"Распознавание категории объявления по изображениям" Артур Кузин (МФТИ)
 
"Успеть за 100 миллисекунд: контекстная реклама на Sphinx" Дмитрий Хасанов (...
"Успеть за 100 миллисекунд: контекстная реклама на Sphinx" Дмитрий Хасанов  (..."Успеть за 100 миллисекунд: контекстная реклама на Sphinx" Дмитрий Хасанов  (...
"Успеть за 100 миллисекунд: контекстная реклама на Sphinx" Дмитрий Хасанов (...
 
"Kotlin для Android: 1.0 и далее" Дмитрий Жемеров (JetBrains)
"Kotlin для Android: 1.0 и далее" Дмитрий Жемеров (JetBrains)"Kotlin для Android: 1.0 и далее" Дмитрий Жемеров (JetBrains)
"Kotlin для Android: 1.0 и далее" Дмитрий Жемеров (JetBrains)
 

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

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

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

Удобный и расширяемый роутинг в iOS-приложении / Тимур Юсипов (Avito)
Удобный и расширяемый роутинг в iOS-приложении / Тимур Юсипов (Avito)Удобный и расширяемый роутинг в iOS-приложении / Тимур Юсипов (Avito)
Удобный и расширяемый роутинг в iOS-приложении / Тимур Юсипов (Avito)
 
What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018
 
Developing ASP.NET Applications Using the Model View Controller Pattern
Developing ASP.NET Applications Using the Model View Controller PatternDeveloping ASP.NET Applications Using the Model View Controller Pattern
Developing ASP.NET Applications Using the Model View Controller Pattern
 
Angular js routing options
Angular js routing optionsAngular js routing options
Angular js routing options
 
Workshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte IIWorkshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte II
 
Exemplo de Fluxograma de Arquitetura aplicativo
Exemplo de Fluxograma de Arquitetura aplicativoExemplo de Fluxograma de Arquitetura aplicativo
Exemplo de Fluxograma de Arquitetura aplicativo
 
Do iOS Presentation - Mobile app architectures
Do iOS Presentation - Mobile app architecturesDo iOS Presentation - Mobile app architectures
Do iOS Presentation - Mobile app architectures
 
The Next Step in AS3 Framework Evolution - FITC Amsterdam 2013
The Next Step in AS3 Framework Evolution - FITC Amsterdam 2013The Next Step in AS3 Framework Evolution - FITC Amsterdam 2013
The Next Step in AS3 Framework Evolution - FITC Amsterdam 2013
 
MBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&CoMBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&Co
 
Foomo / Zugspitze Presentation
Foomo / Zugspitze PresentationFoomo / Zugspitze Presentation
Foomo / Zugspitze Presentation
 
How to instantiate any view controller for free
How to instantiate any view controller for freeHow to instantiate any view controller for free
How to instantiate any view controller for free
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern
 
ITT 2014 - Peter Steinberger - Architecting Modular Codebases
ITT 2014 - Peter Steinberger - Architecting Modular CodebasesITT 2014 - Peter Steinberger - Architecting Modular Codebases
ITT 2014 - Peter Steinberger - Architecting Modular Codebases
 
Daggerate your code - Write your own annotation processor
Daggerate your code - Write your own annotation processorDaggerate your code - Write your own annotation processor
Daggerate your code - Write your own annotation processor
 
알아보자 Dependency Injection과 Deli
알아보자 Dependency Injection과 Deli알아보자 Dependency Injection과 Deli
알아보자 Dependency Injection과 Deli
 
The Next Step in AS3 Framework Evolution
The Next Step in AS3 Framework EvolutionThe Next Step in AS3 Framework Evolution
The Next Step in AS3 Framework Evolution
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingPractical Protocol-Oriented-Programming
Practical Protocol-Oriented-Programming
 
Practialpop 160510130818
Practialpop 160510130818Practialpop 160510130818
Practialpop 160510130818
 
MCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in Swift
MCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in SwiftMCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in Swift
MCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in Swift
 
Migrating Objective-C to Swift
Migrating Objective-C to SwiftMigrating Objective-C to Swift
Migrating Objective-C to Swift
 

More from AvitoTech

Добиваемся эффективности каждого из 9000+ UI-тестов - Максим Сахаров (Tutu.ru)
Добиваемся эффективности каждого из 9000+ UI-тестов - Максим Сахаров (Tutu.ru)Добиваемся эффективности каждого из 9000+ UI-тестов - Максим Сахаров (Tutu.ru)
Добиваемся эффективности каждого из 9000+ UI-тестов - Максим Сахаров (Tutu.ru)
AvitoTech
 

More from AvitoTech (20)

Сегментация изображений на острие науки (Евгений Нижибицкий, Rambler&Co)
Сегментация изображений на острие науки (Евгений Нижибицкий, Rambler&Co)Сегментация изображений на острие науки (Евгений Нижибицкий, Rambler&Co)
Сегментация изображений на острие науки (Евгений Нижибицкий, Rambler&Co)
 
Применение компьютерного зрения для анализа спортивных соревнований (Николай ...
Применение компьютерного зрения для анализа спортивных соревнований (Николай ...Применение компьютерного зрения для анализа спортивных соревнований (Николай ...
Применение компьютерного зрения для анализа спортивных соревнований (Николай ...
 
Распознавание лиц с помощью глубоких нейронных сетей (Сергей Миляев, VisionLabs)
Распознавание лиц с помощью глубоких нейронных сетей (Сергей Миляев, VisionLabs)Распознавание лиц с помощью глубоких нейронных сетей (Сергей Миляев, VisionLabs)
Распознавание лиц с помощью глубоких нейронных сетей (Сергей Миляев, VisionLabs)
 
AvitoNet: сервис компьютерного зрения в Avito (Артур Кузин, Avito)
AvitoNet: сервис компьютерного зрения в Avito (Артур Кузин, Avito)AvitoNet: сервис компьютерного зрения в Avito (Артур Кузин, Avito)
AvitoNet: сервис компьютерного зрения в Avito (Артур Кузин, Avito)
 
Yandex Tank - Арсений Фомченко
Yandex Tank - Арсений ФомченкоYandex Tank - Арсений Фомченко
Yandex Tank - Арсений Фомченко
 
Migro - Юрий Богомолов
Migro - Юрий БогомоловMigro - Юрий Богомолов
Migro - Юрий Богомолов
 
TableKit - Максим Соколов
TableKit - Максим СоколовTableKit - Максим Соколов
TableKit - Максим Соколов
 
Jsonwire Grid - Михаил Подцерковский (Avito)
Jsonwire Grid - Михаил Подцерковский (Avito)Jsonwire Grid - Михаил Подцерковский (Avito)
Jsonwire Grid - Михаил Подцерковский (Avito)
 
SimplePEG - Алексей Охрименко
SimplePEG - Алексей ОхрименкоSimplePEG - Алексей Охрименко
SimplePEG - Алексей Охрименко
 
Как перестать бояться и начать контрибьютить - Алексей Кудрявцев
 Как перестать бояться и начать контрибьютить - Алексей Кудрявцев Как перестать бояться и начать контрибьютить - Алексей Кудрявцев
Как перестать бояться и начать контрибьютить - Алексей Кудрявцев
 
"Анонимизация фото с помощью Vision", Хомутников Тимофей, Avito
"Анонимизация фото с помощью Vision",  Хомутников Тимофей, Avito"Анонимизация фото с помощью Vision",  Хомутников Тимофей, Avito
"Анонимизация фото с помощью Vision", Хомутников Тимофей, Avito
 
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
 
"ARKit в приложении Афиша Рестораны”, Меджлумян Самвел, Антышев Дмитрий, Ramb...
"ARKit в приложении Афиша Рестораны”, Меджлумян Самвел, Антышев Дмитрий, Ramb..."ARKit в приложении Афиша Рестораны”, Меджлумян Самвел, Антышев Дмитрий, Ramb...
"ARKit в приложении Афиша Рестораны”, Меджлумян Самвел, Антышев Дмитрий, Ramb...
 
ASO for iOS 11
ASO for iOS 11ASO for iOS 11
ASO for iOS 11
 
Добиваемся эффективности каждого из 9000+ UI-тестов - Максим Сахаров (Tutu.ru)
Добиваемся эффективности каждого из 9000+ UI-тестов - Максим Сахаров (Tutu.ru)Добиваемся эффективности каждого из 9000+ UI-тестов - Максим Сахаров (Tutu.ru)
Добиваемся эффективности каждого из 9000+ UI-тестов - Максим Сахаров (Tutu.ru)
 
Проблемы управления тестами, или Что мешает создавать дешевые и полезные тест...
Проблемы управления тестами, или Что мешает создавать дешевые и полезные тест...Проблемы управления тестами, или Что мешает создавать дешевые и полезные тест...
Проблемы управления тестами, или Что мешает создавать дешевые и полезные тест...
 
Запускаем тесты в Continuous Integration - Сергей Пак (JetBrains)
Запускаем тесты в Continuous Integration - Сергей Пак (JetBrains)Запускаем тесты в Continuous Integration - Сергей Пак (JetBrains)
Запускаем тесты в Continuous Integration - Сергей Пак (JetBrains)
 
Векторы развития систем автоматизации тестирования - Дмитрий Химион (Avito)
Векторы развития систем автоматизации тестирования - Дмитрий Химион (Avito)Векторы развития систем автоматизации тестирования - Дмитрий Химион (Avito)
Векторы развития систем автоматизации тестирования - Дмитрий Химион (Avito)
 
Прокачиваем WebDriverAgent, или Как тестировать iOS-приложения после ядерного...
Прокачиваем WebDriverAgent, или Как тестировать iOS-приложения после ядерного...Прокачиваем WebDriverAgent, или Как тестировать iOS-приложения после ядерного...
Прокачиваем WebDriverAgent, или Как тестировать iOS-приложения после ядерного...
 
Конкурс Авито-2017 - Решение 2ое место - Василий Рубцов
Конкурс Авито-2017 - Решение 2ое место - Василий РубцовКонкурс Авито-2017 - Решение 2ое место - Василий Рубцов
Конкурс Авито-2017 - Решение 2ое место - Василий Рубцов
 

Recently uploaded

➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men 🔝mehsana🔝 Escorts...
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men  🔝mehsana🔝   Escorts...➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men  🔝mehsana🔝   Escorts...
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men 🔝mehsana🔝 Escorts...
nirzagarg
 
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
dharasingh5698
 
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
nilamkumrai
 
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
Call Girls In Delhi Whatsup 9873940964 Enjoy Unlimited Pleasure
 
( Pune ) VIP Pimpri Chinchwad Call Girls 🎗️ 9352988975 Sizzling | Escorts | G...
( Pune ) VIP Pimpri Chinchwad Call Girls 🎗️ 9352988975 Sizzling | Escorts | G...( Pune ) VIP Pimpri Chinchwad Call Girls 🎗️ 9352988975 Sizzling | Escorts | G...
( Pune ) VIP Pimpri Chinchwad Call Girls 🎗️ 9352988975 Sizzling | Escorts | G...
nilamkumrai
 
💚😋 Bilaspur Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋
💚😋 Bilaspur Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋💚😋 Bilaspur Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋
💚😋 Bilaspur Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋
nirzagarg
 
在线制作约克大学毕业证(yu毕业证)在读证明认证可查
在线制作约克大学毕业证(yu毕业证)在读证明认证可查在线制作约克大学毕业证(yu毕业证)在读证明认证可查
在线制作约克大学毕业证(yu毕业证)在读证明认证可查
ydyuyu
 
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
@Chandigarh #call #Girls 9053900678 @Call #Girls in @Punjab 9053900678
 

Recently uploaded (20)

Ganeshkhind ! Call Girls Pune - 450+ Call Girl Cash Payment 8005736733 Neha T...
Ganeshkhind ! Call Girls Pune - 450+ Call Girl Cash Payment 8005736733 Neha T...Ganeshkhind ! Call Girls Pune - 450+ Call Girl Cash Payment 8005736733 Neha T...
Ganeshkhind ! Call Girls Pune - 450+ Call Girl Cash Payment 8005736733 Neha T...
 
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men 🔝mehsana🔝 Escorts...
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men  🔝mehsana🔝   Escorts...➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men  🔝mehsana🔝   Escorts...
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men 🔝mehsana🔝 Escorts...
 
(INDIRA) Call Girl Pune Call Now 8250077686 Pune Escorts 24x7
(INDIRA) Call Girl Pune Call Now 8250077686 Pune Escorts 24x7(INDIRA) Call Girl Pune Call Now 8250077686 Pune Escorts 24x7
(INDIRA) Call Girl Pune Call Now 8250077686 Pune Escorts 24x7
 
Top Rated Pune Call Girls Daund ⟟ 6297143586 ⟟ Call Me For Genuine Sex Servi...
Top Rated  Pune Call Girls Daund ⟟ 6297143586 ⟟ Call Me For Genuine Sex Servi...Top Rated  Pune Call Girls Daund ⟟ 6297143586 ⟟ Call Me For Genuine Sex Servi...
Top Rated Pune Call Girls Daund ⟟ 6297143586 ⟟ Call Me For Genuine Sex Servi...
 
Real Escorts in Al Nahda +971524965298 Dubai Escorts Service
Real Escorts in Al Nahda +971524965298 Dubai Escorts ServiceReal Escorts in Al Nahda +971524965298 Dubai Escorts Service
Real Escorts in Al Nahda +971524965298 Dubai Escorts Service
 
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
 
Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...
Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...
Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...
 
All Time Service Available Call Girls Mg Road 👌 ⏭️ 6378878445
All Time Service Available Call Girls Mg Road 👌 ⏭️ 6378878445All Time Service Available Call Girls Mg Road 👌 ⏭️ 6378878445
All Time Service Available Call Girls Mg Road 👌 ⏭️ 6378878445
 
VIP Model Call Girls NIBM ( Pune ) Call ON 8005736733 Starting From 5K to 25K...
VIP Model Call Girls NIBM ( Pune ) Call ON 8005736733 Starting From 5K to 25K...VIP Model Call Girls NIBM ( Pune ) Call ON 8005736733 Starting From 5K to 25K...
VIP Model Call Girls NIBM ( Pune ) Call ON 8005736733 Starting From 5K to 25K...
 
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
 
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
 
( Pune ) VIP Pimpri Chinchwad Call Girls 🎗️ 9352988975 Sizzling | Escorts | G...
( Pune ) VIP Pimpri Chinchwad Call Girls 🎗️ 9352988975 Sizzling | Escorts | G...( Pune ) VIP Pimpri Chinchwad Call Girls 🎗️ 9352988975 Sizzling | Escorts | G...
( Pune ) VIP Pimpri Chinchwad Call Girls 🎗️ 9352988975 Sizzling | Escorts | G...
 
💚😋 Bilaspur Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋
💚😋 Bilaspur Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋💚😋 Bilaspur Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋
💚😋 Bilaspur Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋
 
在线制作约克大学毕业证(yu毕业证)在读证明认证可查
在线制作约克大学毕业证(yu毕业证)在读证明认证可查在线制作约克大学毕业证(yu毕业证)在读证明认证可查
在线制作约克大学毕业证(yu毕业证)在读证明认证可查
 
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
 
Trump Diapers Over Dems t shirts Sweatshirt
Trump Diapers Over Dems t shirts SweatshirtTrump Diapers Over Dems t shirts Sweatshirt
Trump Diapers Over Dems t shirts Sweatshirt
 
VIP Model Call Girls Hadapsar ( Pune ) Call ON 9905417584 Starting High Prof...
VIP Model Call Girls Hadapsar ( Pune ) Call ON 9905417584 Starting  High Prof...VIP Model Call Girls Hadapsar ( Pune ) Call ON 9905417584 Starting  High Prof...
VIP Model Call Girls Hadapsar ( Pune ) Call ON 9905417584 Starting High Prof...
 
Pirangut | Call Girls Pune Phone No 8005736733 Elite Escort Service Available...
Pirangut | Call Girls Pune Phone No 8005736733 Elite Escort Service Available...Pirangut | Call Girls Pune Phone No 8005736733 Elite Escort Service Available...
Pirangut | Call Girls Pune Phone No 8005736733 Elite Escort Service Available...
 
Hire↠Young Call Girls in Tilak nagar (Delhi) ☎️ 9205541914 ☎️ Independent Esc...
Hire↠Young Call Girls in Tilak nagar (Delhi) ☎️ 9205541914 ☎️ Independent Esc...Hire↠Young Call Girls in Tilak nagar (Delhi) ☎️ 9205541914 ☎️ Independent Esc...
Hire↠Young Call Girls in Tilak nagar (Delhi) ☎️ 9205541914 ☎️ Independent Esc...
 
WhatsApp 📞 8448380779 ✅Call Girls In Mamura Sector 66 ( Noida)
WhatsApp 📞 8448380779 ✅Call Girls In Mamura Sector 66 ( Noida)WhatsApp 📞 8448380779 ✅Call Girls In Mamura Sector 66 ( Noida)
WhatsApp 📞 8448380779 ✅Call Girls In Mamura Sector 66 ( Noida)
 

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

  • 1. Удобный и расширяемый роутинг в iOS-приложении Юсипов Тимур
  • 2. Историческая справка (2011 .. 2014) Outsource (2014 .. 2016) In-house VIPER 121 662 70 187 lines of code 22.09.2016 29.05.2016 39 051 24 334 lines of code
  • 3. Введем понятие Router Как будет проходить презентация? Рассмотрим необычные задачи Routing’а Попробуем написать Router Адаптируем Router под iPad Сформулируем общие архитектурные правила Routing’а Посмотрим демо
  • 4. Что такое Module? Router View Controller MVC Model View View Interactor Router Presenter Entity VIPER RouterRouter View Controller MVC Model View View InteractorPresenter Entity VIPER
  • 7. 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
  • 8. protocol RouterProtocol: class { func showAuthorization() } Пробуем написать Router 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) } } 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
  • 9. Добавляем слой Assembly protocol 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 }
  • 10. Пробуем написать Router c Assembly 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() { 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) } } } Добавим базовый класс
  • 11. 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) } } } Вынесем в базовый класс
  • 12. 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 } } } Хороший фасад
  • 13. Базовый класс 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 нужен второй навигационный контроллер
  • 14. Второй базовый класс 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) } } } Добавим структурку для передачи всех нужных роутеру параметров
  • 15. Добавляем структурки struct RouterSeed { let navigationController: UINavigationController } struct RouterSeed { let navigationController: UINavigationController } struct MasterDetailRouterSeed { let masterNavigationController: UINavigationController let detailNavigationController: UINavigationController } Теперь рефакторить будет удобней
  • 16. Улучшенный фасад pushViewControllerDerivedFrom { routerSeed -> UIViewController inpushViewControllerDerivedFrom { 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
  • 17. So far, so good Модуль авторизации из всех модулей Поддержка DeepLinks Bonus: (Push’ы, Alert’ы) Нужно научить базовые роутеры искать верхний модуль So far, so good, но что если
  • 18. Поиск верхнего модуля 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 } } Нужна своя система навигации
  • 19. Зачем нужна своя система навигации? Хранение истории переходов Поддержка Third-party контроллеров Bonus: проверка, что модуль был на экране Bonus: расстояние между модулями Поиск верхнего модуля Обертка над UIKit Реакция на изменение SDK
  • 20. Свежий взгляд на базовый Router class 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’ов
  • 21. Абстрагируем 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 общается с обработчиком переходов
  • 22. Обработчик переходов оборачивает UIViewController Виды модулей Анимирующие AnimatingTransitionsHandlerImpl NavigationTransitionsHandlerImpl ContainerTransitionsHandlerImpl SplitViewTransitionsHandlerImpl TabBarTransitionsHandlerImpl Контейнеры pushViewController(_:animated:) presentViewController(_:animated:completion:) visibleAnimatingTransitionsHandlers() allAnimatingTransitionsHandlers() Легко добавить Third-party контроллер
  • 23. class BaseRouter { weak var navigationController: UINavigationController? weak var rootViewController: UIViewController? } Новый базовый Router 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? } 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’ом
  • 24. Схема выполнения отменяемых переходов Transitions handler box выполни отменяемый переход Router presentation context transitions handler box Transitions Coordinator Top animating transitions handlerзапусти анимацию presentation context
  • 25. Взглянем еще раз на новый базовый Router class 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 может закрывать свой модуль
  • 26. Навигационная связь Router 1 transition id 1 Transitions handler 1 Transitions handler 2 presenting transitions handler Вернись на модуль 1 Закрой модуль 2 Router 2 transition id 2 Что лучше: “Вернись на модуль 1” или “Закрой модуль 2” ?
  • 28. Выход из Flow Фильтр Города Router закрой модуль городов
  • 30. Выход из усложненного Flow Фильтр модуль регионов закончил ГородаРегионы модуль городов закончил Router вернись на наш модуль “Вернись на модуль” гибче, чем “Закрой модуль”
  • 31. Слой Router Предварительные итоги Подходы к выполнению обратных переходов Поддержка DeepLinks Слой Assembly Базовые классы Router, поддержка iPad, master-detail Простой Router (фасад, абстрация от UIKit)
  • 33. Один UIViewController, много Router’ов Выводы по демо Проверка наличия модуля в истории (Авторизация) Проверка isIpad() Поиск верхнего модуля (Авторизация, DeepLink’и, Push’ы) Проверка модулей на дубликаты (🍌, 🍏) Аниматоры переходов Проверка isIpad()
  • 34. Выделите слой Router (определять стиль перехода) Общие советы Используйте “Вернись на модуль” вместо “Закрой модуль” Выделите слой Assembly Абстрагируйте Router от UIKit Вынесите логику принятия решений в отдельный слой Описывайте переходы в декларативном стиле
  • 35. One more thing https://github.com/avito-tech/Marshroute Исходники Докладчик: Юсипов Тимур https://vk.com/ma3tsa tyusipov@avito.ru ykylele@gmail.com fizmatchelskype personal mail work mail vk Marshroute Спасибо за внимание! presentation.finish() https://github.com/avito-tech/Marshroute/tree/master/Example Демо