Router (roteador) — um padrão arquitetural que centraliza a lógica de navegação entre telas da aplicação. O Router determina para onde e como navegar quando um evento ocorre. No iOS, o Router abstrai UINavigationController e segue; no Android — FragmentManager e NavController do Jetpack Navigation. O Router pode funcionar como um padrão independente ou como um componente do Coordinator (Router-per-Coordinator) e VIPER (Router-per-module). Mais detalhes — em Android Navigation Component Guide.
Principais pontos
Router — um padrão que encapsula a lógica de transição entre telas. A ideia principal: ViewController (ou Presenter/ViewModel) não chama navigationController.pushViewController diretamente, mas notifica o Router sobre a intenção de navegar. O Router decide qual ViewController criar e como exibi-lo. O Router pode ser simples (um conjunto de métodos de navegação) ou complexo (com roteamento por esquemas de URL, deep links e notificações push).
Três abordagens para Router — Router como serviço (Navigator singleton global), Router como componente de módulo (abordagem VIPER), Router como protocolo (abstração para Coordinator). Router global — simplicidade (uma instância por aplicação) — popular em projetos pequenos. Router modular — cada funcionalidade tem seu próprio Router — padrão VIPER e Clean Architecture. Router de protocolo — usado no Coordinator: RouterProtocol com métodos push/present/pop/dismiss.
| Abordagem | Estrutura | Quando usar |
|---|---|---|
| Navigator global | Singleton com métodos de navegação | Projetos pequenos, protótipos |
| Router modular | Router dentro de cada módulo | VIPER, Clean Architecture |
| Router por protocolo | Protocolo + implementação para Coordinator | MVVM-C, Coordinator pattern |
História do padrão — Router surgiu em frameworks web (URL Router — Rails Routes, Express.js) e foi adaptado para desenvolvimento mobile. No iOS, o Router tornou-se popular com a ascensão do VIPER (2015-2017). No Android, o Router faz parte do Jetpack Navigation (2018), a solução padrão do Google para navegação. Hoje, o Router é parte integrante de qualquer arquitetura modular de aplicações móveis.
iOS Router — um protocolo com métodos para vários tipos de navegação. A implementação básica envolve UINavigationController. O Router pode lidar com apresentações modais (present/dismiss), push/pop na pilha de navegação, showDetail para Split View Controller e apresentações em tela cheia (iOS 13+). O Router não cria ViewControllers — ele recebe uma instância pronta e a exibe. A criação de ViewControllers é feita por uma fábrica (Assembly, contêiner DI) ou Coordinator.
// 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)
}
// Implementação de 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 — criação de um módulo com Router
protocol ProfileModuleFactory {
func makeProfileView() -> UIViewController
}
Router no VIPER — cada módulo VIPER tem seu próprio Router, que sabe quais telas abrir a partir do módulo atual. O Router cria o ViewController do próximo módulo através de uma fábrica e passa o controle. O Router no VIPER é o único componente que contém import UIKit (exceto View). Isso simplifica os testes de Presenter e Interactor: eles não dependem do UIKit.
Jetpack Navigation Component — o roteador padrão para Android, recomendado pelo Google desde 2018. A navegação é definida em um grafo XML (nav_graph.xml): nós (fragments, activities, destinations) e arestas (actions). O NavController gerencia as transições: chama FragmentManager.replace/add e gerencia a pilha de retorno. O Navigation Component suporta deep links, argumentos, animações de transição e Safe Args para passagem de dados type-safe.
// nav_graph.xml — grafo de navegação
<!-- @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 — navegação 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 — argumentos type-safe
class HomeFragmentArgs : NavArgs {
val userId: Int get() = arguments?.getInt("userId") ?: 0
}
// NavigationUI — integração com 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 no Android Navigation — o Navigation Component suporta deep links explícitos (PendingIntent + NavDeepLinkBuilder) e implícitos (através de intent-filter no manifesto). Um deep link pode levar a qualquer nó do nav_graph. Tratamento de notificações push: a Notification cria um PendingIntent com NavDeepLinkBuilder, que restaura a pilha de retorno e abre a tela desejada. O Navigation Component lida automaticamente com os botões Up/Back e retorna o usuário à tela anterior.
URL Routing — roteamento baseado em esquemas de URL, deep links e universal links. A aplicação registra um esquema (myapp://profile/42) ou universal link (https://example.com/profile/42). O Router analisa a URL, extrai parâmetros (userId=42) e abre a tela correspondente. O URL Routing permite abrir a aplicação externamente: através de um link de site, email, notificação push, código QR. O iOS usa NSUserActivity + universal link, o Android — intent-filter + deep link.
// URL Router — análise de deep link e navegação
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 — mapeamento de URLs com rotas
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
}
}
}
}
Roteamento deep link na prática — a aplicação deve lidar com deep links em três estados: não iniciada (inicialização com deep link), em segundo plano (restauração do background), ativa (onNewIntent/SceneDelegate). O URL Router deve restaurar corretamente a pilha de retorno: ao fazer deep link para uma tela de perfil, o usuário deve poder pressionar Voltar e retornar à tela anterior. O iOS e o Android lidam com pilhas de retorno de forma diferente — o Router deve considerar as particularidades da plataforma.
Router vs Coordinator — o Router responde à pergunta "como mostrar uma tela?" (push/present), o Coordinator — "qual tela mostrar?" (qual fluxo iniciar). O Router é uma ferramenta de navegação, o Coordinator é um organizador de fluxos. O Coordinator usa o Router. No VIPER, o Router desempenha ambas as funções: decide para onde ir e como mostrar. No MVVM-C, o Router é um protocolo separado usado pelo Coordinator. Os padrões não se excluem, mas se complementam.
| Característica | Router | Coordinator | Navigator |
|---|---|---|---|
| Foco | Mecânica de navegação | Fluxo de navegação | Acesso global |
| Nível | Framework UI (push/present) | Lógica de negócio (fluxo) | Infraestrutura |
| Criação de telas | Recebe VC pronto | Cria VC via fábrica | Cria VC |
| Ciclo de vida | Um por navegação | childCoordinators | Singleton |
Router vs Navigator — Navigator é um nome mais antigo para Router na comunidade iOS. Em implementações iniciais, o Navigator era uma classe Singleton com métodos de navegação. Navigator é um Router singleton global. Navigator é mais simples, mas cria dependências ocultas. O Router baseado em protocolos é mais testável e modular. Navigator é adequado para projetos sem arquitetura estrita, Router — para Clean Architecture e VIPER.
Perguntas frequentes
Router é uma abstração sobre UINavigationController (push/present/pop). Coordinator é uma camada acima do Router que gerencia o fluxo (qual tela mostrar a seguir). O Router lida com a mecânica, o Coordinator com a lógica de navegação. O Coordinator usa o Router para realizar as transições. No VIPER, o Router combina ambas as funções; no MVVM-C, elas são separadas.
Não. Para aplicações com 3-5 telas, chamadas diretas a navigationController.pushViewController são uma solução perfeitamente válida. O Router torna-se útil com 10+ telas, deep links, notificações push e arquitetura modular. O Router simplifica os testes de navegação e permite o tratamento centralizado de transições de qualquer fonte (botão, deep link, notificação push).
URL Router é uma extensão do Router que analisa deep links e universal links. O URL Router determina o mapeamento entre URLs e telas da aplicação. Por exemplo, rota myapp://profile/42 → ProfileViewController(userId: 42). O URL Router centraliza o tratamento de deep links de diferentes fontes: notificações push, códigos QR, links de email, links universais.
Sim. O Router pode funcionar independentemente — como um Navigator global ou como um componente de módulo VIPER. Router sem Coordinator é simplesmente encapsular UINavigationController atrás de um protocolo. Coordinator sem Router é mais complexo (ViewController chama push/present diretamente), mas é possível. A melhor prática é Router + Coordinator juntos.
Funcionalmente — sim, ambos centralizam a navegação. A implementação é diferente: iOS Router — protocolo + classe em torno de UINavigationController; Android — Jetpack Navigation Component com grafo XML e NavController. O Android Router está integrado ao framework (Navigation Component), o iOS Router é uma implementação de terceiros. O Android Router suporta Safe Args, o iOS Router não (precisa de generics ou Codable).
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também