Sfoglia il codice sorgente

Implement support to present screens modally bu with it’s own navigation stack (for products screen)

Pavel Yurchenko 1 anno fa
parent
commit
cc3d459491

+ 4 - 0
CoordinatorSUI/Sources/CoordinatorSUI/BaseCoordinator.swift

@@ -36,6 +36,10 @@ open class BaseCoordinator: Coordinator {
         }
     }
 
+    public func removeLastChild() {
+        _ = childCoordinators.popLast()
+    }
+
     public init() { }
 
     deinit {

+ 19 - 0
CoordinatorSUI/Sources/CoordinatorSUI/Then.swift

@@ -0,0 +1,19 @@
+//
+//  Then.swift
+//  CoordinatorSUIExamples
+//
+//  Created by Pavel Yurchenko on 03.12.2024.
+//
+
+import Foundation
+
+public protocol Then { }
+
+public extension Then where Self: Any {
+    func then(_ block: (Self) throws -> Void) rethrows -> Self {
+        try block(self)
+        return self
+    }
+}
+
+extension BaseCoordinator: Then {}

+ 5 - 1
CoordinatorSUIExamples/CoordinatorSUIExamples/Application/SUIExamplesApp.swift

@@ -31,7 +31,11 @@ struct SUIExamplesApp: App {
                             destination: appCoordinator.mainCoordinator.buildDestination
                         )
                     }
-                    .sheet(item: $mainCoordinator.presenting, content: mainCoordinator.buildDestination)
+                    .sheet(
+                        item: $mainCoordinator.presentation,
+                        onDismiss: mainCoordinator.onDismissPresentation,
+                        content: mainCoordinator.buildPresentation
+                    )
                 }
 
                 Tab(ApplicationTab.products.rawValue, systemImage: "tray.and.arrow.down.fill", value: ApplicationTab.products) {

+ 69 - 0
CoordinatorSUIExamples/CoordinatorSUIExamples/Flows/ModalProductsCoordinator.swift

@@ -0,0 +1,69 @@
+//
+//  ModalProductsCoordinator.swift
+//  CoordinatorSUIExamples
+//
+//  Created by Pavel Yurchenko on 03.12.2024.
+//
+
+import CoordinatorSUI
+import SwiftUI
+
+final class ModalProductsCoordinator: BaseCoordinator {
+
+    func run() -> some View
+    {
+        showProductsModule()
+    }
+
+    @ViewBuilder
+    func buildDestination(_ route: Route) -> some View {
+        switch route {
+        case .product(let id):
+            showProductModule(with: id)
+        default:
+            fatalError("This should not happen!")
+        }
+    }
+
+    // MARK: - Private methods
+
+    private func showProductsModule() -> some View {
+        ProductsBuilder().build(
+            with: .init(
+                onProduct: { [weak self] in
+                    self?.currentRouter.push(Route.product(id: $0))
+                }
+            )
+        )
+    }
+
+    private func showProductModule(with id: Int) -> some View {
+        ProductBuilder().build(
+            with: .init(id: id),
+            output: .init()
+        )
+    }
+}
+
+// MARK: - View
+
+struct ModalProductsNavigationView: View {
+
+    let coordinator: ModalProductsCoordinator
+    @StateObject var router: Router
+
+    var body: some View {
+        NavigationStack(path: $router.path) {
+            coordinator.run()
+            .navigationDestination(
+                for: Route.self,
+                destination: coordinator.buildDestination
+            )
+        }
+    }
+
+    @ViewBuilder
+    static func build(with coordinator: ModalProductsCoordinator) -> some View {
+        ModalProductsNavigationView(coordinator: coordinator, router: coordinator.currentRouter)
+    }
+}

+ 19 - 3
CoordinatorSUIExamples/CoordinatorSUIExamples/Flows/Tabs/MainCoordinator.swift

@@ -11,7 +11,7 @@ import SwiftUI
 final class MainCoordinator: BaseCoordinator, ObservableObject {
 
     @Published
-    var presenting: Route?
+    var presentation: Route?
 
     func run() -> some View {
         showMainModule()
@@ -33,6 +33,22 @@ final class MainCoordinator: BaseCoordinator, ObservableObject {
         }
     }
 
+    @ViewBuilder
+    func buildPresentation(_ route: Route) -> some View {
+        switch route {
+        case .products:
+            ModalProductsNavigationView.build(with: ModalProductsCoordinator().then { addChild($0) })
+        case .cart:
+            showCartModule()
+        default:
+            fatalError("This should not happen!")
+        }
+    }
+
+    func onDismissPresentation() {
+        removeLastChild()
+    }
+
     // MARK: - Private methods
 
     private func showMainModule() -> some View {
@@ -51,10 +67,10 @@ final class MainCoordinator: BaseCoordinator, ObservableObject {
                     self?.currentRouter.push(Route.feedback)
                 },
                 onModalProducts: { [weak self] in
-                    self?.presenting = .products
+                    self?.presentation = .products
                 },
                 onModalCart: { [weak self] in
-                    self?.presenting = .cart
+                    self?.presentation = .cart
                 }
             )
         )