Skip to content

Commit c669dca

Browse files
committed
Merge branch 'navigation-beta' into prerelease/1.0
2 parents 053a851 + 44c1f87 commit c669dca

File tree

4 files changed

+109
-76
lines changed

4 files changed

+109
-76
lines changed

Sources/ComposableArchitecture/Internal/NavigationID.swift

+8-7
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,20 @@ struct AnyID: Hashable, Identifiable, Sendable {
6666

6767
@usableFromInline
6868
init<Base>(_ base: Base) {
69-
func id(_ identifiable: some Identifiable) -> AnyHashableSendable {
69+
func id<T: Identifiable>(_ identifiable: T) -> AnyHashableSendable {
7070
AnyHashableSendable(identifiable.id)
7171
}
7272

7373
self.objectIdentifier = ObjectIdentifier(Base.self)
7474
self.tag = EnumMetadata(Base.self)?.tag(of: base)
75-
if let base = base as? any Identifiable {
76-
self.identifier = id(base)
77-
} else if let metadata = EnumMetadata(type(of: base)),
78-
metadata.associatedValueType(forTag: metadata.tag(of: base)) is any Identifiable.Type
79-
{
80-
// TODO: Extract enum payload and assign id
75+
if let id = _id(base) {
76+
self.identifier = AnyHashableSendable(id)
8177
}
78+
// TODO: Extract identifiable enum payload and assign id
79+
// else if let metadata = EnumMetadata(type(of: base)),
80+
// metadata.associatedValueType(forTag: metadata.tag(of: base)) is any Identifiable.Type
81+
// {
82+
// }
8283
}
8384

8485
@usableFromInline

Sources/ComposableArchitecture/Internal/OpenExistential.swift

+30
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111
self == other as? Self
1212
}
1313
}
14+
15+
// MARK: Identifiable
16+
17+
func _id(_ value: Any) -> AnyHashable? {
18+
func open(_ value: some Identifiable) -> AnyHashable {
19+
value.id
20+
}
21+
guard let value = value as? any Identifiable else { return nil }
22+
return open(value)
23+
}
1424
#else
1525
// MARK: -
1626
// MARK: swift(<5.7)
@@ -39,4 +49,24 @@
3949
return lhs == rhs
4050
}
4151
}
52+
53+
// MARK: Identifiable
54+
55+
func _id(_ value: Any) -> AnyHashable? {
56+
func open<T>(_: T.Type) -> AnyHashable? {
57+
(Witness<T>.self as? AnyIdentifiable.Type)?.id(value)
58+
}
59+
return _openExistential(type(of: value), do: open)
60+
}
61+
62+
private protocol AnyIdentifiable {
63+
static func id(_ value: Any) -> AnyHashable?
64+
}
65+
66+
extension Witness: AnyIdentifiable where T: Identifiable {
67+
fileprivate static func id(_ value: Any) -> AnyHashable? {
68+
guard let value = value as? T else { return nil }
69+
return value.id
70+
}
71+
}
4272
#endif

Sources/ComposableArchitecture/Reducer/Reducers/Presentation.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,7 @@ extension ReducerProtocol {
172172
file: StaticString = #file,
173173
fileID: StaticString = #fileID,
174174
line: UInt = #line
175-
) -> _PresentationReducer<Self, EmptyReducer<DestinationState, DestinationAction>>
176-
where DestinationState: _EphemeralState {
175+
) -> _PresentationReducer<Self, EmptyReducer<DestinationState, DestinationAction>> {
177176
self.ifLet(
178177
toPresentationState,
179178
action: toPresentationAction,
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,81 @@
11
import SwiftUI
22

3-
extension View {
4-
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
5-
public func navigationDestination<State, Action, Destination: View>(
6-
store: Store<PresentationState<State>, PresentationAction<Action>>,
7-
@ViewBuilder destination: @escaping (Store<State, Action>) -> Destination
8-
) -> some View {
9-
self.navigationDestination(
10-
store: store, state: { $0 }, action: { $0 }, destination: destination
11-
)
12-
}
3+
#if swift(>=5.7)
4+
extension View {
5+
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
6+
public func navigationDestination<State, Action, Destination: View>(
7+
store: Store<PresentationState<State>, PresentationAction<Action>>,
8+
@ViewBuilder destination: @escaping (Store<State, Action>) -> Destination
9+
) -> some View {
10+
self.navigationDestination(
11+
store: store, state: { $0 }, action: { $0 }, destination: destination
12+
)
13+
}
1314

14-
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
15-
public func navigationDestination<
16-
State, Action, DestinationState, DestinationAction, Destination: View
17-
>(
18-
store: Store<PresentationState<State>, PresentationAction<Action>>,
19-
state toDestinationState: @escaping (State) -> DestinationState?,
20-
action fromDestinationAction: @escaping (DestinationAction) -> Action,
21-
@ViewBuilder destination: @escaping (Store<DestinationState, DestinationAction>) -> Destination
22-
) -> some View {
23-
self.modifier(
24-
PresentationNavigationDestinationModifier(
25-
store: store,
26-
state: toDestinationState,
27-
action: fromDestinationAction,
28-
content: destination
15+
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
16+
public func navigationDestination<
17+
State, Action, DestinationState, DestinationAction, Destination: View
18+
>(
19+
store: Store<PresentationState<State>, PresentationAction<Action>>,
20+
state toDestinationState: @escaping (State) -> DestinationState?,
21+
action fromDestinationAction: @escaping (DestinationAction) -> Action,
22+
@ViewBuilder destination: @escaping (Store<DestinationState, DestinationAction>) ->
23+
Destination
24+
) -> some View {
25+
self.modifier(
26+
PresentationNavigationDestinationModifier(
27+
store: store,
28+
state: toDestinationState,
29+
action: fromDestinationAction,
30+
content: destination
31+
)
2932
)
30-
)
33+
}
3134
}
32-
}
3335

34-
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
35-
private struct PresentationNavigationDestinationModifier<
36-
State,
37-
Action,
38-
DestinationState,
39-
DestinationAction,
40-
CoverContent: View
41-
>: ViewModifier {
42-
let store: Store<PresentationState<State>, PresentationAction<Action>>
43-
@StateObject var viewStore: ViewStore<Bool, PresentationAction<Action>>
44-
let toDestinationState: (State) -> DestinationState?
45-
let fromDestinationAction: (DestinationAction) -> Action
46-
let coverContent: (Store<DestinationState, DestinationAction>) -> CoverContent
36+
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
37+
private struct PresentationNavigationDestinationModifier<
38+
State,
39+
Action,
40+
DestinationState,
41+
DestinationAction,
42+
CoverContent: View
43+
>: ViewModifier {
44+
let store: Store<PresentationState<State>, PresentationAction<Action>>
45+
@StateObject var viewStore: ViewStore<Bool, PresentationAction<Action>>
46+
let toDestinationState: (State) -> DestinationState?
47+
let fromDestinationAction: (DestinationAction) -> Action
48+
let coverContent: (Store<DestinationState, DestinationAction>) -> CoverContent
4749

48-
init(
49-
store: Store<PresentationState<State>, PresentationAction<Action>>,
50-
state toDestinationState: @escaping (State) -> DestinationState?,
51-
action fromDestinationAction: @escaping (DestinationAction) -> Action,
52-
content coverContent: @escaping (Store<DestinationState, DestinationAction>) -> CoverContent
53-
) {
54-
self.store = store
55-
self._viewStore = StateObject(
56-
wrappedValue: ViewStore(
57-
store
58-
.filter { state, _ in state.wrappedValue != nil }
59-
.scope(state: { $0.wrappedValue.flatMap(toDestinationState) != nil })
50+
init(
51+
store: Store<PresentationState<State>, PresentationAction<Action>>,
52+
state toDestinationState: @escaping (State) -> DestinationState?,
53+
action fromDestinationAction: @escaping (DestinationAction) -> Action,
54+
content coverContent: @escaping (Store<DestinationState, DestinationAction>) -> CoverContent
55+
) {
56+
self.store = store
57+
self._viewStore = StateObject(
58+
wrappedValue: ViewStore(
59+
store
60+
.filter { state, _ in state.wrappedValue != nil }
61+
.scope(state: { $0.wrappedValue.flatMap(toDestinationState) != nil })
62+
)
6063
)
61-
)
62-
self.toDestinationState = toDestinationState
63-
self.fromDestinationAction = fromDestinationAction
64-
self.coverContent = coverContent
65-
}
64+
self.toDestinationState = toDestinationState
65+
self.fromDestinationAction = fromDestinationAction
66+
self.coverContent = coverContent
67+
}
6668

67-
func body(content: Content) -> some View {
68-
content.navigationDestination(isPresented: self.viewStore.binding(send: .dismiss)) {
69-
IfLetStore(
70-
self.store.scope(
71-
state: returningLastNonNilValue { $0.wrappedValue.flatMap(self.toDestinationState) },
72-
action: { .presented(self.fromDestinationAction($0)) }
73-
),
74-
then: self.coverContent
75-
)
69+
func body(content: Content) -> some View {
70+
content.navigationDestination(isPresented: self.viewStore.binding(send: .dismiss)) {
71+
IfLetStore(
72+
self.store.scope(
73+
state: returningLastNonNilValue { $0.wrappedValue.flatMap(self.toDestinationState) },
74+
action: { .presented(self.fromDestinationAction($0)) }
75+
),
76+
then: self.coverContent
77+
)
78+
}
7679
}
7780
}
78-
}
81+
#endif

0 commit comments

Comments
 (0)