Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
7 changes: 7 additions & 0 deletions Sources/FuturedArchitecture/Architecture/Coordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
2 changes: 1 addition & 1 deletion Sources/FuturedArchitecture/Architecture/DataCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Model: Equatable> {
public actor ActorDataCache<Model: Equatable> {
/// The data held by this data cache.
@inlinable
@Published public private(set) var value: Model
Expand Down
48 changes: 48 additions & 0 deletions Sources/FuturedArchitecture/Architecture/VariantViewFlow.swift
Original file line number Diff line number Diff line change
@@ -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<Coordinator: VariantCoordinator, Content: View>: 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<Coordinator.Destination?> {
.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<Coordinator.Destination?> {
.init {
coordinator.modalCover?.style == .fullscreenCover ? coordinator.modalCover?.destination : nil
} set: { destination in
coordinator.modalCover = destination.map { .init(destination: $0, style: .fullscreenCover) }
}
}
#endif
}
Loading