O padrão MVVM é basicamente o padrão mais consolidado que temos para desenvolvimento Mobile hoje em dia, além de ser extremamente modular e muito mais fácil de se manter do que padrões como MVC por exemplo.
Mas se você já fez navegação usando SwiftUI, tentando balancear estados dispersos e informações dentre páginas, sabe como o SwiftUI está longe de ser a melhor forma de fazer navegação, apesar das NavigationStacks e TabViews poderosas, a dor de cabeça para manter isso é demais.
E por isso existe o padrão de coordinators (para SwiftUI)!!
De forma bem rudimentar, o que o coordinator faz é:
- Cuida da navegação, então você pede para o coordinator te enviar para alguma página, ao invés de usar uma NavigationLink
- Cria as views e as view models, mantendo assim as “regras” do MVVM
- Mantém o estado de navegação de várias sessões do APP
Isso facilita nossa vida, porque agora que se quisermos levar o usuário para outra página, trazendo consigo informações, ao invés de :
struct Page1: View {
@Binding var GoToPage2: Bool
var body: some View {
VStack{
Button("Go To Page"){
GoToPage2 = true
print(GoToPage2)
}
// 👇 pra onde ir 👇 qual informação levar
NavigationLink(destination: Page2(), isActive: $GoToPage2){
EmptyView()
}
}
.navigationTitle("Page 1")
}
}
struct Page2: View {
var body: some View {
Text("page 2!")
.padding()
.navigationTitle("Page 2")
}
}
struct ContentView: View {
@State var GoToPage2 = false
var body: some View {
NavigationView{
VStack{// 👇 responsabilidade dividida dentre reendenizar
// conteúdo e fazer lógica de navegação
NavigationLink(destination: Page1(GoToPage2: $GoToPage2)){
Text("Signe In")
.padding()
.background(.regularMaterial)
.colorScheme(.dark)
.cornerRadius(12)
.font(.largeTitle.bold())
.foregroundColor(.primary)
}
}
.navigationTitle("App")
}
}
}
// Original post - https://stackoverflow.com/questions/72010446/how-do-i-use-a-navigationlink-in-swiftuiIsso trás alguns problemas, como por exemplo, os estados de dados sendo passados de páginas pais ↔ filhas, a testabilidade das funções (quase zero por conta de lógica em views) e escalabilidade extremamente limitada
Mas com o padrão MVVM+C, podemos fazer:
// criação de rotas "type safe"
enum Routes: Identifiable, Hashable {
case home
case list
case details(_ pokemon: PokemonModel?)
var id: String {
switch self {
case .home:
return "Home"
case .list:
return "List"
case .details:
return "Details"
}
}
}
struct HomeView: View {
@Environment(AppCoordinator.self) private var coordinator
@State private var viewModel: HomeViewModel = HomeViewModel()
var body: some View {
@Bindable var coordinator = coordinator
NavigationStack(path: $coordinator.path) {
VStack {
ImageLoader(url: viewModel.imageUrl)
.onTapGesture {
// 👇 envia os dados de forma programática,
// até fora do navigationView se necessário
coordinator.push(.details(viewModel.data))
}
Text(viewModel.data?.name ?? "")
.font(.body)
.bold()
Button(action: { viewModel.getData() }) {
Label("Fetch pokemon", systemImage: "arrow.2.circlepath.circle")
}
.buttonStyle(.borderedProminent)
}
.navigationDestination(for: Routes.self) { route in
// reendeniza qualquer rota
coordinator.buildView(for: route)
}
}
}
}