-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Version 1.1.0 #26
Version 1.1.0 #26
Changes from 15 commits
ff24c2d
87da560
6b38703
310d49e
e56891e
b454f15
63b342b
05eec5a
24ccf4f
38d1d8a
472f6aa
cdb93dd
46d53a9
42a05d6
6e0d936
bb27e34
dbfcb39
ab33329
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// | ||
// ModalCoverModel.swift | ||
// | ||
// | ||
// Created by Simon Sestak on 01/08/2024. | ||
// | ||
|
||
import Foundation | ||
|
||
public struct ModalCoverModel<Destination: Hashable & Identifiable>: 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 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,91 @@ | ||||||||||
// | ||||||||||
// CoordinatorSceneFlowProvider.swift | ||||||||||
// | ||||||||||
// Created by Simon Sestak on 31/07/2024. | ||||||||||
// | ||||||||||
|
||||||||||
import SwiftUI | ||||||||||
import FuturedArchitecture | ||||||||||
|
||||||||||
/** | ||||||||||
A typealias representing navigable destinations in a reusable scene flow. | ||||||||||
|
||||||||||
# Notes: # | ||||||||||
1. If the scenes defined in the provider can continue with other scenes, define an end destination. | ||||||||||
- This end destination should trigger a scene display outside of the scene provider. | ||||||||||
2. Other destinations will be used to display scenes defined within the scene provider. | ||||||||||
|
||||||||||
# Example # | ||||||||||
``` | ||||||||||
protocol TemplateFlowDestination: CoordinatorSceneFlowDestination { | ||||||||||
static var destination: Self { get } | ||||||||||
static var end: Self { get } | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably should use naming which does not appear to have a special meaning, if it is only an example.
Suggested change
|
||||||||||
} | ||||||||||
``` | ||||||||||
*/ | ||||||||||
public typealias CoordinatorSceneFlowDestination = Hashable & Identifiable & Equatable | ||||||||||
|
||||||||||
|
||||||||||
/** | ||||||||||
A protocol providing an interface for reusable scene flow providers. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is a reusable scene flow provider? Is my suggestion correct?
Suggested change
|
||||||||||
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: (any TemplateFlowDestination)?)` | ||||||||||
3. To display the first scene of a scene provider, navigate to the embedded flow with nil. | ||||||||||
- `instance?.navigate(to: .embededFlow(destination: nil))` | ||||||||||
|
||||||||||
# Example # | ||||||||||
The scene provider is defined in the coordinator as follows: | ||||||||||
``` | ||||||||||
private lazy var templateSceneProvider: TemplateSceneFlowProvider = { | ||||||||||
TemplateSceneFlowProvider( | ||||||||||
container: container, | ||||||||||
navigateTo: { [weak self] destination in | ||||||||||
if destination == .end { | ||||||||||
self?.navigate(to: .flowSpecificDestinationAfterEmbededFlow) | ||||||||||
} else { | ||||||||||
self?.navigate(to: destination) | ||||||||||
} | ||||||||||
}, pop: { [weak self] in | ||||||||||
self?.pop() | ||||||||||
} | ||||||||||
) | ||||||||||
}() | ||||||||||
``` | ||||||||||
To create a scene from the SceneFlowProvider: | ||||||||||
``` | ||||||||||
@ViewBuilder | ||||||||||
private func embededFlowScenes(destination: (any TemplateFlowDestination)?) -> some View { | ||||||||||
if let destination = destination as? TemplateSceneFlowProvider.Destination { | ||||||||||
templateSceneProvider.scene(for: destination) | ||||||||||
} else { | ||||||||||
TemplateSceneFlowProvider.rootView(with: templateSceneProvider) | ||||||||||
} | ||||||||||
} | ||||||||||
``` | ||||||||||
*/ | ||||||||||
public protocol CoordinatorSceneFlowProvider { | ||||||||||
associatedtype Destination: Hashable & Identifiable | ||||||||||
associatedtype RootView: View | ||||||||||
associatedtype DestinationViews: View | ||||||||||
|
||||||||||
@ViewBuilder | ||||||||||
static func rootView(with instance: Self) -> RootView | ||||||||||
|
||||||||||
@ViewBuilder | ||||||||||
func scene(for destination: Destination) -> DestinationViews | ||||||||||
|
||||||||||
var navigateTo: (Destination) -> Void { get } | ||||||||||
var pop: () -> Void { get } | ||||||||||
|
||||||||||
var present: ((Destination, ModalCoverModel<Destination>.Style) -> Void)? { get } | ||||||||||
var dismissModal: (() -> Void)? { get } | ||||||||||
var onModalDismiss: (() -> Void)? { get } | ||||||||||
var popTo: ((Destination?) -> Void)? { get } | ||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<ApSceneDelegate: AppSceneDelegate>: 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<T: AppSceneDelegate>(appSceneDelegateClass: T.Type, sceneDelegate: SceneDelegate) -> some View { | ||
modifier(SceneDelegateWrapperViewModifier<T>(delegate: sceneDelegate)) | ||
} | ||
} | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.