From 6e1c6890968f36be1a58280563276820d400d39d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikol=C3=A1=C5=A1=20Stuchl=C3=ADk?= Date: Tue, 14 Jan 2025 12:31:30 +0100 Subject: [PATCH 1/3] Data Cache macro dependency update --- Package.resolved | 4 ++-- Package.swift | 2 +- Sources/FuturedArchitecture/Architecture/DataCache.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Package.resolved b/Package.resolved index 8f1a4cc..e4587ac 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" : "f90f6fe2b46ed426ad6e3f55150df4f0f9e179dd" } }, { diff --git a/Package.swift b/Package.swift index fab3f5e..815c252 100644 --- a/Package.swift +++ b/Package.swift @@ -24,7 +24,7 @@ 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( diff --git a/Sources/FuturedArchitecture/Architecture/DataCache.swift b/Sources/FuturedArchitecture/Architecture/DataCache.swift index 5a8a080..b16d806 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 persistance 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 From 4eaccf14af86a37dc49ada5bf71d5da414cd9e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikol=C3=A1=C5=A1=20Stuchl=C3=ADk?= Date: Tue, 14 Jan 2025 12:57:05 +0100 Subject: [PATCH 2/3] Add new macro to dependency --- Package.resolved | 2 +- Package.swift | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Package.resolved b/Package.resolved index e4587ac..3183534 100644 --- a/Package.resolved +++ b/Package.resolved @@ -24,7 +24,7 @@ "location" : "https://github.com/futuredapp/futured-macros", "state" : { "branch" : "experiment/data-cache-as-macro", - "revision" : "f90f6fe2b46ed426ad6e3f55150df4f0f9e179dd" + "revision" : "adbc3a0c5918454537a89c73d7afee542021e4e7" } }, { diff --git a/Package.swift b/Package.swift index 815c252..4c6f19d 100644 --- a/Package.swift +++ b/Package.swift @@ -30,7 +30,8 @@ let package = Package( .target( name: "FuturedArchitecture", dependencies: [ - .product(name: "FuturedMacros", package: "futured-macros") + .product(name: "EnumIdentable", package: "futured-macros"), + .product(name: "DataCache", package: "futured-macros"), ] ), .target( From cecff9b7b8ef8be933e927f71f309c3d63a262a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikol=C3=A1=C5=A1=20Stuchl=C3=ADk?= Date: Fri, 31 Jan 2025 11:25:58 +0100 Subject: [PATCH 3/3] Introduce VariantView --- .../Architecture/Coordinator.swift | 7 +++ .../Architecture/VariantViewFlow.swift | 48 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 Sources/FuturedArchitecture/Architecture/VariantViewFlow.swift diff --git a/Sources/FuturedArchitecture/Architecture/Coordinator.swift b/Sources/FuturedArchitecture/Architecture/Coordinator.swift index 9bcfb30..23df725 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/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 +}