mirror of
https://github.com/pointfreeco/swift-composable-architecture.git
synced 2025-12-24 12:14:25 +01:00
* bring back view store performance
* wip
* Allow chaining of store bindings
* wip
* Localize ignoring bindings to text field resignation/dismissal
* wip
* fix DiagnosticsError message (#2597)
* store collection
* wip
* wip
* update migration guide
* Add `@Presents` macro for observable presentation
While it would be nice for the `@PresentationState` property wrapper to
"just work" with TCA's upcoming observable tools, sadly that does not
seem to be the case. Adding observation directly to
`@PresentationState`, as we have done with the beta so far, can break
existing projects due to the additional observation. This primarily
manifests itself in projects that present navigation stacks, where the
`@PresentationState` observation can cause the navigation hierarchy to
recompute and trigger SwiftUI bugs.
The best we've come up with so far is introducing a brand new macro that
automatically wraps a property with `@PresentationState` _and_
instruments it with observation.
We're open to other ideas, and we do have future plans to eliminate the
need for a property wrapper or macro at all, but till then this offers a
non-breaking upgrade path!
* fixes
* Observe child store changes
* wip
* wip
* wip
* Fix typo in MigratingTo1.6.md (#2608)
* Rename bindingViewStore argument to store in MigratingTo1.6.md (#2611)
* wip
* Revert "wip"
This reverts commit f221ed0e1a.
* Add `@Presents` macro for observable presentation (#2604)
* Add `@Presents` macro for observable presentation
While it would be nice for the `@PresentationState` property wrapper to
"just work" with TCA's upcoming observable tools, sadly that does not
seem to be the case. Adding observation directly to
`@PresentationState`, as we have done with the beta so far, can break
existing projects due to the additional observation. This primarily
manifests itself in projects that present navigation stacks, where the
`@PresentationState` observation can cause the navigation hierarchy to
recompute and trigger SwiftUI bugs.
The best we've come up with so far is introducing a brand new macro that
automatically wraps a property with `@PresentationState` _and_
instruments it with observation.
We're open to other ideas, and we do have future plans to eliminate the
need for a property wrapper or macro at all, but till then this offers a
non-breaking upgrade path!
* wip
* Fix perception bindings (#2609)
* Fix runtime warning when binding accesses perceptible state.
* Fix runtime warning in SwiftUI bindings.
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
---------
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* wip
* wip
* fix
* wip
* wip
* wip
* Check observable state identity for presentation state.
* Add willSset/didSet to registrar types.
* clean up @Presents
* clean up
* fix
* Emit observation warnings in escaping contexts like `ForEach` and `sheet` (#2613)
* Fix perception warning in ForEach.
* fix
---------
Co-authored-by: Brandon Williams <mbrandonw@hey.com>
* Introduce @ViewAction(for:) macro. (#2612)
* Add back @ViewAction macro.
* wip
* wip
* wip
* wip
* wip
* clean up
* wip
* wip
* fix migration guide'
* ViewActionable
* wip
* rename
* wip
* wip
---------
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* Introduce @BindableStore for bindings in pre-iOS 17 (#2610)
* Introduce @BindableStore.
* docs
* wip
* wip
* fixc
* wip
* wip
* wip
* wip
---------
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* re-record intergration logs
* wip
* wip
* localize invalid stores to store collection
* Deprecate closure-based `store.scope` operations (#2618)
These uncached operations can be problematic, especially when working
with observation, which often depends on the stable identity of stores.
* document
* Update warning message
* Performance Improvement: Skip perception checks when calling reducers. (#2622)
* Skip perception checks when calling reducers.
* inline withoutPerceptionChecking() for RELEASE
Co-authored-by: Brandon Williams <135203+mbrandonw@users.noreply.github.com>
---------
Co-authored-by: Brandon Williams <135203+mbrandonw@users.noreply.github.com>
* Don't show perception warnings in action closures. (#2614)
* Don't show perception warnings in action closures.
* wip
* wip
* wip
* clean up
* wip
---------
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* fix BindableStore + release
* Add docs
* Change associated type names of ViewActionSending (#2629)
* Fix some @ViewAction annoyances.
* wip
* wip
* wip
* wip
* wip
* wip
* fix
* wip
* fixed merge
* Add new view modifiers for observing alerts/dialogs (#2628)
* Add new view modifiers for observing alerts/dialogs
Instead of:
```swift
.alert(store: store.scope(state: \.$alert, action: \.alert))
```
You can now do:
```swift
.alert($store.scope(state: \.alert, action: \.alert))
```
This new modifier is powered by the same store binding scope operation
that can power `sheet(item:)`, etc., and is much lighter weight than the
previous view modifier, which spun up view stores and `WithViewStore`
views.
* wip
* wip
* wip
---------
Co-authored-by: Brandon Williams <mbrandonw@hey.com>
* Fix uncached warning when using Store.ifLet (#2625)
* Fix uncached warning when using Store.ifLet
* wip
* wip
* wip
* wip
* wip
* wip
---------
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* Resolve packages
* Updated scopes
* wip
* wip
* updated binding docs
* adding docs
* clean up
* wip
* wip
* wip
* clean up
* clean up
* clean up
* wip;
* lots of fixes
* update more docs
* fix
* wip
* wip
* Remove ObservationRegistrarWrapper. (#2634)
* Remove ObservationRegistrarWrapper.
* Delete Sources/ComposableArchitecture/Internal/ObservationRegistrarWrapper.swift
---------
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* more docs
* update docs
* a few more tests
* fix
* wip
* wip
* wip
* Cache data in store collections (#2635)
* fix tutorial highlighting
* wip
* wip
* wip
* wip
* tests for observation of special domain types
* another test
* fix
* wip
* Implement memoization for perception checks (#2630)
* Implement memoization for isInSwiftUIBody
* tidy up
* Perception caching updates (#2649)
* Small updates to perception caching.
* wip
* debug
* some more macro tests
* syncups tutorial beginnings
* wip
* wip
* wip
* wip
* wip
* merge fixes
* wip
* update tests
* fix
* fix
* fix perception checking in store
* rename task local
* delete old test
* deprecate test using old apis
* fix test
* perception tests for store
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Opt out of key path for Store.ifLet
* sync ups
* lots more sync up tutorial
* more sync ups tutorial
* wip
* wip
* wio
* wip
* wip
* wip
* updated references of 1.6 to 1.7
* wip
* no need to force unwrap here
* fixed crash in ForEach with bindings
* more sync ups tutorial
* more sync ups tutorial
* wip
* more sync ups
* wip
* wip
* Better support for observing copies of values (#2650)
* Explore using _modify
* wip
* wip
* wip
* wip
* wip
* wip
* more tests
* wip
* get another failing test for an edge case
* wip
* tests all passing
* flag for determining when new state was created
* wip
* clean up
* wip
* wip
* wip;
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* New test that currently fails.
* wip
* wip
* Update Sources/ComposableArchitectureMacros/PresentsMacro.swift
* wip
* remove redundant attached member attribute
* storage
* cleanup
* more benchmarks and tests
* wip
* wip
* wip
* wip
* update tests
* wip
* wip
---------
Co-authored-by: Brandon Williams <mbrandonw@hey.com>
* wip
* wip
* wip
* swift-format
* fix
* wip
* wip
* wip
* wip
* Perception
* wip
* wip
* clean up shared state
* fix shared state tests
* wip
* add alert test
* wip
* wip
* wip
* wip
* Use transaction in binding
* wip
* wip
* wip
* wip
* wip
* wip
* uikit
* keep references to controllers when presenting so that we can properly dismiss
* change order of features in shared state demo
* wip
* cleanup
* cleanup
* wip
* wip
* wip
* Fix perception checking for effect actions.
* wip
* wip
* wip
* Fix perception checking for effect actions.
* wip
* wip
* remove sync ups tutorial
* wip
* wip
* wip
* wip
* wip
* docs for observe function for uikit
* Add cancellation to observation'
* re-record integration test snapshots
* fixed some todos
* update test
* remove 5.9.2 checks
* wip
* improve docs
* update docs
* updates
* lots of fixes
* more docs
* remove unneeded file;
* wip
* wip
* wip
* update readme and getting started
* wip
* simplify
* migration stuff
* wip
* Update Models.swift
* wip
* wip
* wip
* Update Bindings.md
* wip
* wip
* wip
* wip
* fix
* wip
* wip
* wip
* wip
* wip
Co-authored-by: Kabir Oberai <oberai.kabir@gmail.com>
---------
Co-authored-by: Brandon Williams <mbrandonw@hey.com>
Co-authored-by: hmhv <admin@hmhv.info>
Co-authored-by: Jimmy Prime <jimmylevelup@gmail.com>
Co-authored-by: Michael Pohl <15653162+Mika5652@users.noreply.github.com>
Co-authored-by: Brandon Williams <135203+mbrandonw@users.noreply.github.com>
Co-authored-by: George Scott <gscott@gekkoto.com>
Co-authored-by: Kabir Oberai <oberai.kabir@gmail.com>
241 lines
8.5 KiB
Swift
241 lines
8.5 KiB
Swift
#if swift(>=5.9)
|
||
import Observation
|
||
|
||
/// Helps implement the conformance to the ``Reducer`` protocol for a type.
|
||
///
|
||
/// To use this macro you will define a new type, typically a struct, and add inner types for the
|
||
/// ``Reducer/State`` and ``Reducer/Action`` associated types, as well as an implementation of the
|
||
/// reducer's ``Reducer/body-8lumc``:
|
||
///
|
||
/// ```swift
|
||
/// @Reducer
|
||
/// struct Feature {
|
||
/// struct State {
|
||
/// var count = 0
|
||
/// }
|
||
/// enum Action {
|
||
/// case decrementButtonTapped
|
||
/// case incrementButtonTapped
|
||
/// }
|
||
/// var body: some ReducerOf<Self> {
|
||
/// Reduce { state, action in
|
||
/// switch action {
|
||
/// case .decrementButtonTapped:
|
||
/// state.count -= 1
|
||
/// return .none
|
||
/// case .incrementButtonTapped:
|
||
/// state.count += 1
|
||
/// return .none
|
||
/// }
|
||
/// }
|
||
/// }
|
||
/// }
|
||
/// ```
|
||
///
|
||
/// This will expand Swift code to conform `Feature` to the ``Reducer`` protocol:
|
||
///
|
||
/// ```diff
|
||
/// +extension Feature: Reducer {}
|
||
/// ```
|
||
///
|
||
/// It will also apply the `@CasePathable` macro to the `enum Action`:
|
||
///
|
||
/// ```diff
|
||
/// +@CasePathable
|
||
/// enum Action {
|
||
/// // ...
|
||
/// }
|
||
/// ```
|
||
///
|
||
/// This will allow you to use key path syntax for specifying enum cases in various APIs in the
|
||
/// library, such as ``Reducer/ifLet(_:action:destination:fileID:line:)-4f2at``,
|
||
/// ``Reducer/forEach(_:action:destination:fileID:line:)-yz3v``, ``Scope``, and more.
|
||
///
|
||
/// Further, if the ``Reducer/State`` of your feature is an enum, which is useful for modeling a
|
||
/// feature that can be one of multiple mutually exclusive values, the ``Reducer()`` will apply
|
||
/// the `@CasePathable` macro, as well as `@dynamicMemberLookup`:
|
||
///
|
||
/// ```diff
|
||
/// +@CasePathable
|
||
/// +@dynamicMemberLookup
|
||
/// enum State {
|
||
/// // ...
|
||
/// }
|
||
/// ```
|
||
///
|
||
/// This will allow you to use key path syntax for specifying case paths to the `State`'s cases,
|
||
/// as well as allow you to use dot-chaining syntax for optionally extracting a case from the
|
||
/// state. This can be useful when using the operators that come with the library that allow for
|
||
/// driving navigation from an enum of options:
|
||
///
|
||
/// ```swift
|
||
/// .sheet(
|
||
/// item: $store.scope(state: \.destination?.editForm, action: \.destination.editForm)
|
||
/// )
|
||
/// ```
|
||
///
|
||
/// The syntax `state: \.destination?.editForm` is only possible due to both
|
||
/// `@dynamicMemberLookup` and `@CasePathable` being applied to the `State` enum.
|
||
///
|
||
/// ## Gotchas
|
||
///
|
||
/// ### Autocomplete
|
||
///
|
||
/// Applying `@Reducer` can break autocompletion in the `body` of the reducer. This is a known
|
||
/// [issue](https://github.com/apple/swift/issues/69477), and it can generally be worked around by
|
||
/// providing additional type hints to the compiler:
|
||
///
|
||
/// 1. Adding an explicit `Reducer` conformance in addition to the macro application can restore
|
||
/// autocomplete throughout the `body` of the reducer:
|
||
///
|
||
/// ```diff
|
||
/// @Reducer
|
||
/// -struct Feature {
|
||
/// +struct Feature: Reducer {
|
||
/// ```
|
||
///
|
||
/// 2. Adding explicit generics to instances of `Reduce` in the `body` can restore autocomplete
|
||
/// inside the `Reduce`:
|
||
///
|
||
/// ```diff
|
||
/// var body: some Reducer<State, Action> {
|
||
/// - Reduce { state, action in
|
||
/// + Reduce<State, Action> { state, action in
|
||
/// ```
|
||
///
|
||
/// ### Circular reference errors
|
||
///
|
||
/// There is currently a bug in the Swift compiler and macros that prevents you from extending
|
||
/// types that are inside other types with macros applied in the same file. For example, if you
|
||
/// wanted to extend a reducer's `State` with some extra functionality:
|
||
///
|
||
/// ```swift
|
||
/// @Reducer
|
||
/// struct Feature {
|
||
/// struct State { /* ... */ }
|
||
/// // ...
|
||
/// }
|
||
///
|
||
/// extension Feature.State { // 🛑 Circular reference
|
||
/// // ...
|
||
/// }
|
||
/// ```
|
||
///
|
||
/// This unfortunately does not work. It is a
|
||
/// [known issue](https://github.com/apple/swift/issues/66450), and the only workaround is to
|
||
/// either move the extension to a separate file, or move the code from the extension to be
|
||
/// directly inside the `State` type.
|
||
///
|
||
/// ### CI build failures
|
||
///
|
||
/// When testing your code on an external CI server you may run into errors such as the following:
|
||
///
|
||
/// > Error: CasePathsMacros Target 'CasePathsMacros' must be enabled before it can be used.
|
||
/// >
|
||
/// > ComposableArchitectureMacros Target 'ComposableArchitectureMacros' must be enabled
|
||
/// before it can be used.
|
||
///
|
||
/// You can fix this in one of two ways. You can write a default to the CI machine that allows
|
||
/// Xcode to skip macro validation:
|
||
///
|
||
/// ```shell
|
||
/// defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES
|
||
/// ```
|
||
///
|
||
/// Or if you are invoking `xcodebuild` directly in your CI scripts, you can pass the
|
||
/// `-skipMacroValidation` flag to `xcodebuild` when building your project:
|
||
///
|
||
/// ```shell
|
||
/// xcodebuild -skipMacroValidation …
|
||
/// ```
|
||
@attached(memberAttribute)
|
||
@attached(extension, conformances: Reducer)
|
||
public macro Reducer() =
|
||
#externalMacro(
|
||
module: "ComposableArchitectureMacros", type: "ReducerMacro"
|
||
)
|
||
|
||
/// Defines and implements conformance of the Observable protocol.
|
||
@attached(extension, conformances: Observable, ObservableState)
|
||
@attached(member, names: named(_$id), named(_$observationRegistrar), named(_$willModify))
|
||
@attached(memberAttribute)
|
||
public macro ObservableState() =
|
||
#externalMacro(module: "ComposableArchitectureMacros", type: "ObservableStateMacro")
|
||
|
||
@attached(accessor, names: named(init), named(get), named(set))
|
||
@attached(peer, names: prefixed(_))
|
||
public macro ObservationStateTracked() =
|
||
#externalMacro(module: "ComposableArchitectureMacros", type: "ObservationStateTrackedMacro")
|
||
|
||
@attached(accessor, names: named(willSet))
|
||
public macro ObservationStateIgnored() =
|
||
#externalMacro(module: "ComposableArchitectureMacros", type: "ObservationStateIgnoredMacro")
|
||
|
||
/// Wraps a property with ``PresentationState`` and observes it.
|
||
///
|
||
/// Use this macro instead of ``PresentationState`` when you adopt the ``ObservableState()``
|
||
/// macro, which is incompatible with property wrappers like ``PresentationState``.
|
||
@attached(accessor, names: named(init), named(get), named(set))
|
||
@attached(peer, names: prefixed(`$`), prefixed(_))
|
||
public macro Presents() =
|
||
#externalMacro(module: "ComposableArchitectureMacros", type: "PresentsMacro")
|
||
|
||
/// Provides a view with access to a feature's ``ViewAction``s.
|
||
///
|
||
/// If you want to restrict what actions can be sent from the view you can use this macro along
|
||
/// the ``ViewAction`` protocol. You start by conforming your reducer's `Action` enum to the
|
||
/// ``ViewAction`` protocol, and moving view-specific actions to its own inner enum:
|
||
///
|
||
/// ```swift
|
||
/// @Reducer
|
||
/// struct Feature {
|
||
/// struct State { /* ... */ }
|
||
/// enum Action: ViewAction {
|
||
/// case loginResponse(Bool)
|
||
/// case view(View)
|
||
///
|
||
/// enum View {
|
||
/// case loginButtonTapped
|
||
/// }
|
||
/// }
|
||
/// // ...
|
||
/// }
|
||
/// ```
|
||
///
|
||
/// Then you can apply the ``ViewAction(for:)`` macro to your view by specifying the type of the
|
||
/// reducer that powers the view:
|
||
///
|
||
/// ```swift
|
||
/// @ViewAction(for: Feature.self)
|
||
/// struct FeatureView: View {
|
||
/// let store: StoreOf<Feature>
|
||
/// // ...
|
||
/// }
|
||
/// ```
|
||
///
|
||
/// The macro does two things:
|
||
///
|
||
/// * It adds a `send` method to the view that you can use instead of `store.send`. This allows
|
||
/// you to send view actions more simply, without wrapping the action in `.view(…)`:
|
||
/// ```diff
|
||
/// Button("Login") {
|
||
/// - store.send(.view(.loginButtonTapped))
|
||
/// + send(.loginButtonTapped)
|
||
/// }
|
||
/// ```
|
||
/// * It creates warning diagnostics if you try sending actions through `store.send` rather than
|
||
/// using the `send` method on the view:
|
||
/// ```swift
|
||
/// Button("Login") {
|
||
/// store.send(.view(.loginButtonTapped))
|
||
/// //┬─────────
|
||
/// //╰─ ⚠️ Do not use 'store.send' directly when using '@ViewAction'
|
||
/// }
|
||
/// ```
|
||
@attached(extension, conformances: ViewActionSending)
|
||
public macro ViewAction<R: Reducer>(for: R.Type) =
|
||
#externalMacro(
|
||
module: "ComposableArchitectureMacros", type: "ViewActionMacro"
|
||
) where R.Action: ViewAction
|
||
#endif
|