В данной статье хочу поделится своим вариантов как вынести переходы между экранами в iOS приложении в отдельный класс ( Router ). Как дополнять возможности router-a переходить на большее количество экранов при этом не копируя код и избегая наследования.
Если у нас приложение построено на MVC ( да и любой другой ) архитектуре, то мы часто можем столкнуться в коде ViewController-a со следующим вариантом показать экран ( пример перейти на экран деталей пользователя ):
Теперь нам нужно перейти на экран деталей пользователя например из списка контактов, результатов поиска или из пин-а на карте. Итого получается что нам нужно открыть экран деталей пользователя из нескольких ViewController-ов.
Как вынести метод showUserProfile в одно место?
Для решение задачи можно создать отдельный класс ( в нашем варианте UserRouter ) и скопировать туда метод. Получаем:
Теперь нам нужно подключить наш роутер во все controller-ы где мы будем использовать метод showUserProfile. Итого получаем:
UsersListViewController.swift
SearchResultViewController.swift
Вроде все хорошо и мы получили что хотели.
Но теперь нам потребовалось добавить переход на экран добавления нового пользователя из экрана UsersListViewController. Можно просто добавить метод showAddUserScreen метод в UserRouter, но SearchResultViewController-у этот метод не нужен и не хотелось бы видеть лишние метод при использовании router-a. Простой вариант создаем новый роутер UsersListRouter наследуемся от UserRouter и добавляем метод showAddUserScreen. Получаем:
Итого наш UsersListController может открыть экран деталей пользователи и экран добавления нового пользователя. В свою очередь SearchResultController может только открыть экран деталей пользователя. В результате мы не нарушили наше правило НЕ дублировать код.
Наш проект не стоит на месте и добавление нового функционала влечет за собой изменения в нашем коде.
Пришло новое требование: добавить возможность отравления сообщения из UsersListViewController и из UserProfileViewController.
Сейчас нам нужно сделать так чтобы
UserListViewController мог переходить на экраны: UserProfile, AddUser, SendMessage,
SearchResultViewController на экраны: UserProfile
UserProfileViewController на экраны: SendMessage
Наследование здесь не поможет потому что так или иначе какой то класс будет видеть методы ( возможность ) перехода на экран, на который ему переходить не надо :)
В данном случае нам помогут protocol и protocol extension. Изменим наш UserRouter:
UserRouter.swift
Добавим новый роутер SystemToolsRouter:
SystemToolsRouter.swift
И добавим изменения в наши роутеры экранов:
UsersListRouter.swift
SearchResultRouter.swift
UsersProfileRouter.swift
В результате наши контроллеры будут выглядеть:
UsersListViewController.swift
SearchResultViewController.swift
UserProfileViewController.swift
Итого при использовании protocol composition, protocol extension мы легко можем добавлять переходы на другие экраны не используя наследования и дублирования кода.
P.S. Оставляем комментарии, темы для рассмотрения. И кликаем +1, f, в, t, что располагаются чуть ниже статьи.
Если у нас приложение построено на MVC ( да и любой другой ) архитектуре, то мы часто можем столкнуться в коде ViewController-a со следующим вариантом показать экран ( пример перейти на экран деталей пользователя ):
func showUserProfile() {
let profile = UserProfileViewController()
self.show(profile, sender: nil)
}
Теперь нам нужно перейти на экран деталей пользователя например из списка контактов, результатов поиска или из пин-а на карте. Итого получается что нам нужно открыть экран деталей пользователя из нескольких ViewController-ов.
Как вынести метод showUserProfile в одно место?
Для решение задачи можно создать отдельный класс ( в нашем варианте UserRouter ) и скопировать туда метод. Получаем:
class UserRouter {
func showUserProfile(from controller: UIViewController) {
let profile = UserProfileViewController()
controller.show(profile, sender: nil)
}
}
Теперь нам нужно подключить наш роутер во все controller-ы где мы будем использовать метод showUserProfile. Итого получаем:
UsersListViewController.swift
class UsersListViewController {
var router: UserRouter?
...
router?.showUserProfile(from: self)
...
}
SearchResultViewController.swift
class SearchResultViewController {
var router: UserRouter?
...
router?.showUserProfile(from: self)
...
}
Вроде все хорошо и мы получили что хотели.
Но теперь нам потребовалось добавить переход на экран добавления нового пользователя из экрана UsersListViewController. Можно просто добавить метод showAddUserScreen метод в UserRouter, но SearchResultViewController-у этот метод не нужен и не хотелось бы видеть лишние метод при использовании router-a. Простой вариант создаем новый роутер UsersListRouter наследуемся от UserRouter и добавляем метод showAddUserScreen. Получаем:
class UsersListRouter: UserRouter {
func showAddUserScreen(from controller: UIViewController) {
let add = AddUserViewController()
controller.show(add, sender: nil)
}
}
Итого наш UsersListController может открыть экран деталей пользователи и экран добавления нового пользователя. В свою очередь SearchResultController может только открыть экран деталей пользователя. В результате мы не нарушили наше правило НЕ дублировать код.
Наш проект не стоит на месте и добавление нового функционала влечет за собой изменения в нашем коде.
Пришло новое требование: добавить возможность отравления сообщения из UsersListViewController и из UserProfileViewController.
Сейчас нам нужно сделать так чтобы
UserListViewController мог переходить на экраны: UserProfile, AddUser, SendMessage,
SearchResultViewController на экраны: UserProfile
UserProfileViewController на экраны: SendMessage
Наследование здесь не поможет потому что так или иначе какой то класс будет видеть методы ( возможность ) перехода на экран, на который ему переходить не надо :)
В данном случае нам помогут protocol и protocol extension. Изменим наш UserRouter:
UserRouter.swift
protocol UserRouter {
func showUserProfile(from controller: UIViewController)
}
extension UserRouter {
func showUserProfile(from controller: UIViewController) {
let profile = UserProfileViewController()
controller.show(profile, sender: nil)
}
}
Добавим новый роутер SystemToolsRouter:
SystemToolsRouter.swift
protocol SystemToolsRouter {
func showSendMessageScreen(from controller: UIViewController)
}
extension SystemToolsRouter {
func showSendMessageScreen(from controller: UIViewController) {
let sendMessage = SendMessageViewController()
controller.show(sendMessage, sender: nil)
}
}
И добавим изменения в наши роутеры экранов:
UsersListRouter.swift
class UsersListRouter: SystemToolsRouter, UserRouter {
func showAddUserScreen(from controller: UIViewController) {
let add = AddUserViewController()
controller.show(add, sender: nil)
}
}
SearchResultRouter.swift
class SearchResultRouter: UserRouter {
}
UsersProfileRouter.swift
class UserProfileRouter: SystemToolsRouter {
}
В результате наши контроллеры будут выглядеть:
UsersListViewController.swift
class UsersListViewController {
var router: UsersListRouter?
...
router?.showUserProfile(from: self)
router?.showSendMessageScreen(from: self)
router?.showAddUserScreen(from: self)
...
}
SearchResultViewController.swift
class SearchResultViewController {
var router: SearchResultRouter?
...
router?.showUserProfile(from: self)
...
}
UserProfileViewController.swift
class UserProfileViewController {
var router: UserProfileRouter?
...
router?.showSendMessageScreen(from: self)
...
}
Итого при использовании protocol composition, protocol extension мы легко можем добавлять переходы на другие экраны не используя наследования и дублирования кода.
P.S. Оставляем комментарии, темы для рассмотрения. И кликаем +1, f, в, t, что располагаются чуть ниже статьи.