Skip to content

Commit 7551bb8

Browse files
Fix navigationDestinationWrapper in docs. (#2765)
* Fix navigationDestinationWrapper in docs. * Update TreeBasedNavigation.md * Update TreeBasedNavigation.md * wip * update some presentation states --------- Co-authored-by: Stephen Celis <[email protected]>
1 parent 6a177dc commit 7551bb8

File tree

2 files changed

+31
-34
lines changed

2 files changed

+31
-34
lines changed

Sources/ComposableArchitecture/Documentation.docc/Articles/TreeBasedNavigation.md

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ the rest.
1818

1919
## Basics
2020

21-
The tools for this style of navigation include the ``PresentationState`` property wrapper,
22-
``PresentationAction``, the ``Reducer/ifLet(_:action:destination:fileID:line:)-4f2at`` operator, and
23-
a bunch of APIs that mimic SwiftUI's regular tools, such as `.sheet`, `.popover`, etc., but tuned
24-
specifically for the Composable Architecture.
21+
The tools for this style of navigation include the ``Presents()`` macro,
22+
``PresentationAction``, the ``Reducer/ifLet(_:action:destination:fileID:line:)-4f2at`` operator,
23+
and that is all. Once your feature is properly integrated with those tools you can use all of
24+
SwiftUI's normal navigation view modifiers, such as `sheet(item:)`, `popover(item:)`, etc.
2525

2626
The process of integrating two features together for navigation largely consists of 2 steps:
2727
integrating the features' domains together and integrating the features' views together. One
@@ -31,7 +31,7 @@ into the parent.
3131

3232
For example, suppose you have a list of items and you want to be able to show a sheet to display a
3333
form for adding a new item. We can integrate state and actions together by utilizing the
34-
``PresentationState`` and ``PresentationAction`` types:
34+
``Presents()`` macro and ``PresentationAction`` type:
3535

3636
```swift
3737
@Reducer
@@ -214,7 +214,7 @@ struct InventoryFeature {
214214
> enums.
215215
216216
With that done we can now hold onto a _single_ piece of optional state in our feature, using the
217-
``PresentationState`` property wrapper, and we hold onto the destination actions using the
217+
``Presents()`` macro, and we hold onto the destination actions using the
218218
``PresentationAction`` type:
219219

220220
```swift
@@ -382,35 +382,29 @@ project:
382382

383383
```swift
384384
extension View {
385-
@available(iOS, introduced: 13, deprecated: 16)
386-
@available(macOS, introduced: 10.15, deprecated: 13)
387-
@available(tvOS, introduced: 13, deprecated: 16)
388-
@available(watchOS, introduced: 6, deprecated: 9)
385+
@available(iOS, introduced: 16, deprecated: 17)
386+
@available(macOS, introduced: 13, deprecated: 14)
387+
@available(tvOS, introduced: 16, deprecated: 17)
388+
@available(watchOS, introduced: 9, deprecated: 10)
389389
@ViewBuilder
390390
func navigationDestinationWrapper<D: Hashable, C: View>(
391391
item: Binding<D?>,
392392
@ViewBuilder destination: @escaping (D) -> C
393393
) -> some View {
394-
if #available(iOS 17, macOS 14, tvOS 17, visionOS 1, watchOS 10, *) {
395-
navigationDestination(item: item, destination: destination)
396-
} else {
397-
navigationDestination(
398-
isPresented: Binding(
399-
get: { item.wrappedValue != nil },
400-
set: { isPresented, transaction in
401-
if !isPresented {
402-
item.transaction(transaction).wrappedValue = nil
403-
}
404-
}
405-
)
406-
) {
407-
if let item = item.wrappedValue {
408-
destination(item)
409-
}
394+
navigationDestination(isPresented: item.isPresented) {
395+
if let item = item.wrappedValue
396+
destination(item)
410397
}
411398
}
412399
}
413400
}
401+
402+
fileprivate extension Optional where Wrapped: Hashable {
403+
var isPresented: Bool {
404+
get { self != nil }
405+
set { if !newValue { self = nil } }
406+
}
407+
}
414408
```
415409

416410
If you target platforms earlier than iOS 16, macOS 13, tvOS 16 and watchOS 9, then you cannot use
@@ -600,8 +594,9 @@ struct CounterFeature {
600594
}
601595
```
602596

603-
And then let's embed that feature into a parent feature using ``PresentationState``,
604-
``PresentationAction`` and ``Reducer/ifLet(_:action:destination:fileID:line:)-4f2at``:
597+
And then let's embed that feature into a parent feature using the ``Presents()`` macro,
598+
``PresentationAction`` type and ``Reducer/ifLet(_:action:destination:fileID:line:)-4f2at``
599+
operator:
605600

606601
```swift
607602
@Reducer

Sources/ComposableArchitecture/Reducer/Reducers/PresentationReducer.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,16 +95,18 @@ public struct PresentationState<State> {
9595
///
9696
/// If you use the techniques of tree-based navigation (see <doc:TreeBasedNavigation>), then
9797
/// you will have a single enum that determines the destinations your feature can navigate to,
98-
/// and you will hold onto that state using the ``PresentationState`` property wrapper:
98+
/// and you will hold onto that state using the ``Presents()`` macro:
9999
///
100100
/// ```swift
101+
/// @ObservableState
101102
/// struct State {
102-
/// @PresentationState var destination: Destination.State
103+
/// @Presents var destination: Destination.State
103104
/// }
104105
/// ```
105106
///
106-
/// Using the projected value of the ``PresentationState`` property wrapper you can get a
107-
/// succinct syntax for modify the data in a particular case of the `Destination` enum, like so:
107+
/// The `destination` property has a projected value of ``PresentationState``, which gives you a
108+
/// succinct syntax for modifying the data in a particular case of the `Destination` enum, like
109+
/// so:
108110
///
109111
/// ```swift
110112
/// state.$destination[case: \.detail]?.alert = AlertState {
@@ -329,8 +331,8 @@ extension PresentationAction: Encodable where Action: Encodable {}
329331
extension Reducer {
330332
/// Embeds a child reducer in a parent domain that works on an optional property of parent state.
331333
///
332-
/// This version of `ifLet` requires the usage of ``PresentationState`` and ``PresentationAction``
333-
/// in your feature's domain.
334+
/// This version of `ifLet` requires the usage of the ``Presents()`` macro and
335+
/// ``PresentationAction`` type in your feature's domain.
334336
///
335337
/// For example, if a parent feature holds onto a piece of optional child state, then it can
336338
/// perform its core logic _and_ the child's logic by using the `ifLet` operator:

0 commit comments

Comments
 (0)