Router (útválasztó) — architekturális minta, amely központosítja a navigációs logikát az alkalmazás képernyői között. A Router meghatározza, hová és hogyan kell navigálni egy esemény bekövetkeztekor. iOS-ben a Router absztrahálja az UINavigationController-t és a segue-t, Androidban — a FragmentManager-t és a NavController-t a Jetpack Navigation-ből. A Router működhet önálló mintaként vagy Coordinator (Router-per-Coordinator) és VIPER (Router-per-module) komponenseként. Bővebben — a Android Navigation Component Guide-ban.
Főbb pontok
Router — minta, amely beágyazza a képernyők közötti átmenet logikáját. Fő ötlet: a ViewController (vagy Presenter/ViewModel) nem közvetlenül hívja a navigationController.pushViewController-t, hanem értesíti a Router-t a navigációs szándékról. A Router dönti el, melyik ViewController-t hozza létre és hogyan jelenítse meg. A Router lehet egyszerű (navigációs metódusok gyűjteménye) vagy összetett (URL-sémák, deep link-ek és push értesítések szerinti útválasztással).
Három megközelítés a Router-hez — Router szolgáltatásként (globális Navigator-szingleton), Router modulkomponensként (VIPER megközelítés), Router protokollként (absztrakció a Coordinator számára). Globális Router — egyszerűség (egy példány alkalmazásonként) — népszerű kis projektekben. Moduláris Router — minden funkciónak saját Router-e van — a VIPER és Clean Architecture szabványa. Protokoll Router — Coordinator-ban használva: RouterProtocol push/present/pop/dismiss metódusokkal.
| Megközelítés | Szerkezet | Mikor használjuk |
|---|---|---|
| Globális Navigator | Singleton átmeneti metódusokkal | Kis projektek, prototípusok |
| Moduláris Router | Router minden modulon belül | VIPER, Clean Architecture |
| Protokoll Router | Protocol + implementáció Coordinator számára | MVVM-C, Coordinator minta |
A minta története — A Router webframeworks-ekben jelent meg (URL Router — Rails Routes, Express.js) és adaptálták mobilfejlesztésre. iOS-ben a Router a VIPER növekedésével (2015-2017) vált népszerűvé. Androidban a Router — a Jetpack Navigation (2018) része, a Google szabványos navigációs megoldása. Ma a Router minden moduláris mobilalkalmazás-architektúra elválaszthatatlan része.
iOS Router — protokoll metódusokkal különböző navigációs típusokhoz. Az alap implementáció burkolja az UINavigationController-t. A Router kezelhet modális prezentációkat (present/dismiss), push/pop-ot a navigációs veremben, showDetail-t Split View Controller-hez és teljes képernyős prezentációkat (iOS 13+). A Router nem hoz létre ViewController-t — kap egy kész példányt és megjeleníti azt. A ViewController létrehozását gyár (Assembly, DI-konténer) vagy Coordinator végzi.
// RouterProtocol iOS-hez
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)
}
// Router implementáció UINavigationController-en keresztül
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 — modul létrehozása Router-rel
protocol ProfileModuleFactory {
func makeProfileView() -> UIViewController
}
Router VIPER-ben — minden VIPER modulnak saját Router-e van, amely tudja, mely képernyőket kell megnyitni az aktuális modulból. A Router létrehozza a következő modul ViewController-jét a gyáron keresztül és átadja a vezérlést. Router VIPER-ben — az egyetlen komponens, amely import UIKit-et tartalmaz (a View-n kívül). Ez leegyszerűsíti a Presenter és Interactor tesztelését: nem függenek az UIKit-től.
Jetpack Navigation Component — szabványos router Androidhoz, a Google által ajánlva 2018 óta. A navigáció egy XML gráfban (nav_graph.xml) van definiálva: csomópontok (fragments, activities, destinations) és élek (actions). A NavController kezeli az átmeneteket: meghívja a FragmentManager.replace/add-t és kezeli a visszalépési vermet (back stack). A Navigation Component támogatja a deep link-eket, argumentumokat, átmeneti animációkat és a Safe Args-t a típusbiztos adatátvitelhez.
// nav_graph.xml — navigációs gráf
<!-- @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 — navigáció NavController-en keresztül
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 — típusbiztos argumentumok
class HomeFragmentArgs : NavArgs {
val userId: Int get() = arguments?.getInt("userId") ?: 0
}
// NavigationUI — integráció 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 link-ek Android Navigation-ban — A Navigation Component támogatja az explicit deep link-eket (PendingIntent + NavDeepLinkBuilder) és implicit deep link-eket (intent-filter-en keresztül a manifesztumban). A deep link bármelyik nav_graph csomóponthoz vezethet. Push értesítések feldolgozása: a Notification létrehoz egy PendingIntent-et NavDeepLinkBuilder-rel, amely visszaállítja a back stack-et és megnyitja a kívánt képernyőt. A Navigation Component automatikusan kezeli a Up/Back gombokat és visszaviszi a felhasználót az előző képernyőre.
URL Routing — útválasztás URL-sémák, deep link-ek és universal link-ek alapján. Az alkalmazás regisztrál egy sémát (myapp://profile/42) vagy egy universal link-et (https://example.com/profile/42). A Router elemzi az URL-t, kivonja a paramétereket (userId=42) és megnyitja a megfelelő képernyőt. Az URL Routing lehetővé teszi az alkalmazás külső megnyitását: weboldal linkjén, e-mailből, push értesítésből, QR kódból. Az iOS NSUserActivity + universal link-et használ, Android — intent-filter + deep link-et.
// URL Router — deep link elemzés és navigáció
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 — URL-ek hozzárendelése útvonalakhoz
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 a gyakorlatban — az alkalmazásnak három állapotban kell feldolgoznia a deep link-et: nem indult (indítás deep link-kel), háttérben (visszaállítás háttérből), aktív (onNewIntent/SceneDelegate). Az URL Router-nek helyesen kell visszaállítania a back stack-et: profilképernyőre mutató deep link esetén a felhasználónak meg kell tudnia nyomni a Back gombot és visszatérni az előző képernyőre. Az iOS és Android eltérően kezeli a back stack-et — a Router-nek figyelembe kell vennie a platform jellemzőit.
Router vs Coordinator — A Router arra a kérdésre válaszol, hogy "hogyan jelenítsük meg a képernyőt?" (push/present), a Coordinator — "melyik képernyőt jelenítsük meg?" (melyik flow-t indítsuk el). Router — navigációs eszköz, Coordinator — flow szervező. A Router-t a Coordinator használja. VIPER-ben a Router mindkét funkciót ellátja: eldönti, hová navigáljon és hogyan jelenítsen meg. MVVM-C-ben a Router — külön protokoll, amelyet a Coordinator használ. A minták nem zárják ki, hanem kiegészítik egymást.
| Jellemző | Router | Coordinator | Navigator |
|---|---|---|---|
| Fókusz | Navigációs mechanika | Navigációs flow | Globális hozzáférés |
| Szint | UI-framework (push/present) | Üzleti logika (flow) | Infrastruktúra |
| Képernyő létrehozása | Kész VC-t kap | VC-t gyáron keresztül hozza létre | VC-t hoz létre |
| Életciklus | Egy navigációnként | childCoordinators | Singleton |
Router vs Navigator — Navigator — a Router elavult neve az iOS közösségben. Korai implementációkban a Navigator egy Singleton osztály volt átmeneti metódusokkal. Navigator — egy globális Router-singleton. A Navigator egyszerűbb, de rejtett függőségeket hoz létre. Protokollokon alapuló Router — tesztelhetőbb és modulárisabb. Navigator szigorú architektúra nélküli projektekhez alkalmas, Router — Clean Architecture és VIPER számára.
Gyakran ismételt kérdések
Router — absztrakció az UINavigationController felett (push/present/pop). Coordinator — réteg a Router felett, amely a flow-t kezeli (melyik képernyő jelenjen meg következőként). A Router a mechanikáért felel, a Coordinator — a navigációs logikáért. A Coordinator a Router-t használja az átmenetek végrehajtásához. VIPER-ben a Router mindkét funkciót egyesíti, MVVM-C-ben — szét vannak választva.
Nem. 3-5 képernyős alkalmazásoknál a navigationController.pushViewController közvetlen hívása normális megoldás. A Router 10+ képernyő, deep link-ek, push értesítések és moduláris architektúra esetén válik hasznossá. A Router leegyszerűsíti a navigáció tesztelését és lehetővé teszi az átmenetek központosított feldolgozását bármilyen forrásból (gomb, deep link, push).
URL Router — a Router kiterjesztése, amely elemzi a deep link-eket és universal link-eket. Az URL Router meghatározza az URL és az alkalmazás képernyője közötti megfelelést. Például, route myapp://profile/42 → ProfileViewController(userId: 42). Az URL Router központosítja a deep link-ek feldolgozását különböző forrásokból: push értesítések, QR kódok, email linkek, univerzális linkek.
Igen. A Router működhet önállóan — globális Navigatorként vagy VIPER modul komponenseként. Router Coordinator nélkül — csupán az UINavigationController beágyazása egy protokoll mögé. Coordinator Router nélkül — nehezebb (a ViewController maga hívja a push/present-et), de lehetséges. Legjobb gyakorlat — Router + Coordinator együtt.
Funkcionálisan — igen, mindkettő központosítja a navigációt. Az implementáció eltérő: iOS Router — protokoll + osztály az UINavigationController körül; Android — Jetpack Navigation Component XML gráffal és NavController-rel. Az Android Router be van építve a framework-be (Navigation Component), iOS Router — külső implementáció. Az Android Router támogatja a Safe Args-t, az iOS Router nem — generics vagy Codable szükséges.
Összegzés
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is