Router (směrovač) — architektonický vzor centralizující logiku navigace mezi obrazovkami aplikace. Router určuje, kam a jak přejít při výskytu události. V iOS Router abstrahuje UINavigationController a segue, v Android — FragmentManager a NavController z Jetpack Navigation. Router může fungovat jako samostatný vzor nebo jako komponenta Coordinator (Router-per-Coordinator) a VIPER (Router-per-module). Více — v Android Navigation Component Guide.
Hlavní body
Router — vzor zapouzdřující logiku přechodu mezi obrazovkami. Hlavní myšlenka: ViewController (nebo Presenter/ViewModel) nevolá přímo navigationController.pushViewController, ale informuje Router o záměru přejít. Router rozhoduje, který ViewController vytvořit a jak jej zobrazit. Router může být jednoduchý (sada metod navigace) nebo složitý (se směrováním podle URL schémat, deep linků a push oznámení).
Tři přístupy k Router — Router jako služba (globální Navigator-singleton), Router jako komponenta modulu (přístup VIPER), Router jako protokol (abstrakce pro Coordinator). Globální Router — jednoduchost (jedna instance na aplikaci) — populární v malých projektech. Modulární Router — každá funkce má svůj Router — standard VIPER a Clean Architecture. Protokolový Router — používán v Coordinator: RouterProtocol s metodami push/present/pop/dismiss.
| Přístup | Struktura | Kdy použít |
|---|---|---|
| Globální Navigator | Singleton s metodami přechodu | Malé projekty, prototypy |
| Modulární Router | Router uvnitř každého modulu | VIPER, Clean Architecture |
| Protokolový Router | Protocol + implementace pro Coordinator | MVVM-C, Coordinator pattern |
Historie vzoru — Router se objevil ve webových frameworcích (URL Router — Rails Routes, Express.js) a byl adaptován pro mobilní vývoj. V iOS se Router stal populárním s růstem VIPER (2015-2017). V Android je Router — součástí Jetpack Navigation (2018), standardního řešení Google pro navigaci. Nyní je Router neoddělitelnou součástí každé modulární architektury mobilních aplikací.
iOS Router — protokol s metodami pro různé typy navigace. Základní implementace obaluje UINavigationController. Router může spravovat modální prezentace (present/dismiss), push/pop v navigačním zásobníku, showDetail pro Split View Controller a prezentace na celou obrazovku (iOS 13+). Router nevytváří ViewController — obdrží hotovou instanci a zobrazí ji. Vytváření ViewController provádí továrna (Assembly, DI-kontejner) nebo Coordinator.
// RouterProtocol pro iOS
protocol RouterProtocol {
func push(_ viewController: UIViewController, animated: Bool)
func pop(animated: Bool)
func popToRoot(animated: Bool)
func present(_ viewController: UIViewController, animated: Bool)
func dismiss(animated: Bool)
func setViewControllers(_: [UIViewController], animated: Bool)
}
// Implementace Router přes UINavigationController
class NavigationRouter: RouterProtocol {
private let navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func push(_ vc: UIViewController, animated: Bool) {
if navigationController.presentedViewController {
navigationController.dismiss(animated: false)
}
navigationController.pushViewController(vc, animated: animated)
}
func pop(animated: Bool) {
navigationController.popViewController(animated: animated)
}
func popToRoot(animated: Bool) {
navigationController.popToRootViewController(animated: animated)
}
func present(_ vc: UIViewController, animated: Bool) {
navigationController.present(vc, animated: animated)
}
func dismiss(animated: Bool) {
navigationController.dismiss(animated: animated)
}
func setViewControllers(_ vcs: [UIViewController], animated: Bool) {
navigationController.setViewControllers(vcs, animated: animated)
}
}
// Assembly — vytvoření modulu s Routerem
protocol ProfileModuleFactory {
func makeProfileView() -> UIViewController
}
Router ve VIPER — každý modul VIPER má svůj Router, který ví, které obrazovky otevřít z aktuálního modulu. Router vytváří ViewController dalšího modulu přes továrnu a předává řízení. Router ve VIPER — jediná komponenta obsahující import UIKit (kromě View). To zjednodušuje testování Presenter a Interactor: nejsou závislé na UIKit.
Jetpack Navigation Component — standardní router pro Android, doporučovaný Google od roku 2018. Navigace je definována v XML grafu (nav_graph.xml): uzly (fragments, activities, destinations) a hrany (actions). NavController spravuje přechody: volá FragmentManager.replace/add a spravuje zásobník zpět (back stack). Navigation Component podporuje deep linky, argumenty, animace přechodů a Safe Args pro typově bezpečný přenos dat.
// nav_graph.xml — navigační graf
<!-- @xml/nav_graph.xml -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph"
app:startDestination="@id/loginFragment">
<fragment android:id="@+id/loginFragment"
android:name=".ui.login.LoginFragment">
<action android:id="@+id/to_home"
app:destination="@id/homeFragment" />
</fragment>
<fragment android:id="@+id/homeFragment"
android:name=".ui.home.HomeFragment">
<argument android:name="userId"
android:defaultValue="0"
app:argType="integer" />
<deepLink app:uri="myapp://home/{userId}" />
</fragment>
</navigation>
// Kotlin — navigace přes NavController
class LoginFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.loginButton.setOnClickListener {
val action = LoginFragmentDirections.toHome(userId = 42)
findNavController().navigate(action)
}
}
}
// Safe Args — typově bezpečné argumenty
class HomeFragmentArgs : NavArgs {
val userId: Int get() = arguments?.getInt("userId") ?: 0
}
// NavigationUI — integrace s Toolbar, BottomNav, Drawer
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val navHost = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHost.navController
NavigationUI.setupActionBarWithNavController(this, navController)
}
}
Deep linky v Android Navigation — Navigation Component podporuje explicitní deep linky (PendingIntent + NavDeepLinkBuilder) a implicitní (přes intent-filter v manifestu). Deep link může vést na kterýkoli uzel v nav_graph. Zpracování push oznámení: Notification vytváří PendingIntent s NavDeepLinkBuilder, který obnovuje zásobník zpět a otevírá požadovanou obrazovku. Navigation Component automaticky zpracovává tlačítka Up/Back a vrací uživatele na předchozí obrazovku.
URL Routing — směrování na základě URL schémat, deep linků a universal linků. Aplikace registruje schéma (myapp://profile/42) nebo universal link (https://example.com/profile/42). Router parsuje URL, extrahuje parametry (userId=42) a otevírá odpovídající obrazovku. URL Routing umožňuje otevírání aplikace zvenčí: odkazem z webu, z emailu, push oznámení, QR kódu. iOS používá NSUserActivity + universal link, Android — intent-filter + deep link.
// URL Router — parsování deep link a navigace
protocol URLRoute {
var pattern: String { get }
func navigate(parameters: [String: String], router: RouterProtocol)
}
struct ProfileRoute: URLRoute {
let pattern = "myapp://profile/{userId}"
func navigate(parameters: [String: String], router: RouterProtocol) {
guard let userId = parameters["userId"] else { return }
let profileVC = ProfileViewController(userId: userId)
router.push(profileVC, animated: true)
}
}
// URL Router — mapování URL na trasy
class URLRouter {
private var routes: [URLRoute] = []
func handle(_ url: URL, router: RouterProtocol) {
for route in routes {
if let params = matchPattern(route.pattern, url: url) {
route.navigate(parameters: params, router: router)
return
}
}
}
}
Deep link routing v praxi — aplikace musí zpracovat deep link ve třech stavech: nespuštěna (spuštění s deep linkem), na pozadí (obnovení z pozadí), aktivní (onNewIntent/SceneDelegate). URL Router musí správně obnovit zásobník zpět: při deep linku na obrazovku profilu musí uživatel stisknout Back a vrátit se na předchozí obrazovku. iOS a Android zpracovávají zásobník zpět odlišně — Router musí zohlednit platformní specifika.
Router vs Coordinator — Router odpovídá na otázku "jak zobrazit obrazovku?" (push/present), Coordinator — "kterou obrazovku zobrazit?" (který flow spustit). Router — nástroj navigace, Coordinator — organizátor flow. Router je používán Coordinatorem. Ve VIPER Router vykonává obě funkce: rozhoduje, kam přejít a jak zobrazit. V MVVM-C je Router — samostatný protokol používaný Coordinatorem. Vzory se nevylučují, ale doplňují.
| Charakteristika | Router | Coordinator | Navigator |
|---|---|---|---|
| Zaměření | Mechanika navigace | Flow navigace | Globální přístup |
| Úroveň | UI-framework (push/present) | Business logika (flow) | Infrastruktura |
| Tvorba obrazovek | Obdrží hotový VC | Vytváří VC přes továrnu | Vytváří VC |
| Životní cyklus | Jeden na navigaci | childCoordinators | Singleton |
Router vs Navigator — Navigator — zastaralý název pro Router v iOS komunitě. V raných implementacích byl Navigator třída Singleton s metodami přechodu. Navigator — globální Router-singleton. Navigator je jednodušší, ale vytváří skryté závislosti. Router na prototypech — testovatelnější a modulárnější. Navigator se hodí pro projekty bez přísné architektury, Router — pro Clean Architecture a VIPER.
Často kladené otázky
Router — abstrakce nad UINavigationController (push/present/pop). Coordinator — vrstva nad Routerem spravující flow (kterou obrazovku zobrazit jako další). Router je zodpovědný za mechaniku, Coordinator — za logiku navigace. Coordinator používá Router k provádění přechodů. Ve VIPER Router kombinuje obě funkce, v MVVM-C jsou oddělené.
Ne. Pro aplikace s 3-5 obrazovkami jsou přímá volání navigationController.pushViewController normálním řešením. Router se stává užitečným při 10+ obrazovkách, deep linkách, push oznámeních a modulární architektuře. Router zjednodušuje testování navigace a umožňuje centralizované zpracování přechodů z libovolného zdroje (tlačítko, deep link, push).
URL Router — rozšíření Routeru, které parsuje deep linky a universal linky. URL Router určuje shodu mezi URL a obrazovkou aplikace. Například, route myapp://profile/42 → ProfileViewController(userId: 42). URL Router centralizuje zpracování deep linků z různých zdrojů: push oznámení, QR kódy, emailové odkazy, univerzální odkazy.
Ano. Router může pracovat samostatně — jako globální Navigator nebo jako komponenta modulu VIPER. Router bez Coordinatoru — pouze zapouzdření UINavigationController za protokolem. Coordinator bez Routeru — obtížnější (ViewController sám volá push/present), ale možné. Nejlepší praxe — Router + Coordinator dohromady.
Funkčně — ano, oba centralizují navigaci. Implementace se liší: iOS Router — protokol + třída kolem UINavigationController; Android — Jetpack Navigation Component s XML grafem a NavController. Android Router je vestavěný do frameworku (Navigation Component), iOS Router — externí implementace. Android Router podporuje Safe Args, iOS Router — ne (potřebuje generics nebo Codable).
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také