Router (router) — arkitekturmönster som centraliserar navigeringslogiken mellan applikationens skärmar. Router bestämmer vart och hur man navigerar när en händelse inträffar. I iOS abstraherar Router UINavigationController och segue, i Android — FragmentManager och NavController från Jetpack Navigation. Router kan fungera som ett självständigt mönster eller som en komponent i Coordinator (Router-per-Coordinator) och VIPER (Router-per-module). Mer — i Android Navigation Component Guide.
Huvudpunkter
Router — mönster som kapslar in logiken för övergångar mellan skärmar. Huvudidé: ViewController (eller Presenter/ViewModel) anropar inte direkt navigationController.pushViewController, utan informerar Router om avsikten att navigera. Router bestämmer vilken ViewController som ska skapas och hur den ska visas. Router kan vara enkel (en samling navigeringsmetoder) eller komplex (med routing enligt URL-scheman, deep links och push-meddelanden).
Tre tillvägagångssätt för Router — Router som tjänst (global Navigator-singleton), Router som modulkomponent (VIPER-metoden), Router som protokoll (abstraktion för Coordinator). Global Router — enkelhet (en instans per applikation) — populär i små projekt. Modulär Router — varje funktion har sin egen Router — standard för VIPER och Clean Architecture. Protokollbaserad Router — används i Coordinator: RouterProtocol med metoder push/present/pop/dismiss.
| Tillvägagångssätt | Struktur | När ska användas |
|---|---|---|
| Global Navigator | Singleton med övergångsmetoder | Små projekt, prototyper |
| Modulär Router | Router inom varje modul | VIPER, Clean Architecture |
| Protokollbaserad Router | Protocol + implementering för Coordinator | MVVM-C, Coordinator-mönster |
Mönstrets historia — Router uppstod i webbramverk (URL Router — Rails Routes, Express.js) och anpassades för mobil utveckling. I iOS blev Router populär med tillväxten av VIPER (2015-2017). I Android är Router — en del av Jetpack Navigation (2018), Googles standardlösning för navigering. Nu är Router en oskiljaktig del av varje modulär arkitektur för mobila applikationer.
iOS Router — protokoll med metoder för olika typer av navigering. Grundimplementeringen omsluter UINavigationController. Router kan hantera modal presentation (present/dismiss), push/pop i navigeringsstacken, showDetail för Split View Controller och helskärmspresentationer (iOS 13+). Router skapar inte ViewController — den tar emot en färdig instans och visar den. Skapandet av ViewController utförs av en fabrik (Assembly, DI-container) eller Coordinator.
// RouterProtocol för 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)
}
// Implementering av Router via 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 — skapande av modul med Router
protocol ProfileModuleFactory {
func makeProfileView() -> UIViewController
}
Router i VIPER — varje VIPER-modul har sin egen Router som vet vilka skärmar som ska öppnas från den aktuella modulen. Router skapar ViewController för nästa modul via en fabrik och överför kontrollen. Router i VIPER — den enda komponenten som innehåller import UIKit (förutom View). Detta förenklar testning av Presenter och Interactor: de är inte beroende av UIKit.
Jetpack Navigation Component — standardrouter för Android, rekommenderad av Google sedan 2018. Navigering definieras i en XML-graf (nav_graph.xml): noder (fragments, activities, destinations) och kanter (actions). NavController hanterar övergångar: anropar FragmentManager.replace/add och hanterar tillbakahögen (back stack). Navigation Component stöder deep links, argument, övergångsanimationer och Safe Args för typsäker dataöverföring.
// nav_graph.xml — navigeringsgraf
<!-- @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 — navigering via 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 — typsäkra argument
class HomeFragmentArgs : NavArgs {
val userId: Int get() = arguments?.getInt("userId") ?: 0
}
// NavigationUI — integrering med 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 links i Android Navigation — Navigation Component stöder explicita deep links (PendingIntent + NavDeepLinkBuilder) och implicita (genom intent-filter i manifestet). Deep link kan leda till vilken nod som helst i nav_graph. Hantering av push-meddelanden: Notification skapar en PendingIntent med NavDeepLinkBuilder som återställer tillbakahögen och öppnar önskad skärm. Navigation Component hanterar automatiskt Up/Back-knapparna och återför användaren till föregående skärm.
URL Routing — routing baserat på URL-scheman, deep links och universal links. Applikationen registrerar ett schema (myapp://profile/42) eller en universal link (https://example.com/profile/42). Router tolkar URL:en, extraherar parametrar (userId=42) och öppnar motsvarande skärm. URL Routing möjliggör öppning av applikationen utifrån: via en länk från webbplats, e-post, push-meddelande, QR-kod. iOS använder NSUserActivity + universal link, Android — intent-filter + deep link.
// URL Router — tolkning av deep link och navigering
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 — mappning av URL till rutter
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 i praktiken — applikationen måste hantera deep link i tre tillstånd: inte startad (start med deep link), i bakgrunden (återställning från bakgrund), aktiv (onNewIntent/SceneDelegate). URL Router måste korrekt återställa tillbakahögen: vid deep link till profilsidan måste användaren kunna trycka på Back och återgå till föregående skärm. iOS och Android hanterar tillbakahögen olika — Router måste ta hänsyn till plattformsspecifika egenskaper.
Router vs Coordinator — Router svarar på frågan "hur visar vi skärmen?" (push/present), Coordinator — "vilken skärm visar vi?" (vilket flöde ska startas). Router — navigeringsverktyg, Coordinator — flödesorganisatör. Router används av Coordinator. I VIPER utför Router båda funktionerna: bestämmer vart man ska navigera och hur man visar. I MVVM-C är Router — ett separat protokoll som används av Coordinator. Mönstren utesluter inte varandra utan kompletterar varandra.
| Egenskap | Router | Coordinator | Navigator |
|---|---|---|---|
| Fokus | Navigeringsmekanik | Navigeringsflöde | Global åtkomst |
| Nivå | UI-ramverk (push/present) | Affärslogik (flöde) | Infrastruktur |
| Skapande av skärmar | Tar emot färdig VC | Skapar VC via fabrik | Skapar VC |
| Livscykel | En per navigering | childCoordinators | Singleton |
Router vs Navigator — Navigator — föråldrad benämning på Router i iOS-communityt. I tidiga implementeringar var Navigator en Singleton-klass med övergångsmetoder. Navigator — en global Router-singleton. Navigator är enklare men skapar dolda beroenden. Router på protokoll — mer testbar och mer modulär. Navigator passar för projekt utan strikt arkitektur, Router — för Clean Architecture och VIPER.
Vanliga frågor
Router — abstraktion över UINavigationController (push/present/pop). Coordinator — lager ovanpå Router som hanterar flödet (vilken skärm som ska visas härnäst). Router ansvarar för mekaniken, Coordinator — för navigeringslogiken. Coordinator använder Router för att utföra övergångar. I VIPER kombinerar Router båda funktionerna, i MVVM-C är de separerade.
Nej. För applikationer med 3-5 skärmar är direkta anrop av navigationController.pushViewController en normal lösning. Router blir användbar vid 10+ skärmar, deep links, push-meddelanden och modulär arkitektur. Router förenklar testning av navigering och möjliggör centraliserad hantering av övergångar från valfri källa (knapp, deep link, push).
URL Router — en förlängning av Router som tolkar deep links och universal links. URL Router bestämmer överensstämmelsen mellan URL och applikationens skärm. Till exempel, route myapp://profile/42 → ProfileViewController(userId: 42). URL Router centraliserar hanteringen av deep links från olika källor: push-meddelanden, QR-koder, e-postlänkar, universella länkar.
Ja. Router kan fungera självständigt — som global Navigator eller som komponent i en VIPER-modul. Router utan Coordinator — bara inkapsling av UINavigationController bakom ett protokoll. Coordinator utan Router — svårare (ViewController anropar själv push/present), men möjligt. Bästa praxis — Router + Coordinator tillsammans.
Funktionellt — ja, båda centraliserar navigering. Implementeringen skiljer sig: iOS Router — protokoll + klass runt UINavigationController; Android — Jetpack Navigation Component med XML-graf och NavController. Android Router är inbyggd i ramverket (Navigation Component), iOS Router — extern implementering. Android Router stöder Safe Args, iOS Router — inte (behöver generics eller Codable).
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också