SlideShare ist ein Scribd-Unternehmen logo
1 von 35
Downloaden Sie, um offline zu lesen
Удобный и расширяемый
роутинг в iOS-приложении
Юсипов Тимур
Avito
Историческая справка
70187
lines of code
39051
lines of code
(2011 .. 2013) Outsource
(2013 .. 2016) In-house
VIPER
Введем понятие 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
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
dismiss
cities
Усложненный flow
Фильтр Регионы Города
Выход из Flow
Фильтр
module
output
ГородаРегионы
module
output
Router
return
to filter
“Вернись на модуль”
гибче, чем
“Закрой модуль”
Слой Router
Предварительные итоги
Подходы к выполнению обратных переходов
Поддержка DeepLinks
Слой Assembly
Базовые классы Router, поддержка iPad, master-detail
Простой Router (фасад, абстрация от UIKit, поддержка Third-Party)
demo.start()
Один UIViewController, много Router’ов
Выводы по демо
Проверка наличия модуля в истории (Авторизация)
Проверка isIpad()
Поиск верхнего модуля (Авторизация, DeepLink’и, Push’ы)
Проверка модулей на дубликаты (🍌, 🍏)
Аниматоры переходов
Проверка isIpad()
Выделите слой Router (определять стиль перехода)
Общие советы
Используйте “Вернись на модуль” вместо “Закрой модуль”
Выделите слой Assembly (верьте в появление DI для Swift)
Абстрагируйте 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
Демо

Weitere ähnliche Inhalte

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

Do iOS Presentation - Mobile app architectures
Do iOS Presentation - Mobile app architecturesDo iOS Presentation - Mobile app architectures
Do iOS Presentation - Mobile app architecturesDavid Broža
 
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 2013Raimundas Banevičius
 
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 freeBenotCaron
 
Foomo / Zugspitze Presentation
Foomo / Zugspitze PresentationFoomo / Zugspitze Presentation
Foomo / Zugspitze Presentationweareinteractive
 
알아보자 Dependency Injection과 Deli
알아보자 Dependency Injection과 Deli알아보자 Dependency Injection과 Deli
알아보자 Dependency Injection과 DeliJungwon An
 
MBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&CoMBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&Coe-Legion
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingPractical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingNatasha Murashev
 
Practialpop 160510130818
Practialpop 160510130818Practialpop 160510130818
Practialpop 160510130818Shahzain Saeed
 
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 SwiftPROIDEA
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM patternNAVER Engineering
 
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 processorBartosz Kosarzycki
 
Migrating Objective-C to Swift
Migrating Objective-C to SwiftMigrating Objective-C to Swift
Migrating Objective-C to SwiftElmar Kretzer
 
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 CodebasesIstanbul Tech Talks
 
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 EvolutionFITC
 
Inversion Of Control
Inversion Of ControlInversion Of Control
Inversion Of ControlChad Hietala
 
«Управление логикой переходов между экранами приложения с помощью координатор...
«Управление логикой переходов между экранами приложения с помощью координатор...«Управление логикой переходов между экранами приложения с помощью координатор...
«Управление логикой переходов между экранами приложения с помощью координатор...Mail.ru Group
 
A portlet-API based approach for application integration
A portlet-API based approach for application integrationA portlet-API based approach for application integration
A portlet-API based approach for application integrationwhabicht
 
Annotation-Based Spring Portlet MVC
Annotation-Based Spring Portlet MVCAnnotation-Based Spring Portlet MVC
Annotation-Based Spring Portlet MVCJohn Lewis
 

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

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
 
Exemplo de Fluxograma de Arquitetura aplicativo
Exemplo de Fluxograma de Arquitetura aplicativoExemplo de Fluxograma de Arquitetura aplicativo
Exemplo de Fluxograma de Arquitetura aplicativo
 
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
 
Foomo / Zugspitze Presentation
Foomo / Zugspitze PresentationFoomo / Zugspitze Presentation
Foomo / Zugspitze Presentation
 
알아보자 Dependency Injection과 Deli
알아보자 Dependency Injection과 Deli알아보자 Dependency Injection과 Deli
알아보자 Dependency Injection과 Deli
 
MBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&CoMBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&Co
 
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
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern
 
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
 
Migrating Objective-C to Swift
Migrating Objective-C to SwiftMigrating Objective-C to Swift
Migrating Objective-C to Swift
 
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
 
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
 
Inversion Of Control
Inversion Of ControlInversion Of Control
Inversion Of Control
 
«Управление логикой переходов между экранами приложения с помощью координатор...
«Управление логикой переходов между экранами приложения с помощью координатор...«Управление логикой переходов между экранами приложения с помощью координатор...
«Управление логикой переходов между экранами приложения с помощью координатор...
 
A portlet-API based approach for application integration
A portlet-API based approach for application integrationA portlet-API based approach for application integration
A portlet-API based approach for application integration
 
ASP .net MVC
ASP .net MVCASP .net MVC
ASP .net MVC
 
Annotation-Based Spring Portlet MVC
Annotation-Based Spring Portlet MVCAnnotation-Based Spring Portlet MVC
Annotation-Based Spring Portlet MVC
 

Mehr von Ontico

One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...Ontico
 
Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)Ontico
 
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)Ontico
 
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...Ontico
 
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...Ontico
 
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)Ontico
 
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...Ontico
 
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...Ontico
 
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)Ontico
 
MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)Ontico
 
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...Ontico
 
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...Ontico
 
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...Ontico
 
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)Ontico
 
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)Ontico
 
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)Ontico
 
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)Ontico
 
100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...Ontico
 
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...Ontico
 
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...Ontico
 

Mehr von Ontico (20)

One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
 
Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)
 
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
 
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
 
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
 
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
 
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
 
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
 
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
 
MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)
 
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
 
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
 
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
 
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
 
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
 
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
 
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
 
100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...
 
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
 
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
 

Kürzlich hochgeladen

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXssuser89054b
 
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...ranjana rawat
 
AKTU Computer Networks notes --- Unit 3.pdf
AKTU Computer Networks notes ---  Unit 3.pdfAKTU Computer Networks notes ---  Unit 3.pdf
AKTU Computer Networks notes --- Unit 3.pdfankushspencer015
 
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdfONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdfKamal Acharya
 
Generative AI or GenAI technology based PPT
Generative AI or GenAI technology based PPTGenerative AI or GenAI technology based PPT
Generative AI or GenAI technology based PPTbhaskargani46
 
Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...Call Girls in Nagpur High Profile
 
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...ranjana rawat
 
Intze Overhead Water Tank Design by Working Stress - IS Method.pdf
Intze Overhead Water Tank  Design by Working Stress - IS Method.pdfIntze Overhead Water Tank  Design by Working Stress - IS Method.pdf
Intze Overhead Water Tank Design by Working Stress - IS Method.pdfSuman Jyoti
 
University management System project report..pdf
University management System project report..pdfUniversity management System project report..pdf
University management System project report..pdfKamal Acharya
 
chapter 5.pptx: drainage and irrigation engineering
chapter 5.pptx: drainage and irrigation engineeringchapter 5.pptx: drainage and irrigation engineering
chapter 5.pptx: drainage and irrigation engineeringmulugeta48
 
Double rodded leveling 1 pdf activity 01
Double rodded leveling 1 pdf activity 01Double rodded leveling 1 pdf activity 01
Double rodded leveling 1 pdf activity 01KreezheaRecto
 
UNIT - IV - Air Compressors and its Performance
UNIT - IV - Air Compressors and its PerformanceUNIT - IV - Air Compressors and its Performance
UNIT - IV - Air Compressors and its Performancesivaprakash250
 
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...roncy bisnoi
 
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete RecordCCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete RecordAsst.prof M.Gokilavani
 
FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756
FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756
FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756dollysharma2066
 
Call Girls Wakad Call Me 7737669865 Budget Friendly No Advance Booking
Call Girls Wakad Call Me 7737669865 Budget Friendly No Advance BookingCall Girls Wakad Call Me 7737669865 Budget Friendly No Advance Booking
Call Girls Wakad Call Me 7737669865 Budget Friendly No Advance Bookingroncy bisnoi
 
Top Rated Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
Top Rated  Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...Top Rated  Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
Top Rated Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...Call Girls in Nagpur High Profile
 
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Bookingdharasingh5698
 

Kürzlich hochgeladen (20)

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
 
AKTU Computer Networks notes --- Unit 3.pdf
AKTU Computer Networks notes ---  Unit 3.pdfAKTU Computer Networks notes ---  Unit 3.pdf
AKTU Computer Networks notes --- Unit 3.pdf
 
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdfONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
 
Generative AI or GenAI technology based PPT
Generative AI or GenAI technology based PPTGenerative AI or GenAI technology based PPT
Generative AI or GenAI technology based PPT
 
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort ServiceCall Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
 
Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...
 
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
 
Intze Overhead Water Tank Design by Working Stress - IS Method.pdf
Intze Overhead Water Tank  Design by Working Stress - IS Method.pdfIntze Overhead Water Tank  Design by Working Stress - IS Method.pdf
Intze Overhead Water Tank Design by Working Stress - IS Method.pdf
 
Water Industry Process Automation & Control Monthly - April 2024
Water Industry Process Automation & Control Monthly - April 2024Water Industry Process Automation & Control Monthly - April 2024
Water Industry Process Automation & Control Monthly - April 2024
 
University management System project report..pdf
University management System project report..pdfUniversity management System project report..pdf
University management System project report..pdf
 
chapter 5.pptx: drainage and irrigation engineering
chapter 5.pptx: drainage and irrigation engineeringchapter 5.pptx: drainage and irrigation engineering
chapter 5.pptx: drainage and irrigation engineering
 
Double rodded leveling 1 pdf activity 01
Double rodded leveling 1 pdf activity 01Double rodded leveling 1 pdf activity 01
Double rodded leveling 1 pdf activity 01
 
UNIT - IV - Air Compressors and its Performance
UNIT - IV - Air Compressors and its PerformanceUNIT - IV - Air Compressors and its Performance
UNIT - IV - Air Compressors and its Performance
 
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
 
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete RecordCCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
 
FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756
FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756
FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756
 
Call Girls Wakad Call Me 7737669865 Budget Friendly No Advance Booking
Call Girls Wakad Call Me 7737669865 Budget Friendly No Advance BookingCall Girls Wakad Call Me 7737669865 Budget Friendly No Advance Booking
Call Girls Wakad Call Me 7737669865 Budget Friendly No Advance Booking
 
Top Rated Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
Top Rated  Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...Top Rated  Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
Top Rated Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
 
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
 

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

  • 1. Удобный и расширяемый роутинг в iOS-приложении Юсипов Тимур Avito
  • 2. Историческая справка 70187 lines of code 39051 lines of code (2011 .. 2013) Outsource (2013 .. 2016) In-house VIPER
  • 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? } Теперь роутер может закрывать свой модуль
  • 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 dismiss cities
  • 30. Выход из Flow Фильтр module output ГородаРегионы module output Router return to filter “Вернись на модуль” гибче, чем “Закрой модуль”
  • 31. Слой Router Предварительные итоги Подходы к выполнению обратных переходов Поддержка DeepLinks Слой Assembly Базовые классы Router, поддержка iPad, master-detail Простой Router (фасад, абстрация от UIKit, поддержка Third-Party)
  • 33. Один UIViewController, много Router’ов Выводы по демо Проверка наличия модуля в истории (Авторизация) Проверка isIpad() Поиск верхнего модуля (Авторизация, DeepLink’и, Push’ы) Проверка модулей на дубликаты (🍌, 🍏) Аниматоры переходов Проверка isIpad()
  • 34. Выделите слой Router (определять стиль перехода) Общие советы Используйте “Вернись на модуль” вместо “Закрой модуль” Выделите слой Assembly (верьте в появление DI для Swift) Абстрагируйте 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 Демо