Router — fondamenti del pattern di routing in iOS e Android

Autore: IT Sectr Pubblicato: 2026-02-19 Tempo di lettura: 9 min

Router (instradatore) — un pattern architetturale che centralizza la logica di navigazione tra gli schermi dell'applicazione. Il Router determina dove e come navigare al verificarsi di un evento. In iOS, il Router astrae UINavigationController e segue; in Android — FragmentManager e NavController di Jetpack Navigation. Il Router può funzionare come pattern indipendente o come componente di Coordinator (Router-per-Coordinator) e VIPER (Router-per-module). Maggiori dettagli — nella Android Navigation Component Guide.

Punti chiave

  • Router — pattern di routing che centralizza la navigazione tra schermi
  • Due livelli — il Router può essere un componente di Coordinator o un pattern indipendente
  • iOS Router — astrazione su UINavigationController con metodi push/present/pop
  • Android Router — Jetpack Navigation Component con NavController e deep link
  • URL Routing — routing basato su schemi URL (deeplink, universal link)

Cos'è Router: essenza del pattern di routing

Router — un pattern che incapsula la logica di transizione tra schermi. L'idea principale: ViewController (o Presenter/ViewModel) non chiama direttamente navigationController.pushViewController, ma notifica al Router l'intenzione di navigare. Il Router decide quale ViewController creare e come visualizzarlo. Il Router può essere semplice (un insieme di metodi di navigazione) o complesso (con routing tramite schemi URL, deep link e notifiche push).

Tre approcci al Router — Router come servizio (Navigator singleton globale), Router come componente di modulo (approccio VIPER), Router come protocollo (astrazione per Coordinator). Router globale — semplicità (un'istanza per applicazione) — popolare nei progetti piccoli. Router modulare — ogni funzionalità ha il proprio Router — standard VIPER e Clean Architecture. Router a protocollo — usato in Coordinator: RouterProtocol con metodi push/present/pop/dismiss.

ApproccioStrutturaQuando usarlo
Navigator globaleSingleton con metodi di navigazioneProgetti piccoli, prototipi
Router modulareRouter dentro ogni moduloVIPER, Clean Architecture
Router a protocolloProtocollo + implementazione per CoordinatorMVVM-C, Coordinator pattern

Storia del pattern — il Router è apparso nei framework web (URL Router — Rails Routes, Express.js) ed è stato adattato per lo sviluppo mobile. In iOS, il Router è diventato popolare con l'ascesa di VIPER (2015-2017). In Android, il Router fa parte di Jetpack Navigation (2018), la soluzione standard di Google per la navigazione. Oggi, il Router è parte integrante di qualsiasi architettura modulare di applicazioni mobili.

Router in iOS: implementazione in Swift

iOS Router — un protocollo con metodi per vari tipi di navigazione. L'implementazione di base avvolge UINavigationController. Il Router può gestire presentazioni modali (present/dismiss), push/pop nello stack di navigazione, showDetail per Split View Controller e presentazioni a schermo intero (iOS 13+). Il Router non crea ViewController — riceve un'istanza pronta e la visualizza. La creazione di ViewController è gestita da una factory (Assembly, contenitore DI) o Coordinator.

swift
// RouterProtocol per 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)
}

// Implementazione di Router tramite 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 — creazione di un modulo con Router
protocol ProfileModuleFactory {
    func makeProfileView() -> UIViewController
}

Router in VIPER — ogni modulo VIPER ha il proprio Router, che sa quali schermi aprire dal modulo corrente. Il Router crea il ViewController del modulo successivo tramite una factory e passa il controllo. Il Router in VIPER è l'unico componente che contiene import UIKit (tranne View). Questo semplifica i test di Presenter e Interactor: non dipendono da UIKit.

Router in Android: Jetpack Navigation Component

Jetpack Navigation Component — il router standard per Android, raccomandato da Google dal 2018. La navigazione è definita in un grafo XML (nav_graph.xml): nodi (fragments, activities, destinations) e archi (actions). NavController gestisce le transizioni: chiama FragmentManager.replace/add e gestisce lo stack indietro. Navigation Component supporta deep link, argomenti, animazioni di transizione e Safe Args per il passaggio type-safe dei dati.

kotlin
// nav_graph.xml — grafo di navigazione
<!-- @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 — navigazione tramite 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 — argomenti type-safe
class HomeFragmentArgs : NavArgs {
    val userId: Int get() = arguments?.getInt("userId") ?: 0
}

// NavigationUI — integrazione con 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 in Android Navigation — Navigation Component supporta deep link espliciti (PendingIntent + NavDeepLinkBuilder) e impliciti (tramite intent-filter nel manifesto). Un deep link può portare a qualsiasi nodo del nav_graph. Gestione delle notifiche push: la Notification crea un PendingIntent con NavDeepLinkBuilder, che ripristina lo stack indietro e apre lo schermo desiderato. Navigation Component gestisce automaticamente i pulsanti Up/Back e riporta l'utente allo schermo precedente.

URL Routing: routing Deeplink e Universal Link

URL Routing — routing basato su schemi URL, deep link e universal link. L'app registra uno schema (myapp://profile/42) o un universal link (https://example.com/profile/42). Il Router analizza l'URL, estrae i parametri (userId=42) e apre lo schermo corrispondente. L'URL Routing permette di aprire l'app dall'esterno: tramite un link da un sito web, email, notifica push, codice QR. iOS usa NSUserActivity + universal link, Android — intent-filter + deep link.

swift
// URL Router — parsing deep link e navigazione
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 — mappatura degli URL con le route
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
            }
        }
    }
}

Routing deep link nella pratica — l'app deve gestire i deep link in tre stati: non avviata (avvio con deep link), in background (ripristino da background), attiva (onNewIntent/SceneDelegate). L'URL Router deve ripristinare correttamente lo stack indietro: quando si fa un deep link a uno schermo profilo, l'utente deve poter premere Indietro e tornare allo schermo precedente. iOS e Android gestiscono gli stack indietro in modo diverso — il Router deve tenere conto delle peculiarità della piattaforma.

Confronto tra Router e Coordinator e Navigator

Router vs Coordinator — il Router risponde alla domanda "come mostrare uno schermo?" (push/present), il Coordinator — "quale schermo mostrare?" (quale flusso avviare). Il Router è uno strumento di navigazione, il Coordinator è un organizzatore di flussi. Il Coordinator usa il Router. In VIPER, il Router svolge entrambe le funzioni: decide dove andare e come mostrare. In MVVM-C, il Router è un protocollo separato usato dal Coordinator. I pattern non si escludono ma si complementano.

CaratteristicaRouterCoordinatorNavigator
FocusMeccanica di navigazioneFlusso di navigazioneAccesso globale
LivelloFramework UI (push/present)Logica di business (flusso)Infrastruttura
Creazione schermiRiceve VC prontoCrea VC tramite factoryCrea VC
Ciclo di vitaUno per navigazionechildCoordinatorsSingleton

Router vs Navigator — Navigator è un nome più vecchio per Router nella comunità iOS. Nelle prime implementazioni, Navigator era una classe Singleton con metodi di navigazione. Navigator è un Router singleton globale. Navigator è più semplice ma crea dipendenze nascoste. Il Router basato su protocolli è più testabile e modulare. Navigator è adatto a progetti senza architettura rigorosa, Router — per Clean Architecture e VIPER.

Domande frequenti

Qual è la differenza tra Router e Coordinator?

Router è un'astrazione su UINavigationController (push/present/pop). Coordinator è un livello sopra il Router che gestisce il flusso (quale schermo mostrare successivamente). Il Router si occupa della meccanica, il Coordinator della logica di navigazione. Il Coordinator usa il Router per eseguire le transizioni. In VIPER, il Router combina entrambe le funzioni; in MVVM-C, sono separate.

È obbligatorio usare Router in un'applicazione mobile?

No. Per applicazioni con 3-5 schermi, le chiamate dirette a navigationController.pushViewController sono una soluzione perfettamente valida. Il Router diventa utile con 10+ schermi, deep link, notifiche push e architettura modulare. Il Router semplifica i test di navigazione e permette la gestione centralizzata delle transizioni da qualsiasi fonte (pulsante, deep link, notifica push).

Come è correlato Router ai Deep Link?

URL Router è un'estensione del Router che analizza deep link e universal link. URL Router determina la corrispondenza tra URL e schermi dell'app. Ad esempio, rotta myapp://profile/42 → ProfileViewController(userId: 42). URL Router centralizza la gestione dei deep link da diverse fonti: notifiche push, codici QR, link email, link universali.

Si può usare Router senza Coordinator?

Sì. Il Router può funzionare indipendentemente — come Navigator globale o come componente di modulo VIPER. Router senza Coordinator è semplicemente incapsulare UINavigationController dietro un protocollo. Coordinator senza Router è più complesso (ViewController chiama push/present direttamente), ma possibile. La best practice è Router + Coordinator insieme.

Il Router in Android è lo stesso dell'iOS Router?

Funzionalmente — sì, entrambi centralizzano la navigazione. L'implementazione è diversa: iOS Router — protocollo + classe attorno a UINavigationController; Android — Jetpack Navigation Component con grafo XML e NavController. Android Router è integrato nel framework (Navigation Component), iOS Router è un'implementazione di terze parti. Android Router supporta Safe Args, iOS Router no (necessita di generics o Codable).

Riepilogo

  • Router — pattern di routing che incapsula la logica di transizione tra schermi
  • iOS Router — protocollo con metodi push/present/pop attorno a UINavigationController
  • Android Router — Jetpack Navigation Component con XML nav_graph e NavController
  • URL Routing — routing tramite deep link e universal link
  • Router vs Coordinator — Router gestisce la meccanica, Coordinator gestisce il flusso di navigazione
  • Applicazione — architettura modulare, deep link, test di navigazione

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche