Skip to content

Commit

Permalink
Add migration guide for 1.16. (#3491)
Browse files Browse the repository at this point in the history
* Add migration guide for 1.16.

* Update README.md

---------

Co-authored-by: Stephen Celis <stephen@stephencelis.com>
  • Loading branch information
mbrandonw and stephencelis authored Nov 19, 2024
1 parent 333bc82 commit 69011b6
Show file tree
Hide file tree
Showing 36 changed files with 295 additions and 69 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,13 +532,14 @@ advanced usages.
The documentation for releases and `main` are available here:

* [`main`](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/)
* [1.15.0](https://pointfreeco.github.io/swift-composable-architecture/1.15.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.15))
* [1.16.0](https://pointfreeco.github.io/swift-composable-architecture/1.16.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.16))

<details>
<summary>
Other versions
</summary>

* [1.15.0](https://pointfreeco.github.io/swift-composable-architecture/1.15.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.15))
* [1.14.0](https://pointfreeco.github.io/swift-composable-architecture/1.14.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.14))
* [1.13.0](https://pointfreeco.github.io/swift-composable-architecture/1.13.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.13))
* [1.12.0](https://pointfreeco.github.io/swift-composable-architecture/1.12.0/documentation/composablearchitecture/) ([migration guide](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.12))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ APIs, and these guides contain tips to do so.
## Topics

- <doc:MigratingTo1.16>
- <doc:MigratingTo1.15>
- <doc:MigratingTo1.14>
- <doc:MigratingTo1.13>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Migrating to 1.16

The `.appStorage` strategy used with `@Shared` now uses key-value observing instead of
`NotificationCenter` when possible. Learn how this may affect your code.

## Overview

There are no steps needed to migrate to 1.16 of the Composable Architecture, but there has been
a change to the underlying behavior of `.appStorage` that one should be aware of. When using
`.appStorage` with `@Shared`, if your key does not contain the characters "." or "@", then changes
to that key in `UserDefaults` will be observed using key-value observing (KVO).
Otherwise, `NotificationCenter` will be used to observe changes.

KVO is a far more efficient way of observing changes to `UserDefaults` and it works cross-process,
such as from widgets and app extensions. However, KVO does not work when the keys contain "."
or "@", and so in those cases we must use the cruder tool of `NotificationCenter`. That is not
as efficient, and it forces us to perform a thread-hop when the notification is posted before
we can update the `@Shared` value. For this reason it is not possible to animate changes that are
made directly to `UserDefaults`:

```swift
withAnimation {
// ⚠️ This will not animate any SwiftUI views using '@Shared(.appStorage("co.pointfree.count"))'
UserDefaults.standard.set(0, forKey: "co.pointfree.count")
}
```

In general, we recommend using other delimeters for your keys, such as "/", ":", "-", etc.:

```swift
@Shared(.appStorage("co:pointfree:count")) var count = 0
```
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ enum Action {
```

And in the reducer, instead of invoking
``Reducer/forEach(_:action:element:fileID:filePath:line:column:)-3dw7i`` with a case path using the
``Reducer/forEach(_:action:element:fileID:filePath:line:column:)-6zye8`` with a case path using the
`/` prefix operator:

```swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,7 @@ func viewDidLoad() {
}
```

This can now be done more simply using the ``ObjectiveC/NSObject/observe(_:)`` method defined on
This can now be done more simply using the ``ObjectiveC/NSObject/observe(_:)-94oxy`` method defined on
all `NSObject`s:

```swift
Expand All @@ -920,7 +920,7 @@ func viewDidLoad() {
}
```

Be sure to read the documentation for ``ObjectiveC/NSObject/observe(_:)`` to learn how to best
Be sure to read the documentation for ``ObjectiveC/NSObject/observe(_:)-94oxy`` to learn how to best
wield this tool.

### Replacing Store.ifLet
Expand All @@ -940,7 +940,7 @@ store
```

This can now be done more simply using the `observe` method and
``Store/scope(state:action:fileID:filePath:line:column:)-2ck1n``:
``Store/scope(state:action:fileID:filePath:line:column:)-3yvuf``:

```swift
observe {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ should take a plain, non-`Shared` value and you construct the `Shared` value in
* You are using a persistence strategy with shared state (_e.g._
``PersistenceReaderKey/appStorage(_:)-4l5b``, ``PersistenceReaderKey/fileStorage(_:decoder:encoder:)``, _etc._),
then the initializer should take a plain, non-`Shared` value and you construct the `Shared` value in
the initializer using ``Shared/init(wrappedValue:_:fileID:line:)-512rh`` which takes a
the initializer using ``Shared/init(wrappedValue:_:fileID:line:)-9kfmy`` which takes a
``PersistenceKey`` as the second argument:

```swift
Expand All @@ -338,7 +338,7 @@ the initializer using ``Shared/init(wrappedValue:_:fileID:line:)-512rh`` which t

> Important: The value passed to this initializer is only used if the external storage does not
> already have a value. If a value exists in the storage then it is not used. In fact, the
> `wrappedValue` argument of ``Shared/init(wrappedValue:_:fileID:line:)-512rh`` is an
> `wrappedValue` argument of ``Shared/init(wrappedValue:_:fileID:line:)-9kfmy`` is an
> `@autoclosure` so that it is not even evaluated if not used. For that reason you
> may prefer to make the argument to the initializer an `@autoclosure` so that it too is evaluated
> only if actually used:
Expand Down Expand Up @@ -436,7 +436,7 @@ responsible for persisting and deriving shared state to pass to the child.
If your shared state is a collection, and in particular an `IdentifiedArray`, then we have another
tool for deriving shared state to a particular element of the array. You can subscript into a
``Shared`` collection with the `[id:]` subscript, and that will give a piece of optional shared
state (thanks to a dynamic member overload ``Shared/subscript(dynamicMember:)-7ibhr``), which you
state (thanks to a dynamic member overload ``Shared/subscript(dynamicMember:)-9xw64``), which you
can then unwrap to turn into honest shared state:

```swift
Expand Down Expand Up @@ -1071,7 +1071,7 @@ own implementations of `encode(to:)` and `init(from:)` that do the appropriate t
For example, if the data type is sharing state with a persistence strategy, you can decode by
delegating to the memberwise initializer that implicitly loads the shared value from the property
wrapper's persistence strategy, or you can explicitly initialize a shared value via
``Shared/init(wrappedValue:_:fileID:line:)-512rh``. And for encoding you can often skip encoding
``Shared/init(wrappedValue:_:fileID:line:)-9kfmy``. And for encoding you can often skip encoding
the shared value:

```swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,29 @@

### Storing a value

- ``PersistenceReaderKey/appStorage(_:)-4l5b``
- ``PersistenceReaderKey/appStorage(_:)-6d47p``
- ``PersistenceReaderKey/appStorage(_:)-6tsph``
- ``PersistenceReaderKey/appStorage(_:)-69h4r``
- ``PersistenceReaderKey/appStorage(_:)-xphy``
- ``PersistenceReaderKey/appStorage(_:)-617ld``
- ``PersistenceReaderKey/appStorage(_:)-6lnxu``
- ``PersistenceReaderKey/appStorage(_:)-ibg0``
- ``PersistenceReaderKey/appStorage(_:)-4l5b`` <!-- Bool -->
- ``PersistenceReaderKey/appStorage(_:)-6d47p`` <!-- Data -->
- ``PersistenceReaderKey/appStorage(_:)-6tsph`` <!-- Double -->
- ``PersistenceReaderKey/appStorage(_:)-69h4r`` <!-- Integer -->
- ``PersistenceReaderKey/appStorage(_:)-xphy`` <!-- String -->
- ``PersistenceReaderKey/appStorage(_:)-617ld`` <!-- URL -->
- ``PersistenceReaderKey/appStorage(_:)-6k27r`` <!-- RawRepresentable<Int> -->
- ``PersistenceReaderKey/appStorage(_:)-m54v`` <!-- RawRepresentable<String> -->

### Storing an optional value

- ``PersistenceReaderKey/appStorage(_:)-4s3s5``
- ``PersistenceReaderKey/appStorage(_:)-2dfnh``
- ``PersistenceReaderKey/appStorage(_:)-5wv8g``
- ``PersistenceReaderKey/appStorage(_:)-40e42``
- ``PersistenceReaderKey/appStorage(_:)-4veqp``
- ``PersistenceReaderKey/appStorage(_:)-7rox5``
- ``PersistenceReaderKey/appStorage(_:)-2keyn``
- ``PersistenceReaderKey/appStorage(_:)-7u49u``
- ``PersistenceReaderKey/appStorage(_:)-4s3s5`` <!-- Bool -->
- ``PersistenceReaderKey/appStorage(_:)-2dfnh`` <!-- Data -->
- ``PersistenceReaderKey/appStorage(_:)-5wv8g`` <!-- Double -->
- ``PersistenceReaderKey/appStorage(_:)-40e42`` <!-- Integer -->
- ``PersistenceReaderKey/appStorage(_:)-4veqp`` <!-- String -->
- ``PersistenceReaderKey/appStorage(_:)-7rox5`` <!-- URL -->
- ``PersistenceReaderKey/appStorage(_:)-2cfq9`` <!-- RawRepresentable<Int> -->
- ``PersistenceReaderKey/appStorage(_:)-9j150`` <!-- RawRepresentable<String> -->

### Key-path access

- ``PersistenceReaderKey/appStorage(_:)-5jsie``
- ``PersistenceReaderKey/appStorage(_:)-69h4r``
- ``AppStorageKeyPathKey``

### Overriding app storage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ instead.
- ``Reducer/ifLet(_:action:destination:fileID:filePath:line:column:)-5y8z4``
- ``Reducer/ifLet(_:action:fileID:filePath:line:column:)-12kry``
- ``Reducer/ifCaseLet(_:action:then:fileID:filePath:line:column:)-403y9``
- ``Reducer/forEach(_:action:element:fileID:filePath:line:column:)-1oguc``
- ``Reducer/forEach(_:action:element:fileID:filePath:line:column:)-o1gn``
- ``Reducer/forEach(_:action:destination:fileID:filePath:line:column:)-74erx``
- ``Reducer/onChange(of:removeDuplicates:_:)``

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ xcodebuild -skipMacroValidation …
- ``Scope``
- ``ifLet(_:action:then:fileID:filePath:line:column:)-2r2pn``
- ``ifCaseLet(_:action:then:fileID:filePath:line:column:)-7sg8d``
- ``forEach(_:action:element:fileID:filePath:line:column:)-3dw7i``
- ``forEach(_:action:element:fileID:filePath:line:column:)-6zye8``
- <doc:Navigation>

### Sharing state
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# ``ComposableArchitecture/Reducer/forEach(_:action:element:fileID:filePath:line:column:)-3dw7i``
# ``ComposableArchitecture/Reducer/forEach(_:action:element:fileID:filePath:line:column:)-6zye8``

## Topics

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,22 @@

### Creating a persisted value

- ``init(wrappedValue:_:fileID:line:)-512rh``
- ``init(wrappedValue:_:fileID:line:)-7a80y``
- ``init(wrappedValue:_:fileID:line:)-9kfmy``
- ``init(wrappedValue:_:fileID:line:)-7ndwc``
- ``init(_:fileID:line:)-8zcy1``
- ``init(_:fileID:line:)-8jqg5``
- ``init(_:fileID:line:)-gluj``
- ``init(_:fileID:line:)-9d3q``
- ``init(_:fileID:line:)-1q6ev``

### Accessing the value

- ``wrappedValue``
- ``projectedValue``
- ``reader``
- ``subscript(dynamicMember:)-6kmzm``
- ``subscript(dynamicMember:)-22ga9``
- ``subscript(dynamicMember:)-6dq81``
- ``subscript(dynamicMember:)-7n9xc``
- ``subscript(dynamicMember:)-6f2x``
- ``subscript(dynamicMember:)-9xw64``

### Isolating the value

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@

### Creating a persisted value

- ``init(wrappedValue:_:fileID:line:)-7q52``
- ``init(wrappedValue:_:fileID:line:)-6asu2``
- ``init(wrappedValue:_:fileID:line:)-7f68o``
- ``init(wrappedValue:_:fileID:line:)-galu``
- ``init(_:fileID:line:)-41rb8``
- ``init(_:fileID:line:)-3lxyf``
- ``init(_:fileID:line:)-hzp``
- ``init(_:fileID:line:)-5bxk6``

### Getting the value

- ``wrappedValue``
- ``projectedValue``
- ``subscript(dynamicMember:)-34wfb``
- ``subscript(dynamicMember:)-pigs``
- ``subscript(dynamicMember:)-2barb``

### SwiftUI integration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@

### Writable, bindable state

- ``Store/state-3ppqv``
- ``Store/state-7k27v``
- ``Store/state-20w4g``
- ``Store/state-2wgiw``
- ``Store/state-1qxwl``
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

### Configuring exhaustivity

- ``withDependencies(_:operation:)-3x2vc``
- ``withDependencies(_:operation:)-988rh``
- ``withDependencies(_:operation:)-61in2``
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
### Configuring exhaustivity

- ``Exhaustivity``
- ``withExhaustivity(_:operation:)-9psu7``
- ``withExhaustivity(_:operation:)-3fqeg``
- ``withExhaustivity(_:operation:)-1mhu4``
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ integrate into application code written in UIKit.

### Subscribing to state changes

- ``ObjectiveC/NSObject/observe(_:)``
- ``ObjectiveC/NSObject/observe(_:)-94oxy``
- ``ObservationToken``

### Presenting alerts and action sheets
Expand Down
6 changes: 5 additions & 1 deletion Sources/ComposableArchitecture/Effect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,12 @@ extension Effect {
/// - priority: Priority of the underlying task. If `nil`, the priority will come from
/// `Task.currentPriority`.
/// - operation: The operation to execute.
/// - catch: An error handler, invoked if the operation throws an error other than
/// - handler: An error handler, invoked if the operation throws an error other than
/// `CancellationError`.
/// - fileID: The fileID.
/// - filePath: The filePath.
/// - line: The line.
/// - column: The column.
/// - Returns: An effect wrapping the given asynchronous work.
public static func run(
priority: TaskPriority? = nil,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ extension Store where State: ObservableState {
/// - Parameters:
/// - state: A key path to an identified array of child state.
/// - action: A case key path to an identified child action.
/// - column: The column.
/// - fileID: The fileID.
/// - filePath: The filePath.
/// - line: The line.
/// - Returns: An collection of stores of child state.
@_disfavoredOverload
public func scope<ElementID, ElementState, ElementAction>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ extension NavigationLink where Destination == Never {
/// - state: An optional value to present. When the user selects the link, SwiftUI stores a
/// copy of the value. Pass a `nil` value to disable the link.
/// - label: A label that describes the view that this link presents.
/// - fileID: The fileID.
/// - filePath: The filePath.
/// - line: The line.
/// - column: The column.
#if compiler(>=6)
@MainActor
#endif
Expand Down Expand Up @@ -269,6 +273,8 @@ extension NavigationLink where Destination == Never {
/// presents.
/// - state: An optional value to present. When the user selects the link, SwiftUI stores a
/// copy of the value. Pass a `nil` value to disable the link.
/// - fileID: The fileID.
/// - line: The line.
#if compiler(>=6)
@MainActor
#endif
Expand All @@ -292,6 +298,8 @@ extension NavigationLink where Destination == Never {
/// - title: A string that describes the view that this link presents.
/// - state: An optional value to present. When the user selects the link, SwiftUI stores a
/// copy of the value. Pass a `nil` value to disable the link.
/// - fileID: The fileID.
/// - line: The line.
#if compiler(>=6)
@MainActor
#endif
Expand Down
16 changes: 16 additions & 0 deletions Sources/ComposableArchitecture/Observation/Store+Observation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ extension Store where State: ObservableState {
/// - Parameters:
/// - state: A key path to optional child state.
/// - action: A case key path to child actions.
/// - fileID: The fileID.
/// - filePath: The filePath.
/// - line: The line.
/// - column: The column.
/// - Returns: An optional store of non-optional child state and actions.
public func scope<ChildState, ChildAction>(
state: KeyPath<State, ChildState?>,
Expand Down Expand Up @@ -153,6 +157,10 @@ extension Binding {
/// - Parameters:
/// - state: A key path to optional child state.
/// - action: A case key path to presentation child actions.
/// - fileID: The fileID.
/// - filePath: The filePath.
/// - line: The line.
/// - column: The column.
/// - Returns: A binding of an optional child store.
#if swift(>=5.10)
@preconcurrency@MainActor
Expand Down Expand Up @@ -228,6 +236,10 @@ extension SwiftUI.Bindable {
/// - Parameters:
/// - state: A key path to optional child state.
/// - action: A case key path to presentation child actions.
/// - fileID: The fileID.
/// - filePath: The filePath.
/// - line: The line.
/// - column: The column.
/// - Returns: A binding of an optional child store.
#if swift(>=5.10)
@preconcurrency@MainActor
Expand Down Expand Up @@ -306,6 +318,10 @@ extension Perception.Bindable {
/// - Parameters:
/// - state: A key path to optional child state.
/// - action: A case key path to presentation child actions.
/// - fileID: The fileID.
/// - filePath: The filePath.
/// - line: The line.
/// - column: The column.
/// - Returns: A binding of an optional child store.
public func scope<State: ObservableState, Action, ChildState, ChildAction>(
state: KeyPath<State, ChildState?>,
Expand Down
Loading

0 comments on commit 69011b6

Please sign in to comment.