Router — fundamentos del patrón de enrutamiento en iOS y Android

Autor: IT Sectr Publicado: 2026-02-19 Tiempo de lectura: 9 min

Router (enrutador) — un patrón arquitectónico que centraliza la lógica de navegación entre pantallas de la aplicación. Router determina hacia dónde y cómo navegar cuando ocurre un evento. En iOS, Router abstrae UINavigationController y segue; en Android — FragmentManager y NavController de Jetpack Navigation. Router puede funcionar como patrón independiente o como componente de Coordinator (Router-per-Coordinator) y VIPER (Router-per-module). Más detalles — en Android Navigation Component Guide.

Puntos clave

  • Router — patrón de enrutamiento que centraliza la navegación entre pantallas
  • Dos niveles — Router puede ser un componente de Coordinator o un patrón independiente
  • iOS Router — abstracción sobre UINavigationController con métodos push/present/pop
  • Android Router — Jetpack Navigation Component con NavController y deep links
  • URL Routing — enrutamiento basado en esquemas URL (deeplink, universal link)

Qué es Router: esencia del patrón de enrutamiento

Router — un patrón que encapsula la lógica de transición entre pantallas. La idea principal: ViewController (o Presenter/ViewModel) no llama directamente a navigationController.pushViewController, sino que notifica al Router su intención de navegar. El Router decide qué ViewController crear y cómo mostrarlo. Router puede ser simple (un conjunto de métodos de navegación) o complejo (con enrutamiento por esquemas URL, deep links y notificaciones push).

Tres enfoques de Router — Router como servicio (Navigator singleton global), Router como componente de módulo (enfoque VIPER), Router como protocolo (abstracción para Coordinator). Router global — simplicidad (una instancia por aplicación) — popular en proyectos pequeños. Router modular — cada feature tiene su propio Router — estándar VIPER y Clean Architecture. Router de protocolo — usado en Coordinator: RouterProtocol con métodos push/present/pop/dismiss.

EnfoqueEstructuraCuándo usarlo
Navigator globalSingleton con métodos de navegaciónProyectos pequeños, prototipos
Router modularRouter dentro de cada móduloVIPER, Clean Architecture
Router por protocoloProtocolo + implementación para CoordinatorMVVM-C, Coordinator pattern

Historia del patrón — Router apareció en frameworks web (URL Router — Rails Routes, Express.js) y fue adaptado para el desarrollo móvil. En iOS, Router se popularizó con el auge de VIPER (2015-2017). En Android, Router es parte de Jetpack Navigation (2018), la solución estándar de Google para navegación. Hoy, Router es una parte integral de cualquier arquitectura modular de aplicaciones móviles.

Router en iOS: implementación en Swift

iOS Router — un protocolo con métodos para varios tipos de navegación. La implementación básica envuelve UINavigationController. Router puede manejar presentaciones modales (present/dismiss), push/pop en el stack de navegación, showDetail para Split View Controller y presentaciones a pantalla completa (iOS 13+). Router no crea ViewControllers — recibe una instancia lista y la muestra. La creación de ViewControllers la maneja una fábrica (Assembly, contenedor DI) o Coordinator.

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

// Implementación de Router mediante 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 — creación de un módulo con Router
protocol ProfileModuleFactory {
    func makeProfileView() -> UIViewController
}

Router en VIPER — cada módulo VIPER tiene su propio Router, que sabe qué pantallas abrir desde el módulo actual. El Router crea el ViewController del siguiente módulo a través de una fábrica y pasa el control. Router en VIPER es el único componente que contiene import UIKit (excepto View). Esto simplifica las pruebas de Presenter e Interactor: no dependen de UIKit.

Router en Android: Jetpack Navigation Component

Jetpack Navigation Component — el router estándar para Android, recomendado por Google desde 2018. La navegación se define en un grafo XML (nav_graph.xml): nodos (fragments, activities, destinations) y aristas (actions). NavController gestiona las transiciones: llama a FragmentManager.replace/add y gestiona la pila de retroceso. Navigation Component admite deep links, argumentos, animaciones de transición y Safe Args para paso de datos type-safe.

kotlin
// nav_graph.xml — grafo de navegación
<!-- @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 — navegación mediante 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 — argumentos type-safe
class HomeFragmentArgs : NavArgs {
    val userId: Int get() = arguments?.getInt("userId") ?: 0
}

// NavigationUI — integración 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 links en Android Navigation — Navigation Component admite deep links explícitos (PendingIntent + NavDeepLinkBuilder) e implícitos (mediante intent-filter en el manifiesto). Un deep link puede conducir a cualquier nodo del nav_graph. Manejo de notificaciones push: Notification crea un PendingIntent con NavDeepLinkBuilder, que restaura la pila de retroceso y abre la pantalla deseada. Navigation Component maneja automáticamente los botones Up/Back y devuelve al usuario a la pantalla anterior.

URL Routing: enrutamiento con Deeplink y Universal Link

URL Routing — enrutamiento basado en esquemas URL, deep links y universal links. La aplicación registra un esquema (myapp://profile/42) o universal link (https://example.com/profile/42). El Router analiza la URL, extrae parámetros (userId=42) y abre la pantalla correspondiente. URL Routing permite abrir la aplicación desde fuera: mediante un enlace desde un sitio web, correo electrónico, notificación push, código QR. iOS usa NSUserActivity + universal link, Android — intent-filter + deep link.

swift
// URL Router — análisis de deep link y navegación
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 — mapeo de URLs con rutas
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
            }
        }
    }
}

Enrutamiento deep link en la práctica — la aplicación debe manejar deep links en tres estados: no iniciada (inicio con deep link), en segundo plano (restauración desde background), activa (onNewIntent/SceneDelegate). El URL Router debe restaurar correctamente la pila de retroceso: al hacer deep link a una pantalla de perfil, el usuario debe poder presionar Atrás y volver a la pantalla anterior. iOS y Android manejan las pilas de retroceso de manera diferente — el Router debe tener en cuenta las particularidades de cada plataforma.

Comparación de Router con Coordinator y Navigator

Router vs Coordinator — Router responde a la pregunta "¿cómo mostrar una pantalla?" (push/present), Coordinator — "¿qué pantalla mostrar?" (qué flujo iniciar). Router es una herramienta de navegación, Coordinator es un organizador de flujos. Coordinator utiliza Router. En VIPER, Router realiza ambas funciones: decide hacia dónde ir y cómo mostrarlo. En MVVM-C, Router es un protocolo separado usado por Coordinator. Los patrones no se excluyen, sino que se complementan.

CaracterísticaRouterCoordinatorNavigator
EnfoqueMecánica de navegaciónFlujo de navegaciónAcceso global
NivelFramework UI (push/present)Lógica de negocio (flujo)Infraestructura
Creación de pantallasRecibe VC listoCrea VC mediante fábricaCrea VC
Ciclo de vidaUno por navegaciónchildCoordinatorsSingleton

Router vs Navigator — Navigator es un nombre antiguo para Router en la comunidad iOS. En implementaciones tempranas, Navigator era una clase Singleton con métodos de navegación. Navigator es un Router singleton global. Navigator es más simple pero crea dependencias ocultas. El Router basado en protocolos es más testeable y modular. Navigator es adecuado para proyectos sin arquitectura estricta, Router — para Clean Architecture y VIPER.

Preguntas frecuentes

¿Cuál es la diferencia entre Router y Coordinator?

Router es una abstracción sobre UINavigationController (push/present/pop). Coordinator es una capa sobre Router que gestiona el flujo (qué pantalla mostrar a continuación). Router se encarga de la mecánica, Coordinator de la lógica de navegación. Coordinator utiliza Router para realizar las transiciones. En VIPER, Router combina ambas funciones; en MVVM-C, están separadas.

¿Es obligatorio usar Router en una aplicación móvil?

No. Para aplicaciones con 3-5 pantallas, las llamadas directas a navigationController.pushViewController son una solución perfectamente válida. Router se vuelve útil con 10+ pantallas, deep links, notificaciones push y arquitectura modular. Router simplifica las pruebas de navegación y permite manejar de forma centralizada las transiciones desde cualquier fuente (botón, deep link, notificación push).

¿Cómo se relaciona Router con los Deep Links?

URL Router es una extensión de Router que analiza deep links y universal links. URL Router determina la correspondencia entre URLs y pantallas de la aplicación. Por ejemplo, ruta myapp://profile/42 → ProfileViewController(userId: 42). URL Router centraliza el manejo de deep links de diferentes fuentes: notificaciones push, códigos QR, enlaces de correo electrónico, enlaces universales.

¿Se puede usar Router sin Coordinator?

Sí. Router puede funcionar de forma independiente — como Navigator global o como componente de un módulo VIPER. Router sin Coordinator es simplemente encapsular UINavigationController detrás de un protocolo. Coordinator sin Router es más complejo (ViewController llama a push/present directamente), pero es posible. La mejor práctica es Router + Coordinator juntos.

¿Router en Android es lo mismo que iOS Router?

Funcionalmente — sí, ambos centralizan la navegación. La implementación es diferente: iOS Router — protocolo + clase alrededor de UINavigationController; Android — Jetpack Navigation Component con grafo XML y NavController. Android Router está integrado en el framework (Navigation Component), iOS Router es una implementación de terceros. Android Router admite Safe Args, iOS Router no (necesita generics o Codable).

Resumen

  • Router — patrón de enrutamiento que encapsula la lógica de transición entre pantallas
  • iOS Router — protocolo con métodos push/present/pop alrededor de UINavigationController
  • Android Router — Jetpack Navigation Component con XML nav_graph y NavController
  • URL Routing — enrutamiento mediante deep links y universal links
  • Router vs Coordinator — Router maneja la mecánica, Coordinator maneja el flujo de navegación
  • Aplicación — arquitectura modular, deep links, pruebas de navegación

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también