swiftui-navigation/Sources/SwiftUINavigation/Switch.swift

1114 lines
28 KiB
Swift

/// A view that can switch over a binding of enum state and exhaustively handle each case.
///
/// Useful for computing a view from enum state where every case should be handled (using a
/// ``CaseLet`` view), or where there should be a default fallback view (using a ``Default`` view).
///
/// For example, a warehousing application may model the status of an inventory item using an enum
/// with cases that distinguish in-stock and out-of-stock statuses. ``Switch`` (and ``CaseLet``) can
/// be used to produce bindings to the associated values of each case.
///
/// ```swift
/// enum ItemStatus {
/// case inStock(quantity: Int)
/// case outOfStock(isOnBackOrder: Bool)
/// }
///
/// struct InventoryItemView {
/// @State var status: ItemStatus
///
/// var body: some View {
/// Switch(self.$status) {
/// CaseLet(/ItemStatus.inStock) { $quantity in
/// HStack {
/// Text("Quantity: \(quantity)")
/// Stepper("Quantity", value: $quantity)
/// }
/// Button("Out of stock") { self.status = .outOfStock(isOnBackOrder: false) }
/// }
/// CaseLet(/ItemStatus.outOfStock) { $isOnBackOrder in
/// Toggle("Is on back order?", isOn: $isOnBackOrder)
/// Button("In stock") { self.status = .inStock(quantity: 1) }
/// }
/// }
/// }
/// }
/// ```
///
/// To unwrap an individual case of a binding to an enum (_i.e._, if exhaustivity is not needed),
/// use ``IfCaseLet``, instead. Or, to unwrap a binding to an optional, use ``IfLet``.
///
/// > Note: In debug builds, exhaustivity is handled at runtime: if the `Switch` encounters an
/// > unhandled case, and no ``Default`` view is present, a runtime warning is issued and a warning
/// > view is presented.
public struct Switch<Enum, Content: View>: View {
public let `enum`: Binding<Enum>
public let content: Content
private init(
enum: Binding<Enum>,
@ViewBuilder content: () -> Content
) {
self.enum = `enum`
self.content = content()
}
public var body: some View {
self.content
.environmentObject(BindingObject(binding: self.enum))
}
}
/// A view that handles a specific case of enum state in a ``Switch``.
public struct CaseLet<Enum, Case, Content>: View
where Content: View {
@EnvironmentObject private var `enum`: BindingObject<Enum>
public let casePath: CasePath<Enum, Case>
public let content: (Binding<Case>) -> Content
/// Computes content for a particular case of an enum handled by a ``Switch``.
///
/// - Parameters:
/// - casePath: A case path that identifies a case of the ``Switch``'s enum that holds a source
/// of truth for the content.
/// - content: A closure returning the content to be computed from a binding to an enum case.
public init(
_ casePath: CasePath<Enum, Case>,
@ViewBuilder then content: @escaping (Binding<Case>) -> Content
) {
self.casePath = casePath
self.content = content
}
public var body: some View {
Binding(unwrapping: self.enum.wrappedValue, case: self.casePath).map(self.content)
}
}
/// A view that covers any cases that aren't explicitly addressed in a ``Switch``.
///
/// If you wish to use ``Switch`` in a non-exhaustive manner (_i.e._, you do not want to provide a
/// ``CaseLet`` for every case of the enum), then you must insert a ``Default`` view at the end of
/// the ``Switch``'s body, or use ``IfCaseLet`` instead.
public struct Default<Content: View>: View {
private let content: Content
/// Initializes a ``Default`` view that computes content depending on if a binding to enum state
/// does not match a particular case.
///
/// - Parameter content: A function that returns a view that is visible only when the switch
/// view'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 Switch {
public init<Case1, Content1, DefaultContent>(
_ enum: Binding<Enum>,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
Default<DefaultContent>
)
>
)
where
Content == _ConditionalContent<
CaseLet<Enum, Case1, Content1>,
Default<DefaultContent>
>
{
self.init(enum: `enum`) {
let content = content().value
if content.0.casePath ~= `enum`.wrappedValue {
content.0
} else {
content.1
}
}
}
public init<Case1, Content1>(
_ enum: Binding<Enum>,
file: StaticString = #fileID,
line: UInt = #line,
@ViewBuilder content: () -> CaseLet<Enum, Case1, Content1>
)
where
Content == _ConditionalContent<
CaseLet<Enum, Case1, Content1>,
Default<_ExhaustivityCheckView<Enum>>
>
{
self.init(`enum`) {
content()
Default { _ExhaustivityCheckView<Enum>(file: file, line: line) }
}
}
public init<Case1, Content1, Case2, Content2, DefaultContent>(
_ enum: Binding<Enum>,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
Default<DefaultContent>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
Default<DefaultContent>
>
{
self.init(enum: `enum`) {
let content = content().value
switch `enum`.wrappedValue {
case content.0.casePath:
content.0
case content.1.casePath:
content.1
default:
content.2
}
}
}
public init<Case1, Content1, Case2, Content2>(
_ enum: Binding<Enum>,
file: StaticString = #fileID,
line: UInt = #line,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
Default<_ExhaustivityCheckView<Enum>>
>
{
let content = content()
self.init(`enum`) {
content.value.0
content.value.1
Default { _ExhaustivityCheckView<Enum>(file: file, line: line) }
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
DefaultContent
>(
_ enum: Binding<Enum>,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
Default<DefaultContent>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
Default<DefaultContent>
>
>
{
self.init(enum: `enum`) {
let content = content().value
switch `enum`.wrappedValue {
case content.0.casePath:
content.0
case content.1.casePath:
content.1
case content.2.casePath:
content.2
default:
content.3
}
}
}
public init<Case1, Content1, Case2, Content2, Case3, Content3>(
_ enum: Binding<Enum>,
file: StaticString = #fileID,
line: UInt = #line,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
Default<_ExhaustivityCheckView<Enum>>
>
>
{
let content = content()
self.init(`enum`) {
content.value.0
content.value.1
content.value.2
Default { _ExhaustivityCheckView<Enum>(file: file, line: line) }
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
Case4, Content4,
DefaultContent
>(
_ enum: Binding<Enum>,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>,
Default<DefaultContent>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
>
>,
Default<DefaultContent>
>
{
self.init(enum: `enum`) {
let content = content().value
switch `enum`.wrappedValue {
case content.0.casePath:
content.0
case content.1.casePath:
content.1
case content.2.casePath:
content.2
case content.3.casePath:
content.3
default:
content.4
}
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
Case4, Content4
>(
_ enum: Binding<Enum>,
file: StaticString = #fileID,
line: UInt = #line,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
>
>,
Default<_ExhaustivityCheckView<Enum>>
>
{
let content = content()
self.init(`enum`) {
content.value.0
content.value.1
content.value.2
content.value.3
Default { _ExhaustivityCheckView<Enum>(file: file, line: line) }
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
Case4, Content4,
Case5, Content5,
DefaultContent
>(
_ enum: Binding<Enum>,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>,
CaseLet<Enum, Case5, Content5>,
Default<DefaultContent>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
>
>,
_ConditionalContent<
CaseLet<Enum, Case5, Content5>,
Default<DefaultContent>
>
>
{
self.init(enum: `enum`) {
let content = content().value
switch `enum`.wrappedValue {
case content.0.casePath:
content.0
case content.1.casePath:
content.1
case content.2.casePath:
content.2
case content.3.casePath:
content.3
case content.4.casePath:
content.4
default:
content.5
}
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
Case4, Content4,
Case5, Content5
>(
_ enum: Binding<Enum>,
file: StaticString = #fileID,
line: UInt = #line,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>,
CaseLet<Enum, Case5, Content5>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
>
>,
_ConditionalContent<
CaseLet<Enum, Case5, Content5>,
Default<_ExhaustivityCheckView<Enum>>
>
>
{
let content = content()
self.init(`enum`) {
content.value.0
content.value.1
content.value.2
content.value.3
content.value.4
Default { _ExhaustivityCheckView<Enum>(file: file, line: line) }
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
Case4, Content4,
Case5, Content5,
Case6, Content6,
DefaultContent
>(
_ enum: Binding<Enum>,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>,
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>,
Default<DefaultContent>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
>
>,
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>
>,
Default<DefaultContent>
>
>
{
self.init(enum: `enum`) {
let content = content().value
switch `enum`.wrappedValue {
case content.0.casePath:
content.0
case content.1.casePath:
content.1
case content.2.casePath:
content.2
case content.3.casePath:
content.3
case content.4.casePath:
content.4
case content.5.casePath:
content.5
default:
content.6
}
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
Case4, Content4,
Case5, Content5,
Case6, Content6
>(
_ enum: Binding<Enum>,
file: StaticString = #fileID,
line: UInt = #line,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>,
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
>
>,
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>
>,
Default<_ExhaustivityCheckView<Enum>>
>
>
{
let content = content()
self.init(`enum`) {
content.value.0
content.value.1
content.value.2
content.value.3
content.value.4
content.value.5
Default { _ExhaustivityCheckView<Enum>(file: file, line: line) }
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
Case4, Content4,
Case5, Content5,
Case6, Content6,
Case7, Content7,
DefaultContent
>(
_ enum: Binding<Enum>,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>,
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>,
CaseLet<Enum, Case7, Content7>,
Default<DefaultContent>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
>
>,
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>
>,
_ConditionalContent<
CaseLet<Enum, Case7, Content7>,
Default<DefaultContent>
>
>
>
{
self.init(enum: `enum`) {
let content = content().value
switch `enum`.wrappedValue {
case content.0.casePath:
content.0
case content.1.casePath:
content.1
case content.2.casePath:
content.2
case content.3.casePath:
content.3
case content.4.casePath:
content.4
case content.5.casePath:
content.5
case content.6.casePath:
content.6
default:
content.7
}
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
Case4, Content4,
Case5, Content5,
Case6, Content6,
Case7, Content7
>(
_ enum: Binding<Enum>,
file: StaticString = #fileID,
line: UInt = #line,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>,
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>,
CaseLet<Enum, Case7, Content7>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
>
>,
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>
>,
_ConditionalContent<
CaseLet<Enum, Case7, Content7>,
Default<_ExhaustivityCheckView<Enum>>
>
>
>
{
let content = content()
self.init(`enum`) {
content.value.0
content.value.1
content.value.2
content.value.3
content.value.4
content.value.5
content.value.6
Default { _ExhaustivityCheckView<Enum>(file: file, line: line) }
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
Case4, Content4,
Case5, Content5,
Case6, Content6,
Case7, Content7,
Case8, Content8,
DefaultContent
>(
_ enum: Binding<Enum>,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>,
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>,
CaseLet<Enum, Case7, Content7>,
CaseLet<Enum, Case8, Content8>,
Default<DefaultContent>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
>
>,
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>
>,
_ConditionalContent<
CaseLet<Enum, Case7, Content7>,
CaseLet<Enum, Case8, Content8>
>
>
>,
Default<DefaultContent>
>
{
self.init(enum: `enum`) {
let content = content().value
switch `enum`.wrappedValue {
case content.0.casePath:
content.0
case content.1.casePath:
content.1
case content.2.casePath:
content.2
case content.3.casePath:
content.3
case content.4.casePath:
content.4
case content.5.casePath:
content.5
case content.6.casePath:
content.6
case content.7.casePath:
content.7
default:
content.8
}
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
Case4, Content4,
Case5, Content5,
Case6, Content6,
Case7, Content7,
Case8, Content8
>(
_ enum: Binding<Enum>,
file: StaticString = #fileID,
line: UInt = #line,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>,
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>,
CaseLet<Enum, Case7, Content7>,
CaseLet<Enum, Case8, Content8>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
>
>,
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>
>,
_ConditionalContent<
CaseLet<Enum, Case7, Content7>,
CaseLet<Enum, Case8, Content8>
>
>
>,
Default<_ExhaustivityCheckView<Enum>>
>
{
let content = content()
self.init(`enum`) {
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<Enum>(file: file, line: line) }
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
Case4, Content4,
Case5, Content5,
Case6, Content6,
Case7, Content7,
Case8, Content8,
Case9, Content9,
DefaultContent
>(
_ enum: Binding<Enum>,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>,
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>,
CaseLet<Enum, Case7, Content7>,
CaseLet<Enum, Case8, Content8>,
CaseLet<Enum, Case9, Content9>,
Default<DefaultContent>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
>
>,
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>
>,
_ConditionalContent<
CaseLet<Enum, Case7, Content7>,
CaseLet<Enum, Case8, Content8>
>
>
>,
_ConditionalContent<
CaseLet<Enum, Case9, Content9>,
Default<DefaultContent>
>
>
{
self.init(enum: `enum`) {
let content = content().value
switch `enum`.wrappedValue {
case content.0.casePath:
content.0
case content.1.casePath:
content.1
case content.2.casePath:
content.2
case content.3.casePath:
content.3
case content.4.casePath:
content.4
case content.5.casePath:
content.5
case content.6.casePath:
content.6
case content.7.casePath:
content.7
case content.8.casePath:
content.8
default:
content.9
}
}
}
public init<
Case1, Content1,
Case2, Content2,
Case3, Content3,
Case4, Content4,
Case5, Content5,
Case6, Content6,
Case7, Content7,
Case8, Content8,
Case9, Content9
>(
_ enum: Binding<Enum>,
file: StaticString = #fileID,
line: UInt = #line,
@ViewBuilder content: () -> TupleView<
(
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>,
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>,
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>,
CaseLet<Enum, Case7, Content7>,
CaseLet<Enum, Case8, Content8>,
CaseLet<Enum, Case9, Content9>
)
>
)
where
Content == _ConditionalContent<
_ConditionalContent<
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case1, Content1>,
CaseLet<Enum, Case2, Content2>
>,
_ConditionalContent<
CaseLet<Enum, Case3, Content3>,
CaseLet<Enum, Case4, Content4>
>
>,
_ConditionalContent<
_ConditionalContent<
CaseLet<Enum, Case5, Content5>,
CaseLet<Enum, Case6, Content6>
>,
_ConditionalContent<
CaseLet<Enum, Case7, Content7>,
CaseLet<Enum, Case8, Content8>
>
>
>,
_ConditionalContent<
CaseLet<Enum, Case9, Content9>,
Default<_ExhaustivityCheckView<Enum>>
>
>
{
let content = content()
self.init(`enum`) {
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<Enum>(file: file, line: line) }
}
}
}
public struct _ExhaustivityCheckView<Enum>: View {
@EnvironmentObject private var `enum`: BindingObject<Enum>
let file: StaticString
let line: UInt
public var body: some View {
#if DEBUG
let message = """
Warning: Switch.body@\(self.file):\(self.line)
"Switch" did not handle "\(describeCase(self.enum.wrappedValue.wrappedValue))"
Make sure that you exhaustively provide a "CaseLet" view for each case in "\(Enum.self)", \
provide a "Default" view at the end of the "Switch", or use an "IfCaseLet" view instead.
"""
VStack(spacing: 17) {
self.exclamation()
.font(.largeTitle)
Text(message)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.foregroundColor(.white)
.padding()
.background(Color.red.edgesIgnoringSafeArea(.all))
.onAppear { runtimeWarn(message, file: self.file, line: self.line) }
#else
EmptyView()
#endif
}
func exclamation() -> some View {
#if os(macOS)
return Text("⚠️")
#else
return Image(systemName: "exclamationmark.triangle.fill")
#endif
}
}
private class BindingObject<Value>: ObservableObject {
let wrappedValue: Binding<Value>
init(binding: Binding<Value>) {
self.wrappedValue = binding
}
}
private func describeCase<Enum>(_ enum: Enum) -> String {
let mirror = Mirror(reflecting: `enum`)
let `case`: String
if mirror.displayStyle == .enum, let child = mirror.children.first, let label = child.label {
let childMirror = Mirror(reflecting: child.value)
let associatedValuesMirror =
childMirror.displayStyle == .tuple
? childMirror
: Mirror(`enum`, unlabeledChildren: [child.value], displayStyle: .tuple)
`case` = """
\(label)(\
\(associatedValuesMirror.children.map { "\($0.label ?? "_"):" }.joined())\
)
"""
} else {
`case` = "\(`enum`)"
}
var type = String(reflecting: Enum.self)
if let index = type.firstIndex(of: ".") {
type.removeSubrange(...index)
}
return "\(type).\(`case`)"
}