diff --git a/Package.resolved b/Package.resolved index 8f1a4cc..3183534 100644 --- a/Package.resolved +++ b/Package.resolved @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/futuredapp/futured-macros", "state" : { - "revision" : "ef659dff70d16d27cbe091d365e561c4087e61f5", - "version" : "0.1.0" + "branch" : "experiment/data-cache-as-macro", + "revision" : "adbc3a0c5918454537a89c73d7afee542021e4e7" } }, { diff --git a/Package.swift b/Package.swift index fab3f5e..4c6f19d 100644 --- a/Package.swift +++ b/Package.swift @@ -24,13 +24,14 @@ 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/futuredapp/futured-macros", from: "0.1.0") + .package(url: "https://github.com/futuredapp/futured-macros", branch: "experiment/data-cache-as-macro") ], targets: [ .target( name: "FuturedArchitecture", dependencies: [ - .product(name: "FuturedMacros", package: "futured-macros") + .product(name: "EnumIdentable", package: "futured-macros"), + .product(name: "DataCache", package: "futured-macros"), ] ), .target( diff --git a/Sources/FuturedArchitecture/Architecture/Coordinator.swift b/Sources/FuturedArchitecture/Architecture/Coordinator.swift index dd3e7a0..a66a9c6 100644 --- a/Sources/FuturedArchitecture/Architecture/Coordinator.swift +++ b/Sources/FuturedArchitecture/Architecture/Coordinator.swift @@ -83,6 +83,13 @@ public protocol TabCoordinator: Coordinator { var selectedTab: Tab { get set } } +/// `VariantCoordinator` is essentially the same as `TabCoordinator` in a sence, that +/// it presents one of multiple possible views. The difference is, that a `Scene` gets destroyed upon +/// being switched to different scene. An example of intended usage might be switching between different +/// App configurations after launch without modals. This *coordinator* is ment to have +/// ``VariantViewFlow`` as the Root view. +public protocol VariantCoordinator: TabCoordinator {} + /// `NavigationStackCoordinator` provides additional requirements for use with ``SwiftUI.NavigationStack``. /// This *coordinator* is ment have ``NavigationStackFlow`` as the Root view. /// diff --git a/Sources/FuturedArchitecture/Architecture/DataCache.swift b/Sources/FuturedArchitecture/Architecture/DataCache.swift index 6adb364..588841c 100644 --- a/Sources/FuturedArchitecture/Architecture/DataCache.swift +++ b/Sources/FuturedArchitecture/Architecture/DataCache.swift @@ -14,7 +14,7 @@ import Foundation /// - ToDo: How the `DataCache` may interact with persistence such as /// `CoreData` or `SwiftData` is an open question and subject of further /// research. -public actor DataCache { +public actor ActorDataCache { /// The data held by this data cache. @inlinable @Published public private(set) var value: Model diff --git a/Sources/FuturedArchitecture/Architecture/VariantViewFlow.swift b/Sources/FuturedArchitecture/Architecture/VariantViewFlow.swift new file mode 100644 index 0000000..90e05ae --- /dev/null +++ b/Sources/FuturedArchitecture/Architecture/VariantViewFlow.swift @@ -0,0 +1,48 @@ +import SwiftUI + +/// The `VariantViewFlow` encapsulates the ``SwiftUI.View`` and binds it to the +/// variables and callbacks of the ``VariantCoordinator`` which is retains as a ``SwiftUI.StateObject``. +/// - Experiment: This API is in preview and subjet to change. +public struct VariantViewFlow: View { + @StateObject private var coordinator: Coordinator + @ViewBuilder private let content: () -> Content + + /// - Parameters: + /// - coordinator: The instance of the coordinator used as the model and retained as the ``SwiftUI.StateObject`` + /// - content: The content should contain a single `switch` statement picking a view depending on the state of `selectedTab`. + public init(coordinator: @autoclosure @escaping () -> Coordinator, @ViewBuilder content: @MainActor @escaping () -> Content) { + self._coordinator = StateObject(wrappedValue: coordinator()) + self.content = content + } + +#if os(macOS) + public var body: some View { + content() + .sheet(item: sheetBinding, onDismiss: coordinator.onModalDismiss, content: coordinator.scene(for:)) + } +#else + public var body: some View { + content() + .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 +}