diff --git a/Package.resolved b/Package.resolved index fdf4600..8f1a4cc 100644 --- a/Package.resolved +++ b/Package.resolved @@ -17,6 +17,24 @@ "revision" : "b4f23e24b5a1bff301efc5e70871083ca029ff95", "version" : "0.2.0" } + }, + { + "identity" : "futured-macros", + "kind" : "remoteSourceControl", + "location" : "https://github.com/futuredapp/futured-macros", + "state" : { + "revision" : "ef659dff70d16d27cbe091d365e561c4087e61f5", + "version" : "0.1.0" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-syntax.git", + "state" : { + "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d", + "version" : "509.1.1" + } } ], "version" : 2 diff --git a/Package.swift b/Package.swift index c88fa85..fab3f5e 100644 --- a/Package.swift +++ b/Package.swift @@ -1,6 +1,7 @@ -// swift-tools-version:5.7.1 +// swift-tools-version:5.9 import PackageDescription +import CompilerPluginSupport let package = Package( name: "FuturedKit", @@ -22,11 +23,15 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/mkj-is/BindingKit", from: "1.0.0"), - .package(url: "https://github.com/JohnSundell/CollectionConcurrencyKit", from: "0.1.0") + .package(url: "https://github.com/JohnSundell/CollectionConcurrencyKit", from: "0.1.0"), + .package(url: "https://github.com/futuredapp/futured-macros", from: "0.1.0") ], targets: [ .target( - name: "FuturedArchitecture" + name: "FuturedArchitecture", + dependencies: [ + .product(name: "FuturedMacros", package: "futured-macros") + ] ), .target( name: "FuturedHelpers", diff --git a/README.md b/README.md index 3f4de1c..c2cf6f3 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,6 @@ SwiftUI state management tools, resources and views used by Futured. - ``CameraImagePicker`` - ``GalleryImagePicker`` -### Alert presentation - -- ``AlertModel`` - ## Installation When using Swift package manager install using or add following line to your dependencies: diff --git a/Sources/FuturedArchitecture/Architecture/Coordinator.swift b/Sources/FuturedArchitecture/Architecture/Coordinator.swift index 38a92e0..fa581b5 100644 --- a/Sources/FuturedArchitecture/Architecture/Coordinator.swift +++ b/Sources/FuturedArchitecture/Architecture/Coordinator.swift @@ -5,56 +5,43 @@ public protocol Coordinator: ObservableObject { associatedtype RootView: View associatedtype DestinationViews: View + /// `rootView` returns the coordinator's main view. Maintain its purity by defining only the view, without added logic or modifiers. + /// If logic or modifiers are needed, encapsulate them in a separate view that can accommodate necessary dependencies. + /// Skipping this recommendation may prevent UI updates when changing `@Published` properties, as `rootView` is static. @ViewBuilder static func rootView(with instance: Self) -> RootView - var sheet: Destination? { get set } - #if !os(macOS) - var fullscreenCover: Destination? { get set } - #endif - var alertModel: AlertModel? { get set } + var modalCover: ModalCoverModel? { get set } @ViewBuilder func scene(for destination: Destination) -> DestinationViews - func onSheetDismiss() - #if !os(macOS) - func onFullscreenCoverDismiss() - #endif + func onModalDismiss() } public extension Coordinator { - func present(sheet: Destination) { - Task { @MainActor in - self.sheet = sheet - } - } - - func dismissSheet() { - Task { @MainActor in - self.sheet = nil - } - } - - func onSheetDismiss() {} -} - -#if !os(macOS) -public extension Coordinator { - func present(fullscreenCover: Destination) { - Task { @MainActor in - self.fullscreenCover = fullscreenCover + func present(modal destination: Destination, type: ModalCoverModel.Style) { + switch type { + case .sheet: + Task { @MainActor in + self.modalCover = .init(destination: destination, style: .sheet) + } + #if !os(macOS) + case .fullscreenCover: + Task { @MainActor in + self.modalCover = .init(destination: destination, style: .fullscreenCover) + } + #endif } } - func dismissFullscreenCover() { + func dismissModal() { Task { @MainActor in - self.fullscreenCover = nil + self.modalCover = nil } } - func onFullscreenCoverDismiss() {} + func onModalDismiss() {} } -#endif public protocol TabCoordinator: Coordinator { associatedtype Tab: Hashable diff --git a/Sources/FuturedArchitecture/Architecture/ModalCoverModel.swift b/Sources/FuturedArchitecture/Architecture/ModalCoverModel.swift new file mode 100644 index 0000000..920e248 --- /dev/null +++ b/Sources/FuturedArchitecture/Architecture/ModalCoverModel.swift @@ -0,0 +1,24 @@ +// +// ModalCoverModel.swift +// +// +// Created by Simon Sestak on 01/08/2024. +// + +import Foundation + +public struct ModalCoverModel: Identifiable { + let destination: Destination + let style: Style + + public enum Style { + case sheet + #if !os(macOS) + case fullscreenCover + #endif + } + + public var id: Destination.ID { + destination.id + } +} diff --git a/Sources/FuturedArchitecture/Architecture/NavigationStackFlow.swift b/Sources/FuturedArchitecture/Architecture/NavigationStackFlow.swift index 2b2a7ab..6ebc267 100644 --- a/Sources/FuturedArchitecture/Architecture/NavigationStackFlow.swift +++ b/Sources/FuturedArchitecture/Architecture/NavigationStackFlow.swift @@ -14,17 +14,33 @@ public struct NavigationStackFlow { + .init { + coordinator.modalCover?.style == .sheet ? coordinator.modalCover?.destination : nil + } set: { destination in + coordinator.modalCover = destination.map { .init(destination: $0, style: .sheet) } + } + } + + #if !os(macOS) + private var fullscreenCoverBinding: Binding { + .init { + coordinator.modalCover?.style == .fullscreenCover ? coordinator.modalCover?.destination : nil + } set: { destination in + coordinator.modalCover = destination.map { .init(destination: $0, style: .fullscreenCover) } + } } #endif } diff --git a/Sources/FuturedArchitecture/Architecture/TabViewFlow.swift b/Sources/FuturedArchitecture/Architecture/TabViewFlow.swift index 71fa1b1..efcc477 100644 --- a/Sources/FuturedArchitecture/Architecture/TabViewFlow.swift +++ b/Sources/FuturedArchitecture/Architecture/TabViewFlow.swift @@ -14,17 +14,33 @@ public struct TabViewFlow: View { TabView(selection: $coordinator.selectedTab) { content() } - .sheet(item: $coordinator.sheet, onDismiss: coordinator.onSheetDismiss, content: coordinator.scene(for:)) - .defaultAlert(model: $coordinator.alertModel) + .sheet(item: sheetBinding, onDismiss: coordinator.onModalDismiss, content: coordinator.scene(for:)) } #else public var body: some View { TabView(selection: $coordinator.selectedTab) { content() } - .sheet(item: $coordinator.sheet, onDismiss: coordinator.onSheetDismiss, content: coordinator.scene(for:)) - .fullScreenCover(item: $coordinator.fullscreenCover, onDismiss: coordinator.onFullscreenCoverDismiss, content: coordinator.scene(for:)) - .defaultAlert(model: $coordinator.alertModel) + .sheet(item: sheetBinding, onDismiss: coordinator.onModalDismiss, content: coordinator.scene(for:)) + .fullScreenCover(item: fullscreenCoverBinding, onDismiss: coordinator.onModalDismiss, content: coordinator.scene(for:)) + } + #endif + + private var sheetBinding: Binding { + .init { + coordinator.modalCover?.style == .sheet ? coordinator.modalCover?.destination : nil + } set: { destination in + coordinator.modalCover = destination.map { .init(destination: $0, style: .sheet) } + } + } + + #if !os(macOS) + private var fullscreenCoverBinding: Binding { + .init { + coordinator.modalCover?.style == .fullscreenCover ? coordinator.modalCover?.destination : nil + } set: { destination in + coordinator.modalCover = destination.map { .init(destination: $0, style: .fullscreenCover) } + } } #endif } diff --git a/Sources/FuturedArchitecture/Documentation.docc/Documentation.md b/Sources/FuturedArchitecture/Documentation.docc/Documentation.md index d928433..0354dcd 100644 --- a/Sources/FuturedArchitecture/Documentation.docc/Documentation.md +++ b/Sources/FuturedArchitecture/Documentation.docc/Documentation.md @@ -18,7 +18,3 @@ SwiftUI architecture, resources and views used by Futured. - ``WrappedUIImagePicker`` - ``CameraImagePicker`` - ``GalleryImagePicker`` - -### Alert presentation - -- ``AlertModel`` diff --git a/Sources/FuturedHelpers/Helpers/CoordinatorSceneFlowProvider.swift b/Sources/FuturedHelpers/Helpers/CoordinatorSceneFlowProvider.swift new file mode 100644 index 0000000..3ed823c --- /dev/null +++ b/Sources/FuturedHelpers/Helpers/CoordinatorSceneFlowProvider.swift @@ -0,0 +1,67 @@ +// +// CoordinatorSceneFlowProvider.swift +// +// Created by Simon Sestak on 31/07/2024. +// + +import SwiftUI +import FuturedArchitecture + +/** + A protocol providing an interface for reusable scene flow providers. *Reusable scene flow provider* is part of a scene flow, which can be used as a part of more *Flow Coordinators*. The shared section of the flow is taken out of the *Flow Coordinator* and placed into a class conforming to `CoordinatorSceneFlowProvider`. + + Protocol defines necessary (`navigateTo`, `pop`) and optional navigation functions. + Optional functions cater to specific navigational use cases like presenting/dismissing modal screen, and popping to destinations. + + - Warning: The `@EnumIndetable` macro won't function with this scene provider as you need to define a destination with an associated value, which isn't primitive. + + # Notes: # + 1. Declare the scene flow provider as a lazy var property in the coordinator. + 2. Coordinator destinations should have an enum that encapsulates flow provider destinations. + - `case embededFlow(destination: TemplateSceneFlowProvider.Destination)` + + # Example # + The scene provider is defined in the coordinator as follows: + ``` + private lazy var templateSceneFlowProvider: TemplateSceneFlowProvider = { + TemplateSceneFlowProvider( + container: container, + navigateTo: { [weak self] destination in + if destination == .end { + self?.navigate(to: .flowSpecificDestinationAfterEmbededFlow) + } else { + self?.navigate(to: .embeded(destination: destination)) + } + }, pop: { [weak self] in + self?.pop() + } + ) + }() + ``` + Then scenes can be provided in flow coordinator like: + ``` + func scene(for destination: Destination) -> some View { + switch destination { + case let .embededFlow(destination): + templateSceneFlowProvider.scene(for: destination) + case .flowSpecificDestinationAfterEmbededFlow: + SomeComponent(model: ... + } + } + ``` + */ +public protocol CoordinatorSceneFlowProvider { + associatedtype Destination: Hashable & Identifiable + associatedtype DestinationViews: View + + @ViewBuilder + func scene(for destination: Destination) -> DestinationViews + + var navigateTo: (Destination) -> Void { get } + var pop: () -> Void { get } + + var present: ((Destination, ModalCoverModel.Style) -> Void)? { get } + var dismissModal: (() -> Void)? { get } + var onModalDismiss: (() -> Void)? { get } + var popTo: ((Destination?) -> Void)? { get } +} diff --git a/Sources/FuturedHelpers/Helpers/SceneDelegate.swift b/Sources/FuturedHelpers/Helpers/SceneDelegate.swift new file mode 100644 index 0000000..8a6d0f3 --- /dev/null +++ b/Sources/FuturedHelpers/Helpers/SceneDelegate.swift @@ -0,0 +1,44 @@ +// +// SceneDelegate.swift +// +// +// Created by Simon Sestak on 31/07/2024. +// + +import SwiftUI + +#if !os(macOS) +protocol AppSceneDelegate: AnyObject, UIWindowSceneDelegate, ObservableObject { + var delegate: SceneDelegate? { get set } +} + +protocol SceneDelegate: AnyObject { + func sceneDidEnterBackground(_ scene: UIScene) + func sceneWillEnterForeground(_ scene: UIScene) +} + +private struct SceneDelegateWrapperViewModifier: ViewModifier { + @EnvironmentObject private var sceneDelegate: ApSceneDelegate + + let delegate: SceneDelegate? + + func body(content: Content) -> some View { + content + .onAppear { + sceneDelegate.delegate = delegate + } + } +} + +extension View { + /// Sets the SceneDelegate for the application. + /// - Parameter appSceneDelegateClass: The call which conforms to the UIWindowSceneDelegate. + /// - Parameter sceneDelegate: The SceneDelegate to set. + /// - Description: + /// In the main app root view call this modifier and pass the SceneDelegate. You need to specify the AppSceneDelegate which conforms to the UIWindowSceneDelegate. + /// This is necessary because the SceneDelegate is accessible in SwiftUI only via EnviromentObject. + func set(appSceneDelegateClass: T.Type, sceneDelegate: SceneDelegate) -> some View { + modifier(SceneDelegateWrapperViewModifier(delegate: sceneDelegate)) + } +} +#endif diff --git a/Templates/Flow Coordinator.xctemplate/___VARIABLE_flowCoordinatorIdentifier___FlowCoordinator.swift b/Templates/Flow Coordinator.xctemplate/___VARIABLE_flowCoordinatorIdentifier___FlowCoordinator.swift index de9edb3..8f9a9b5 100644 --- a/Templates/Flow Coordinator.xctemplate/___VARIABLE_flowCoordinatorIdentifier___FlowCoordinator.swift +++ b/Templates/Flow Coordinator.xctemplate/___VARIABLE_flowCoordinatorIdentifier___FlowCoordinator.swift @@ -1,16 +1,16 @@ // ___FILEHEADER___ +import EnumIdentable import FuturedArchitecture import SwiftUI final class ___VARIABLE_flowCoordinatorIdentifier___FlowCoordinator: NavigationStackCoordinator { - private var container: ___PACKAGENAME:identifier___Container + private var container: Container @Published var path: [Destination] = [] - @Published var sheet: Destination? - @Published var alertModel: AlertModel? + @Published var modalCover: ModalCoverModel? - init(container: ___PACKAGENAME:identifier___Container) { + init(container: Container) { self.container = container } @@ -30,11 +30,8 @@ final class ___VARIABLE_flowCoordinatorIdentifier___FlowCoordinator: NavigationS } extension ___VARIABLE_flowCoordinatorIdentifier___FlowCoordinator { - enum Destination: String, Hashable, Identifiable { + @EnumIdentable + enum Destination: Hashable, Identifiable { case destination - - var id: String { - rawValue - } } } diff --git a/Templates/Scene.xctemplate/Resource Scene/___VARIABLE_sceneIdentifier___ComponentModel.swift b/Templates/Scene.xctemplate/Resource Scene/___VARIABLE_sceneIdentifier___ComponentModel.swift index 76fa822..59d9a68 100644 --- a/Templates/Scene.xctemplate/Resource Scene/___VARIABLE_sceneIdentifier___ComponentModel.swift +++ b/Templates/Scene.xctemplate/Resource Scene/___VARIABLE_sceneIdentifier___ComponentModel.swift @@ -1,6 +1,7 @@ // ___FILEHEADER___ import FuturedArchitecture +import SwiftUI protocol ___VARIABLE_sceneIdentifier___ComponentModelProtocol: ComponentModel { func onAppear() async @@ -30,18 +31,15 @@ final class ___VARIABLE_sceneIdentifier___ComponentModel: ___VARIABLE_sceneIdent } extension ___VARIABLE_sceneIdentifier___ComponentModel { - enum Event { - - } + enum Event {} } #if DEBUG final class ___VARIABLE_sceneIdentifier___ComponentModelMock: ___VARIABLE_sceneIdentifier___ComponentModelProtocol { typealias Event = ___VARIABLE_sceneIdentifier___ComponentModel.Event - var onEvent: (___VARIABLE_sceneIdentifier___ComponentModel.Event) -> Void = { _ in } - - func onAppear() async { - } + var onEvent: (Event) -> Void = { _ in } + + func onAppear() async {} } #endif diff --git a/Templates/Scene.xctemplate/Scene/___VARIABLE_sceneIdentifier___ComponentModel.swift b/Templates/Scene.xctemplate/Scene/___VARIABLE_sceneIdentifier___ComponentModel.swift index 13b19f9..6df1470 100644 --- a/Templates/Scene.xctemplate/Scene/___VARIABLE_sceneIdentifier___ComponentModel.swift +++ b/Templates/Scene.xctemplate/Scene/___VARIABLE_sceneIdentifier___ComponentModel.swift @@ -1,6 +1,7 @@ // ___FILEHEADER___ import FuturedArchitecture +import SwiftUI protocol ___VARIABLE_sceneIdentifier___ComponentModelProtocol: ComponentModel { func onAppear() async @@ -27,18 +28,15 @@ final class ___VARIABLE_sceneIdentifier___ComponentModel: ___VARIABLE_sceneIdent } extension ___VARIABLE_sceneIdentifier___ComponentModel { - enum Event { - - } + enum Event {} } #if DEBUG final class ___VARIABLE_sceneIdentifier___ComponentModelMock: ___VARIABLE_sceneIdentifier___ComponentModelProtocol { typealias Event = ___VARIABLE_sceneIdentifier___ComponentModel.Event - var onEvent: (___VARIABLE_sceneIdentifier___ComponentModel.Event) -> Void = { _ in } - - func onAppear() async { - } + var onEvent: (Event) -> Void = { _ in } + + func onAppear() async {} } #endif diff --git a/Templates/SceneFlowProvider.xctemplate/TemplateIcon.png b/Templates/SceneFlowProvider.xctemplate/TemplateIcon.png new file mode 100644 index 0000000..8f844e4 Binary files /dev/null and b/Templates/SceneFlowProvider.xctemplate/TemplateIcon.png differ diff --git a/Templates/SceneFlowProvider.xctemplate/TemplateIcon@2x.png b/Templates/SceneFlowProvider.xctemplate/TemplateIcon@2x.png new file mode 100644 index 0000000..7ac98b5 Binary files /dev/null and b/Templates/SceneFlowProvider.xctemplate/TemplateIcon@2x.png differ diff --git a/Templates/SceneFlowProvider.xctemplate/TemplateInfo.plist b/Templates/SceneFlowProvider.xctemplate/TemplateInfo.plist new file mode 100644 index 0000000..77add9f --- /dev/null +++ b/Templates/SceneFlowProvider.xctemplate/TemplateInfo.plist @@ -0,0 +1,59 @@ + + + + + DefaultCompletionName + Scene Flow Provider + Description + This generates a new Scene Flow Provider for populating SwiftUI Navigation Stack + Kind + Xcode.IDEKit.TextSubstitutionFileTemplateKind + Options + + + Description + The name of the scene flow provider to create + Identifier + sceneFlowProviderName + Name + New Scene Flow Provider Name: + NotPersisted + + Required + + Type + text + + + Default + ___VARIABLE_sceneFlowProviderName:identifier___ + Identifier + sceneFlowProviderIdentifier + Type + static + + + Default + ___VARIABLE_sceneFlowProviderIdentifier___SceneFlowProvider + Description + Scene flow provider name + Identifier + sceneFlowProviderNameName + Name + Scene Flow Provider Name: + Required + + Type + static + + + Platforms + + com.apple.platform.iphoneos + + SortOrder + 9 + Summary + This generates a new Scene Flow Provider + + diff --git a/Templates/SceneFlowProvider.xctemplate/___VARIABLE_sceneFlowProviderIdentifier___SceneFlowProvider.swift b/Templates/SceneFlowProvider.xctemplate/___VARIABLE_sceneFlowProviderIdentifier___SceneFlowProvider.swift new file mode 100644 index 0000000..0513b74 --- /dev/null +++ b/Templates/SceneFlowProvider.xctemplate/___VARIABLE_sceneFlowProviderIdentifier___SceneFlowProvider.swift @@ -0,0 +1,49 @@ +// ___FILEHEADER___ + +import EnumIdentable +import FuturedArchitecture +import FuturedHelpers +import SwiftUI + +final class ___VARIABLE_sceneFlowProviderIdentifier___SceneFlowProvider: CoordinatorSceneFlowProvider { + let navigateTo: (Destination) -> Void + let pop: () -> Void + let present: ((Destination, ModalCoverModel.Style) -> Void)? = nil + let dismissModal: (() -> Void)? = nil + let onModalDismiss: (() -> Void)? = nil + let popTo: ((Destination?) -> Void)? = nil + + private let container: Container + + init( + container: Container, + navigateTo: @escaping (Destination) -> Void, + pop: @escaping () -> Void + ) { + self.container = container + self.navigateTo = navigateTo + self.pop = pop + } + + func scene(for destination: Destination) -> some View { + switch destination { + case .someDestination: + EmptyView() + case .otherDestination: + EmptyView() + // case .end: + // EmptyView() + } + } +} + +extension ___VARIABLE_sceneFlowProviderIdentifier___SceneFlowProvider { + @EnumIdentable + enum Destination: Hashable, Identifiable { + case someDestination + case otherDestination + // Uncomment 'end' case if the coordinator may present additional scenes following those defined by the flow provider. + // The 'end' scene acts as an indicator for the coordinator to take over scene presentation. + // case end + } +} diff --git a/Templates/SwiftUI App.xctemplate/ExampleComponentModel.swift b/Templates/SwiftUI App.xctemplate/ExampleComponentModel.swift index f64d86c..d9566f8 100644 --- a/Templates/SwiftUI App.xctemplate/ExampleComponentModel.swift +++ b/Templates/SwiftUI App.xctemplate/ExampleComponentModel.swift @@ -34,7 +34,6 @@ final class ExampleComponentModel: ExampleComponentModelProtocol { extension ExampleComponentModel { enum Event { case touchEvent - case alert(title: String, message: String) } } @@ -42,10 +41,10 @@ extension ExampleComponentModel { final class ExampleComponentModelMock: ExampleComponentModelProtocol { typealias Event = ExampleComponentModel.Event - var onEvent: (ExampleComponentModel.Event) -> Void = { _ in } + var onEvent: (Event) -> Void = { _ in } func onAppear() async { } func onTouchUpInside() { } } -#endif \ No newline at end of file +#endif diff --git a/Templates/SwiftUI App.xctemplate/ExampleFlowCoordinator.swift b/Templates/SwiftUI App.xctemplate/ExampleFlowCoordinator.swift index dda2a07..71e713d 100644 --- a/Templates/SwiftUI App.xctemplate/ExampleFlowCoordinator.swift +++ b/Templates/SwiftUI App.xctemplate/ExampleFlowCoordinator.swift @@ -1,5 +1,6 @@ // ___FILEHEADER___ +import EnumIdentable import FuturedArchitecture import SwiftUI @@ -7,8 +8,7 @@ final class ExampleFlowCoordinator: NavigationStackCoordinator { private var container: Container @Published var path: [Destination] = [] - @Published var sheet: Destination? - @Published var alertModel: AlertModel? + @Published var modalCover: ModalCoverModel? init(container: Container) { self.container = container @@ -21,9 +21,7 @@ final class ExampleFlowCoordinator: NavigationStackCoordinator { dataCache: instance.container.dataCache) { [weak instance] event in switch event { case .touchEvent: - instance?.path.append(.destination) - case let .alert(title, message): - instance?.alertModel = .init(title: title, message: message) + instance?.navigate(to: .destination) } } ) @@ -40,11 +38,8 @@ final class ExampleFlowCoordinator: NavigationStackCoordinator { } extension ExampleFlowCoordinator { - enum Destination: String, Hashable, Identifiable { + @EnumIdentable + enum Destination: Hashable, Identifiable { case destination - - var id: String { - rawValue - } } } diff --git a/Templates/Tab Coordinator.xctemplate/___VARIABLE_tabCoordinatorIdentifier___TabCoordinator.swift b/Templates/Tab Coordinator.xctemplate/___VARIABLE_tabCoordinatorIdentifier___TabCoordinator.swift index 7df0ac2..d7e963d 100644 --- a/Templates/Tab Coordinator.xctemplate/___VARIABLE_tabCoordinatorIdentifier___TabCoordinator.swift +++ b/Templates/Tab Coordinator.xctemplate/___VARIABLE_tabCoordinatorIdentifier___TabCoordinator.swift @@ -1,29 +1,21 @@ // ___FILEHEADER___ -import SwiftUI +import EnumIdentable import FuturedArchitecture +import SwiftUI final class ___VARIABLE_tabCoordinatorIdentifier___TabCoordinator: TabCoordinator { - enum Destination: String, Hashable, Identifiable { - case destination - - var id: String { - rawValue - } - } - enum Tab { case firstTab case secondTab } - private let container: ___PACKAGENAME:identifier___Container + private let container: Container - @Published var sheet: Destination? @Published var selectedTab: Tab - @Published var alertModel: AlertModel? + @Published var modalCover: ModalCoverModel? - init(container: ___PACKAGENAME:identifier___Container, selectedTab: Tab) { + init(container: Container, selectedTab: Tab) { self.container = container self.selectedTab = selectedTab } @@ -52,3 +44,10 @@ final class ___VARIABLE_tabCoordinatorIdentifier___TabCoordinator: TabCoordinato } } } + +extension ___VARIABLE_tabCoordinatorIdentifier___TabCoordinator { + @EnumIdentable + enum Destination: Hashable, Identifiable { + case destination + } +}