Ao criar apps iOS, chega um momento em que o view controller fica inchado.
O código de UI, o processamento de dados e até pushViewController a navegação acabam misturados.
Se você deixar assim, quanto mais complexo o fluxo, mais difícil fica rastrear cada transição.
Hoje vamos falar sobre o padrão Coordinator do iOS, que separa a navegação do view controller.
Em resumo, o padrão Coordinator adiciona um objeto separado responsável pela navegação. O view controller desenha a tela e deixa o Coordinator decidir para onde ir em seguida.
O que é o padrão Coordinator?
Em uma linha:
Coordinator é um objeto dedicado ao fluxo de navegação entre telas.
Antes, o view controller desenhava a tela, recebia a entrada do usuário e também apresentava a próxima tela.
Dessa tarefa, extraímos a transição para a próxima tela e a delegamos ao Coordinator.
Assim, o view controller só precisa informar ao Coordinator que um botão foi pressionado.
Ele não precisa saber para onde nem como a transição acontecerá.
Os view controllers deixam de conhecer uns aos outros diretamente. A tela A não precisa conhecer a B, o que facilita muito a reutilização.
Por que separar a navegação?
Manter código de navegação dentro do view controller causa vários problemas.
Reuni aqui os que vivi na prática.
- Dependências entrelaçadas: A cria B diretamente; se B mudar, A também precisa ser alterada.
- Sem reutilização: para usar a mesma tela em outro fluxo, é preciso alterar a navegação novamente.
- Fluxo difícil de entender: a lógica de navegação se espalha por 20 arquivos e a visão geral desaparece.
- Testes difíceis: a navegação fica presa ao view controller, dificultando testes isolados.
Centralizar a navegação resolve boa parte desses problemas.
Basta olhar um arquivo de Coordinator para entender o fluxo de telas do app.
Como criar um Coordinator?
A estrutura básica é mais simples do que parece.
Primeiro, defina um protocolo comum. start() funciona como ponto de entrada.
protocol Coordinator: AnyObject {
var navigationController: UINavigationController { get }
func start()
}
Depois, crie o Coordinator real, responsável por criar e apresentar a primeira tela.
final class MainCoordinator: Coordinator {
let navigationController: UINavigationController
init(nav: UINavigationController) { self.navigationController = nav }
func start() {
let vc = HomeViewController()
vc.coordinator = self // canal para receber solicitações de navegação
navigationController.pushViewController(vc, animated: false)
}
func showDetail() { // a navegação para a próxima tela acontece somente aqui
let vc = DetailViewController()
navigationController.pushViewController(vc, animated: true)
}
}
Quando o botão é pressionado, o view controller só precisa chamar coordinator?.showDetail().
Você não precisa se preocupar com o destino nem com a forma de inserir a tela.
Qual é a diferença para fazer push ou usar Segue diretamente?
É uma dúvida comum, então organizei tudo em uma tabela.
| Item | Push direto / Segue | Padrão Coordinator |
|---|---|---|
| Local da navegação | Dentro do view controller | Centralizada no Coordinator |
| Dependência entre telas | Conhecem-se diretamente | Não precisam se conhecer |
| Reutilização de telas | Difícil | Fácil |
| Entendimento do fluxo | Espalhado por vários arquivos | Gerenciado em um só lugar |
| Trabalho inicial | Menor | Um pouco maior |
Como você pode ver, Coordinator não resolve tudo.
Em um app pequeno com apenas duas ou três telas, ele pode aumentar o código e acabar complicando mais do que ajudando.
Por outro lado, em apps com fluxos ramificados, como login, onboarding ou troca de abas, ele realmente vale a pena.
Perguntas frequentes
P. Se há várias telas, devo criar vários Coordinators?
Sim. Normalmente, eles são divididos por fluxo: login, fluxo principal e assim por diante. É comum um Coordinator superior gerenciar os inferiores como filhos.
P. Quando devo remover um Coordinator filho?
Quando o fluxo termina, o pai precisa remover a referência ao filho do array. Caso contrário, ele continua na memória e causa um vazamento.
P. Também é usado com SwiftUI?
O SwiftUI tem roteamento baseado em NavigationStack e path, então a abordagem é um pouco diferente. Ainda assim, a ideia de extrair o fluxo para um objeto separado pode ser aplicada da mesma forma.
No início, adicionar mais um arquivo pode parecer trabalhoso.
Mas, quando o app crescer para dez ou vinte telas, você vai perceber o valor dessa estrutura.
Comece extraindo um fluxo pequeno para um Coordinator. Você sentirá o view controller ficar bem mais leve. 🙂
Artigos recomendados
- [Fundamentos de Swift #1] O que é um Optional do Swift? Na verdade, é um enum (incluindo 5 formas práticas de desembrulhá-lo)
- [Fundamentos de Swift #2] Guia completo sobre closures do Swift: por que captura, [weak self] e @escaping andam juntos
- [Filosofia do Swift #1] Por que o Swift é tão rígido? Um resumo completo das três filosofias: Safe, Fast e Expressive

