I have been trying to implement the coordinator pattern following this video – https://www.youtube.com/watch?v=aaLRST7tHFQ&t=917s
Currently this is my code
import SwiftUI
enum Page: String, Identifiable {
case launch, dashboard, newContact, contactList
var id: String {
self.rawValue
}
}
enum Sheet: String, Identifiable {
case `default`
var id: String {
self.rawValue
}
}
enum FullScreenCover: String, Identifiable {
case `default`
var id: String {
self.rawValue
}
}
protocol Coordinator: ObservableObject {
func push(_ page: Page)
func present(_ sheet: Sheet)
func present(_ fullScreenCover: FullScreenCover)
func pop()
func popToRoot()
func dismissSheet()
func dismissFullScreenCover()
}
class AppCoordinator: Coordinator {
// MARK: - Properties
@Published var path = NavigationPath()
@Published var sheet: Sheet?
@Published var fullScreenCover: FullScreenCover?
// MARK: - Builders
// Launch
let launchBuilder = Launch.Builder()
let signInBuilder = SignIn.Builder()
let signUpBuilder = SignUp.Builder()
// Main
let dashboardBuilder = Dashboard.Builder()
// let newContactBuilder = NewContact.Builder()
// let contactListBuilder = ContactList.Builder()
// MARK: - Conformance: Coordinator
func push(_ page: Page) {
path.append(page)
}
func present(_ sheet: Sheet) {
self.sheet = sheet
}
func present(_ fullScreenCover: FullScreenCover) {
self.fullScreenCover = fullScreenCover
}
func pop() {
path.removeLast()
}
func popToRoot() {
path.removeLast(path.count)
}
func dismissSheet() {
self.sheet = nil
}
func dismissFullScreenCover() {
self.fullScreenCover = nil
}
// MARK: - Helpers
@ViewBuilder
func build(page: Page) -> some View {
switch page {
case .launch: self.launchBuilder.build()
case .dashboard: self.dashboardBuilder.build(with: self)
default: self.launchBuilder.build()
}
}
@ViewBuilder
func build(sheet: Sheet) -> some View {
switch sheet {
default: self.launchBuilder.build()
}
}
@ViewBuilder
func build(fullScreenCover: FullScreenCover) -> some View {
switch fullScreenCover {
default: self.launchBuilder.build()
}
}
}
import SwiftUI
struct AppCoordinatorView: View {
@StateObject var coordinator: AppCoordinator = AppCoordinator()
var body: some View {
NavigationStack(path: $coordinator.path) {
coordinator.build(page: .launch)
.navigationDestination(for: Page.self) { page in
coordinator.build(page: page)
}
.sheet(item: $coordinator.sheet) { sheet in
coordinator.build(sheet: sheet)
}
.fullScreenCover(item: $coordinator.fullScreenCover) { fullScreenCover in
coordinator.build(fullScreenCover: fullScreenCover)
}
}
.environmentObject(coordinator)
}
}
extension Launch {
struct Builder: LaunchBuilder {
// MARK: - Properties
// MARK: - Conformance: Builder
func build() -> some CRM_View {
let useCases = Launch.UseCases()
let viewModel = Launch.ViewModel(
useCases: useCases
)
let view = Launch.View(viewModel: viewModel)
return view
}
}
}
import SwiftUI
protocol LaunchView: CRM_View {
}
extension Launch {
struct View: LaunchView {
// MARK: - Properties
let viewModel: Launch.ViewModel
@EnvironmentObject var coordinator: AppCoordinator
// MARK: - Views
var body: some SwiftUI.View {
NavigationStack {
VStack {
Text("Launch Placeholder")
Button {
viewModel.handle(event: .didAppear)
coordinator.push(.dashboard)
} label: {
Text("Hi")
}
}
}
}
}
}
import SwiftUI
protocol LaunchViewModel: CRM_ViewModel {
typealias ModuleEvents = Launch.Event
}
extension Launch {
class ViewModel: LaunchViewModel {
// MARK: - Properties
let useCases: Launch.UseCases
// MARK: - Lifecycle
init(
useCases: Launch.UseCases
) {
self.useCases = useCases
}
// MARK: - Conformance: LaunchViewModel
func handle(event: Launch.Event) {
switch event {
case .didAppear:
break
case .willLoadData: break
case .didLoadData: break
}
}
}
}
I’ve got the dashboard module implemented in basically exactly the same way, just with the name change. I haven’t implemented any functionality into any of these yet.
When I tap on the button on the launch view, it should route to the dashboard view, but it just dismisses immediately and I can’t figure out why.
2