mirror of
https://github.com/pointfreeco/swift-composable-architecture.git
synced 2025-12-14 20:35:56 +01:00
* Converted voice memos back to identified array
* update deps
* update docs for DismissEffect
* wip
* Add Sendable conformance to PresentationState (#2086)
* wip
* swift-format
* wip
* wip
* fix some warnings
* docs
* wip
* wip
* Catch some typos in Articles (#2088)
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* docs
* wip
* wip
* docs
* wip
* wip
* wip
* wip
* docs
* docs
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Fix invalid states count for 3 optionals and typos (#2094)
* wip
* wip
* more dismisseffect docs
* fixed some references
* navigation doc corrections
* more nav docs
* fix cancellation tests in release mode
* wrap some tests in #if DEBUG since they are testing expected failures
* update UUIDs in tests to use shorter initializer
* fixed a todo
* wip
* fix merge errors
* wip
* fix
* wip
* wip
* fixing a bunch of todos
* get rid of rawvalue in StackElementID
* more todos
* NavLinkStore docs
* fix swift 5.6 stuff
* fix some standups tests
* fix
* clean up
* docs fix
* fixes
* wip
* 5.6 fix
* wip
* wip
* dont parallelize tests
* updated demo readmes
* wip
* Use ObservedObject instead of StateObject for alert/dialog modifiers.
* integration tests for bad dismissal behavior
* check for runtime warnings in every integration test
* wip
* wip
* wip
* fix
* wip
* wip
* wip
* wip
* wip
* wip
* Drop a bunch of Hashables.
* some nav bug fixes
* wip
* wip
* wip
* fix
* fix
* wip
* wip
* Simplify recording test.
* add concurrent async test
* fix
* wip
* Refact how detail dismisses itself.
* fix
* 5.6 fix
* wip
* wip
* wip
* wip
* Add TestStore.assert.
* Revert "Add TestStore.assert."
This reverts commit a892cccc66.
* add Ukrainian Readme.md (#2121)
* Add TestStore.assert. (#2123)
* Add TestStore.assert.
* wip
* Update Sources/ComposableArchitecture/TestStore.swift
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* Update Sources/ComposableArchitecture/Documentation.docc/Extensions/TestStore.md
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* fix tests
---------
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* Run swift-format
* push for store.finish and presentation
* wip
* move docs around
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Add case subscripts
* wip
* wip
* wip
* 5.7-only
* wip
* wip
* wip
* wip
* fix
* revert store.finish task cancellation
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* add test for presentation scope
* wip
* wip
* wip
* wip
* wip
* cleanup
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Rename ReducerProtocol.swift to Reducer.swift (#2206)
* Hard-deprecate old SwitchStore initializers/overloads
* wip
* wip
* Resolve CaseStudies crash (#2258)
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Bump timeout for CI
* wip
* wip
---------
Co-authored-by: Jackson Utsch <jutechs@gmail.com>
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
Co-authored-by: 유재호 <y73447jh@gmail.com>
Co-authored-by: Dmytro <barabashdmyto@gmail.com>
Co-authored-by: mbrandonw <mbrandonw@users.noreply.github.com>
1320 lines
39 KiB
Swift
1320 lines
39 KiB
Swift
@_spi(Reflection) import CasePaths
|
|
import SwiftUI
|
|
|
|
/// A view that observes when enum state held in a store changes cases, and provides stores to
|
|
/// ``CaseLet`` views.
|
|
///
|
|
/// An application may model parts of its state with enums. For example, app state may differ if a
|
|
/// user is logged-in or not:
|
|
///
|
|
/// ```swift
|
|
/// struct AppFeature: Reducer {
|
|
/// enum State {
|
|
/// case loggedIn(LoggedInState)
|
|
/// case loggedOut(LoggedOutState)
|
|
/// }
|
|
/// // ...
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// In the view layer, a store on this state can switch over each case using a ``SwitchStore`` and
|
|
/// a ``CaseLet`` view per case:
|
|
///
|
|
/// ```swift
|
|
/// struct AppView: View {
|
|
/// let store: StoreOf<AppFeature>
|
|
///
|
|
/// var body: some View {
|
|
/// SwitchStore(self.store) { state in
|
|
/// switch state {
|
|
/// case .loggedIn:
|
|
/// CaseLet(
|
|
/// /AppFeature.State.loggedIn, action: AppFeature.Action.loggedIn
|
|
/// ) { loggedInStore in
|
|
/// LoggedInView(store: loggedInStore)
|
|
/// }
|
|
/// case .loggedOut:
|
|
/// CaseLet(
|
|
/// /AppFeature.State.loggedOut, action: AppFeature.Action.loggedOut
|
|
/// ) { loggedOutStore in
|
|
/// LoggedOutView(store: loggedOutStore)
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// > Important: The `SwitchStore` view builder is only evaluated when the case of state passed to
|
|
/// > it changes. As such, you should not rely on this value for anything other than checking the
|
|
/// > current case, _e.g._ by switching on it and routing to an appropriate `CaseLet`.
|
|
///
|
|
/// See ``Reducer/ifCaseLet(_:action:then:fileID:line:)`` and
|
|
/// ``Scope/init(state:action:child:fileID:line:)`` for embedding reducers that operate on each case
|
|
/// of an enum in reducers that operate on the entire enum.
|
|
public struct SwitchStore<State, Action, Content: View>: View {
|
|
public let store: Store<State, Action>
|
|
public let content: (State) -> Content
|
|
|
|
public init(
|
|
_ store: Store<State, Action>,
|
|
@ViewBuilder content: @escaping (_ initialState: State) -> Content
|
|
) {
|
|
self.store = store
|
|
self.content = content
|
|
}
|
|
|
|
public var body: some View {
|
|
WithViewStore(
|
|
self.store, observe: { $0 }, removeDuplicates: { enumTag($0) == enumTag($1) }
|
|
) { viewStore in
|
|
self.content(viewStore.state)
|
|
.environmentObject(StoreObservableObject(store: self.store))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A view that handles a specific case of enum state in a ``SwitchStore``.
|
|
public struct CaseLet<EnumState, EnumAction, CaseState, CaseAction, Content: View>: View {
|
|
public let toCaseState: (EnumState) -> CaseState?
|
|
public let fromCaseAction: (CaseAction) -> EnumAction
|
|
public let content: (Store<CaseState, CaseAction>) -> Content
|
|
|
|
private let fileID: StaticString
|
|
private let line: UInt
|
|
|
|
@EnvironmentObject private var store: StoreObservableObject<EnumState, EnumAction>
|
|
|
|
/// Initializes a ``CaseLet`` view that computes content depending on if a store of enum state
|
|
/// matches a particular case.
|
|
///
|
|
/// - Parameters:
|
|
/// - toCaseState: A function that can extract a case of switch store state, which can be
|
|
/// specified using case path literal syntax, _e.g._ `/State.case`.
|
|
/// - fromCaseAction: A function that can embed a case action in a switch store action.
|
|
/// - content: A function that is given a store of the given case's state and returns a view
|
|
/// that is visible only when the switch store's state matches.
|
|
public init(
|
|
_ toCaseState: @escaping (EnumState) -> CaseState?,
|
|
action fromCaseAction: @escaping (CaseAction) -> EnumAction,
|
|
@ViewBuilder then content: @escaping (_ store: Store<CaseState, CaseAction>) -> Content,
|
|
fileID: StaticString = #fileID,
|
|
line: UInt = #line
|
|
) {
|
|
self.toCaseState = toCaseState
|
|
self.fromCaseAction = fromCaseAction
|
|
self.content = content
|
|
self.fileID = fileID
|
|
self.line = line
|
|
}
|
|
|
|
public var body: some View {
|
|
IfLetStore(
|
|
self.store.wrappedValue.scope(
|
|
state: self.toCaseState,
|
|
action: self.fromCaseAction
|
|
),
|
|
then: self.content,
|
|
else: {
|
|
_CaseLetMismatchView<EnumState, EnumAction>(
|
|
fileID: self.fileID,
|
|
line: self.line
|
|
)
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
extension CaseLet where EnumAction == CaseAction {
|
|
/// Initializes a ``CaseLet`` view that computes content depending on if a store of enum state
|
|
/// matches a particular case.
|
|
///
|
|
/// - Parameters:
|
|
/// - toCaseState: A function that can extract a case of switch store state, which can be
|
|
/// specified using case path literal syntax, _e.g._ `/State.case`.
|
|
/// - content: A function that is given a store of the given case's state and returns a view
|
|
/// that is visible only when the switch store's state matches.
|
|
public init(
|
|
state toCaseState: @escaping (EnumState) -> CaseState?,
|
|
@ViewBuilder then content: @escaping (_ store: Store<CaseState, CaseAction>) -> Content
|
|
) {
|
|
self.init(
|
|
toCaseState,
|
|
action: { $0 },
|
|
then: content
|
|
)
|
|
}
|
|
}
|
|
|
|
/// A view that covers any cases that aren't addressed in a ``SwitchStore``.
|
|
///
|
|
/// If you wish to use ``SwitchStore`` in a non-exhaustive manner (i.e. you do not want to provide
|
|
/// a ``CaseLet`` for each case of the enum), then you must insert a ``Default`` view at the end of
|
|
/// the ``SwitchStore``'s body.
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message:
|
|
"Use the 'SwitchStore.init' that is passed state to 'switch' over, and use 'default' instead."
|
|
)
|
|
public struct Default<Content: View>: View {
|
|
private let content: Content
|
|
|
|
/// Initializes a ``Default`` view that computes content depending on if a store of enum state
|
|
/// does not match a particular case.
|
|
///
|
|
/// - Parameter content: A function that returns a view that is visible only when the switch
|
|
/// store's state does not match a preceding ``CaseLet`` view.
|
|
public init(@ViewBuilder content: () -> Content) {
|
|
self.content = content()
|
|
}
|
|
|
|
public var body: some View {
|
|
self.content
|
|
}
|
|
}
|
|
|
|
extension SwitchStore {
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<State1, Action1, Content1, DefaultContent>(
|
|
_ store: Store<State, Action>,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
Default<DefaultContent>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
Default<DefaultContent>
|
|
>
|
|
{
|
|
let content = content().value
|
|
self.init(store) { state in
|
|
if content.0.toCaseState(state) != nil {
|
|
content.0
|
|
} else {
|
|
content.1
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<State1, Action1, Content1>(
|
|
_ store: Store<State, Action>,
|
|
fileID: StaticString = #fileID,
|
|
line: UInt = #line,
|
|
@ViewBuilder content: () -> CaseLet<State, Action, State1, Action1, Content1>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
Default<_ExhaustivityCheckView<State, Action>>
|
|
>
|
|
{
|
|
self.init(store) {
|
|
content()
|
|
Default { _ExhaustivityCheckView<State, Action>(fileID: fileID, line: line) }
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<State1, Action1, Content1, State2, Action2, Content2, DefaultContent>(
|
|
_ store: Store<State, Action>,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
Default<DefaultContent>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
Default<DefaultContent>
|
|
>
|
|
{
|
|
let content = content().value
|
|
self.init(store) { state in
|
|
if content.0.toCaseState(state) != nil {
|
|
content.0
|
|
} else if content.1.toCaseState(state) != nil {
|
|
content.1
|
|
} else {
|
|
content.2
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<State1, Action1, Content1, State2, Action2, Content2>(
|
|
_ store: Store<State, Action>,
|
|
fileID: StaticString = #fileID,
|
|
line: UInt = #line,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
Default<_ExhaustivityCheckView<State, Action>>
|
|
>
|
|
{
|
|
let content = content()
|
|
self.init(store) {
|
|
content.value.0
|
|
content.value.1
|
|
Default { _ExhaustivityCheckView<State, Action>(fileID: fileID, line: line) }
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
DefaultContent
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
Default<DefaultContent>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
Default<DefaultContent>
|
|
>
|
|
>
|
|
{
|
|
let content = content().value
|
|
self.init(store) { state in
|
|
if content.0.toCaseState(state) != nil {
|
|
content.0
|
|
} else if content.1.toCaseState(state) != nil {
|
|
content.1
|
|
} else if content.2.toCaseState(state) != nil {
|
|
content.2
|
|
} else {
|
|
content.3
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<State1, Action1, Content1, State2, Action2, Content2, State3, Action3, Content3>(
|
|
_ store: Store<State, Action>,
|
|
fileID: StaticString = #fileID,
|
|
line: UInt = #line,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
Default<_ExhaustivityCheckView<State, Action>>
|
|
>
|
|
>
|
|
{
|
|
let content = content()
|
|
self.init(store) {
|
|
content.value.0
|
|
content.value.1
|
|
content.value.2
|
|
Default { _ExhaustivityCheckView<State, Action>(fileID: fileID, line: line) }
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
State4, Action4, Content4,
|
|
DefaultContent
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>,
|
|
Default<DefaultContent>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
>
|
|
>,
|
|
Default<DefaultContent>
|
|
>
|
|
{
|
|
let content = content().value
|
|
self.init(store) { state in
|
|
if content.0.toCaseState(state) != nil {
|
|
content.0
|
|
} else if content.1.toCaseState(state) != nil {
|
|
content.1
|
|
} else if content.2.toCaseState(state) != nil {
|
|
content.2
|
|
} else if content.3.toCaseState(state) != nil {
|
|
content.3
|
|
} else {
|
|
content.4
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
State4, Action4, Content4
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
fileID: StaticString = #fileID,
|
|
line: UInt = #line,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
>
|
|
>,
|
|
Default<_ExhaustivityCheckView<State, Action>>
|
|
>
|
|
{
|
|
let content = content()
|
|
self.init(store) {
|
|
content.value.0
|
|
content.value.1
|
|
content.value.2
|
|
content.value.3
|
|
Default { _ExhaustivityCheckView<State, Action>(fileID: fileID, line: line) }
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
State4, Action4, Content4,
|
|
State5, Action5, Content5,
|
|
DefaultContent
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>,
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
Default<DefaultContent>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
Default<DefaultContent>
|
|
>
|
|
>
|
|
{
|
|
let content = content().value
|
|
self.init(store) { state in
|
|
if content.0.toCaseState(state) != nil {
|
|
content.0
|
|
} else if content.1.toCaseState(state) != nil {
|
|
content.1
|
|
} else if content.2.toCaseState(state) != nil {
|
|
content.2
|
|
} else if content.3.toCaseState(state) != nil {
|
|
content.3
|
|
} else if content.4.toCaseState(state) != nil {
|
|
content.4
|
|
} else {
|
|
content.5
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
State4, Action4, Content4,
|
|
State5, Action5, Content5
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
fileID: StaticString = #fileID,
|
|
line: UInt = #line,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>,
|
|
CaseLet<State, Action, State5, Action5, Content5>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
Default<_ExhaustivityCheckView<State, Action>>
|
|
>
|
|
>
|
|
{
|
|
let content = content()
|
|
self.init(store) {
|
|
content.value.0
|
|
content.value.1
|
|
content.value.2
|
|
content.value.3
|
|
content.value.4
|
|
Default { _ExhaustivityCheckView<State, Action>(fileID: fileID, line: line) }
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
State4, Action4, Content4,
|
|
State5, Action5, Content5,
|
|
State6, Action6, Content6,
|
|
DefaultContent
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>,
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>,
|
|
Default<DefaultContent>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
>
|
|
>,
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>
|
|
>,
|
|
Default<DefaultContent>
|
|
>
|
|
>
|
|
{
|
|
let content = content().value
|
|
self.init(store) { state in
|
|
if content.0.toCaseState(state) != nil {
|
|
content.0
|
|
} else if content.1.toCaseState(state) != nil {
|
|
content.1
|
|
} else if content.2.toCaseState(state) != nil {
|
|
content.2
|
|
} else if content.3.toCaseState(state) != nil {
|
|
content.3
|
|
} else if content.4.toCaseState(state) != nil {
|
|
content.4
|
|
} else if content.5.toCaseState(state) != nil {
|
|
content.5
|
|
} else {
|
|
content.6
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
State4, Action4, Content4,
|
|
State5, Action5, Content5,
|
|
State6, Action6, Content6
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
fileID: StaticString = #fileID,
|
|
line: UInt = #line,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>,
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
>
|
|
>,
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>
|
|
>,
|
|
Default<_ExhaustivityCheckView<State, Action>>
|
|
>
|
|
>
|
|
{
|
|
let content = content()
|
|
self.init(store) {
|
|
content.value.0
|
|
content.value.1
|
|
content.value.2
|
|
content.value.3
|
|
content.value.4
|
|
content.value.5
|
|
Default { _ExhaustivityCheckView<State, Action>(fileID: fileID, line: line) }
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
State4, Action4, Content4,
|
|
State5, Action5, Content5,
|
|
State6, Action6, Content6,
|
|
State7, Action7, Content7,
|
|
DefaultContent
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>,
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>,
|
|
CaseLet<State, Action, State7, Action7, Content7>,
|
|
Default<DefaultContent>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
>
|
|
>,
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State7, Action7, Content7>,
|
|
Default<DefaultContent>
|
|
>
|
|
>
|
|
>
|
|
{
|
|
let content = content().value
|
|
self.init(store) { state in
|
|
if content.0.toCaseState(state) != nil {
|
|
content.0
|
|
} else if content.1.toCaseState(state) != nil {
|
|
content.1
|
|
} else if content.2.toCaseState(state) != nil {
|
|
content.2
|
|
} else if content.3.toCaseState(state) != nil {
|
|
content.3
|
|
} else if content.4.toCaseState(state) != nil {
|
|
content.4
|
|
} else if content.5.toCaseState(state) != nil {
|
|
content.5
|
|
} else if content.6.toCaseState(state) != nil {
|
|
content.6
|
|
} else {
|
|
content.7
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
State4, Action4, Content4,
|
|
State5, Action5, Content5,
|
|
State6, Action6, Content6,
|
|
State7, Action7, Content7
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
fileID: StaticString = #fileID,
|
|
line: UInt = #line,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>,
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>,
|
|
CaseLet<State, Action, State7, Action7, Content7>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
>
|
|
>,
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State7, Action7, Content7>,
|
|
Default<_ExhaustivityCheckView<State, Action>>
|
|
>
|
|
>
|
|
>
|
|
{
|
|
let content = content()
|
|
self.init(store) {
|
|
content.value.0
|
|
content.value.1
|
|
content.value.2
|
|
content.value.3
|
|
content.value.4
|
|
content.value.5
|
|
content.value.6
|
|
Default { _ExhaustivityCheckView<State, Action>(fileID: fileID, line: line) }
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
State4, Action4, Content4,
|
|
State5, Action5, Content5,
|
|
State6, Action6, Content6,
|
|
State7, Action7, Content7,
|
|
State8, Action8, Content8,
|
|
DefaultContent
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>,
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>,
|
|
CaseLet<State, Action, State7, Action7, Content7>,
|
|
CaseLet<State, Action, State8, Action8, Content8>,
|
|
Default<DefaultContent>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
>
|
|
>,
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State7, Action7, Content7>,
|
|
CaseLet<State, Action, State8, Action8, Content8>
|
|
>
|
|
>
|
|
>,
|
|
Default<DefaultContent>
|
|
>
|
|
{
|
|
let content = content().value
|
|
self.init(store) { state in
|
|
if content.0.toCaseState(state) != nil {
|
|
content.0
|
|
} else if content.1.toCaseState(state) != nil {
|
|
content.1
|
|
} else if content.2.toCaseState(state) != nil {
|
|
content.2
|
|
} else if content.3.toCaseState(state) != nil {
|
|
content.3
|
|
} else if content.4.toCaseState(state) != nil {
|
|
content.4
|
|
} else if content.5.toCaseState(state) != nil {
|
|
content.5
|
|
} else if content.6.toCaseState(state) != nil {
|
|
content.6
|
|
} else if content.7.toCaseState(state) != nil {
|
|
content.7
|
|
} else {
|
|
content.8
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
State4, Action4, Content4,
|
|
State5, Action5, Content5,
|
|
State6, Action6, Content6,
|
|
State7, Action7, Content7,
|
|
State8, Action8, Content8
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
fileID: StaticString = #fileID,
|
|
line: UInt = #line,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>,
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>,
|
|
CaseLet<State, Action, State7, Action7, Content7>,
|
|
CaseLet<State, Action, State8, Action8, Content8>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
>
|
|
>,
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State7, Action7, Content7>,
|
|
CaseLet<State, Action, State8, Action8, Content8>
|
|
>
|
|
>
|
|
>,
|
|
Default<_ExhaustivityCheckView<State, Action>>
|
|
>
|
|
{
|
|
let content = content()
|
|
self.init(store) {
|
|
content.value.0
|
|
content.value.1
|
|
content.value.2
|
|
content.value.3
|
|
content.value.4
|
|
content.value.5
|
|
content.value.6
|
|
content.value.7
|
|
Default { _ExhaustivityCheckView<State, Action>(fileID: fileID, line: line) }
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
State4, Action4, Content4,
|
|
State5, Action5, Content5,
|
|
State6, Action6, Content6,
|
|
State7, Action7, Content7,
|
|
State8, Action8, Content8,
|
|
State9, Action9, Content9,
|
|
DefaultContent
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>,
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>,
|
|
CaseLet<State, Action, State7, Action7, Content7>,
|
|
CaseLet<State, Action, State8, Action8, Content8>,
|
|
CaseLet<State, Action, State9, Action9, Content9>,
|
|
Default<DefaultContent>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
>
|
|
>,
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State7, Action7, Content7>,
|
|
CaseLet<State, Action, State8, Action8, Content8>
|
|
>
|
|
>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State9, Action9, Content9>,
|
|
Default<DefaultContent>
|
|
>
|
|
>
|
|
{
|
|
let content = content().value
|
|
self.init(store) { state in
|
|
if content.0.toCaseState(state) != nil {
|
|
content.0
|
|
} else if content.1.toCaseState(state) != nil {
|
|
content.1
|
|
} else if content.2.toCaseState(state) != nil {
|
|
content.2
|
|
} else if content.3.toCaseState(state) != nil {
|
|
content.3
|
|
} else if content.4.toCaseState(state) != nil {
|
|
content.4
|
|
} else if content.5.toCaseState(state) != nil {
|
|
content.5
|
|
} else if content.6.toCaseState(state) != nil {
|
|
content.6
|
|
} else if content.7.toCaseState(state) != nil {
|
|
content.7
|
|
} else if content.8.toCaseState(state) != nil {
|
|
content.8
|
|
} else {
|
|
content.9
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(
|
|
*,
|
|
deprecated,
|
|
message: "Use the 'SwitchStore.init' that is passed state to explicitly 'switch' over instead."
|
|
)
|
|
public init<
|
|
State1, Action1, Content1,
|
|
State2, Action2, Content2,
|
|
State3, Action3, Content3,
|
|
State4, Action4, Content4,
|
|
State5, Action5, Content5,
|
|
State6, Action6, Content6,
|
|
State7, Action7, Content7,
|
|
State8, Action8, Content8,
|
|
State9, Action9, Content9
|
|
>(
|
|
_ store: Store<State, Action>,
|
|
fileID: StaticString = #fileID,
|
|
line: UInt = #line,
|
|
@ViewBuilder content: () -> TupleView<
|
|
(
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>,
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>,
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>,
|
|
CaseLet<State, Action, State7, Action7, Content7>,
|
|
CaseLet<State, Action, State8, Action8, Content8>,
|
|
CaseLet<State, Action, State9, Action9, Content9>
|
|
)
|
|
>
|
|
)
|
|
where
|
|
Content == _ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State1, Action1, Content1>,
|
|
CaseLet<State, Action, State2, Action2, Content2>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State3, Action3, Content3>,
|
|
CaseLet<State, Action, State4, Action4, Content4>
|
|
>
|
|
>,
|
|
_ConditionalContent<
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State5, Action5, Content5>,
|
|
CaseLet<State, Action, State6, Action6, Content6>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State7, Action7, Content7>,
|
|
CaseLet<State, Action, State8, Action8, Content8>
|
|
>
|
|
>
|
|
>,
|
|
_ConditionalContent<
|
|
CaseLet<State, Action, State9, Action9, Content9>,
|
|
Default<_ExhaustivityCheckView<State, Action>>
|
|
>
|
|
>
|
|
{
|
|
let content = content()
|
|
self.init(store) {
|
|
content.value.0
|
|
content.value.1
|
|
content.value.2
|
|
content.value.3
|
|
content.value.4
|
|
content.value.5
|
|
content.value.6
|
|
content.value.7
|
|
content.value.8
|
|
Default { _ExhaustivityCheckView<State, Action>(fileID: fileID, line: line) }
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(
|
|
iOS,
|
|
deprecated: 9999,
|
|
message: "Use the 'SwitchStore.init' that can 'switch' over a given 'state' instead."
|
|
)
|
|
@available(
|
|
macOS,
|
|
deprecated: 9999,
|
|
message: "Use the 'SwitchStore.init' that can 'switch' over a given 'state' instead."
|
|
)
|
|
@available(
|
|
tvOS,
|
|
deprecated: 9999,
|
|
message: "Use the 'SwitchStore.init' that can 'switch' over a given 'state' instead."
|
|
)
|
|
@available(
|
|
watchOS,
|
|
deprecated: 9999,
|
|
message: "Use the 'SwitchStore.init' that can 'switch' over a given 'state' instead."
|
|
)
|
|
public struct _ExhaustivityCheckView<State, Action>: View {
|
|
@EnvironmentObject private var store: StoreObservableObject<State, Action>
|
|
let fileID: StaticString
|
|
let line: UInt
|
|
|
|
public var body: some View {
|
|
#if DEBUG
|
|
let message = """
|
|
Warning: SwitchStore.body@\(self.fileID):\(self.line)
|
|
|
|
"\(debugCaseOutput(self.store.wrappedValue.state.value))" was encountered by a \
|
|
"SwitchStore" that does not handle this case.
|
|
|
|
Make sure that you exhaustively provide a "CaseLet" view for each case in "\(State.self)", \
|
|
or provide a "Default" view at the end of the "SwitchStore".
|
|
"""
|
|
return VStack(spacing: 17) {
|
|
#if os(macOS)
|
|
Text("⚠️")
|
|
#else
|
|
Image(systemName: "exclamationmark.triangle.fill")
|
|
.font(.largeTitle)
|
|
#endif
|
|
|
|
Text(message)
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
.foregroundColor(.white)
|
|
.padding()
|
|
.background(Color.red.edgesIgnoringSafeArea(.all))
|
|
.onAppear {
|
|
runtimeWarn(
|
|
"""
|
|
A "SwitchStore" at "\(self.fileID):\(self.line)" does not handle the current case. …
|
|
|
|
Unhandled case:
|
|
\(debugCaseOutput(self.store.wrappedValue.state.value))
|
|
|
|
Make sure that you exhaustively provide a "CaseLet" view for each case in your state, \
|
|
or provide a "Default" view at the end of the "SwitchStore".
|
|
"""
|
|
)
|
|
}
|
|
#else
|
|
return EmptyView()
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public struct _CaseLetMismatchView<State, Action>: View {
|
|
@EnvironmentObject private var store: StoreObservableObject<State, Action>
|
|
let fileID: StaticString
|
|
let line: UInt
|
|
|
|
public var body: some View {
|
|
#if DEBUG
|
|
let message = """
|
|
Warning: A "CaseLet" at "\(self.fileID):\(self.line)" was encountered when state was set \
|
|
to another case:
|
|
|
|
\(debugCaseOutput(self.store.wrappedValue.state.value))
|
|
|
|
This usually happens when there is a mismatch between the case being switched on and the \
|
|
"CaseLet" view being rendered.
|
|
|
|
For example, if ".screenA" is being switched on, but the "CaseLet" view is pointed to \
|
|
".screenB":
|
|
|
|
case .screenA:
|
|
CaseLet(
|
|
/State.screenB, action: Action.screenB
|
|
) { /* ... */ }
|
|
|
|
Look out for typos to ensure that these two cases align.
|
|
"""
|
|
return VStack(spacing: 17) {
|
|
#if os(macOS)
|
|
Text("⚠️")
|
|
#else
|
|
Image(systemName: "exclamationmark.triangle.fill")
|
|
.font(.largeTitle)
|
|
#endif
|
|
|
|
Text(message)
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
.foregroundColor(.white)
|
|
.padding()
|
|
.background(Color.red.edgesIgnoringSafeArea(.all))
|
|
.onAppear { runtimeWarn(message) }
|
|
#else
|
|
return EmptyView()
|
|
#endif
|
|
}
|
|
}
|
|
|
|
private final class StoreObservableObject<State, Action>: ObservableObject {
|
|
let wrappedValue: Store<State, Action>
|
|
|
|
init(store: Store<State, Action>) {
|
|
self.wrappedValue = store
|
|
}
|
|
}
|
|
|
|
private func enumTag<Case>(_ `case`: Case) -> UInt32? {
|
|
EnumMetadata(Case.self)?.tag(of: `case`)
|
|
}
|