mirror of
https://github.com/pointfreeco/swift-composable-architecture.git
synced 2025-12-14 20:35:56 +01:00
Perception 2.0 (#3736)
* Perception 2.0
* wip
* wip
* wip
* wip
* wip
* Revert "wip"
This reverts commit c5d0a06017.
* wip
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "92f2ded678a41ef5d8bc6b77a6f478ed09039d89ffc674e73012e9f30791ecb5",
|
||||
"originHash" : "0e9a414ac23b15d7d00b87671275679eb6cce1d5dc101ec944f5e8aee4cb097b",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "combine-schedulers",
|
||||
@@ -105,8 +105,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-navigation",
|
||||
"state" : {
|
||||
"revision" : "ae208d1a5cf33aee1d43734ea780a09ada6e2a21",
|
||||
"version" : "2.3.1"
|
||||
"revision" : "4e89284c1966538109dc783497405bc680e9bc96",
|
||||
"version" : "2.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -114,8 +114,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-perception",
|
||||
"state" : {
|
||||
"revision" : "d924c62a70fca5f43872f286dbd7cef0957f1c01",
|
||||
"version" : "1.6.0"
|
||||
"revision" : "f4f57cac7d273cddf0161293d47adbb5a6ba3aed",
|
||||
"version" : "2.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -123,8 +123,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-sharing",
|
||||
"state" : {
|
||||
"revision" : "75e846ee3159dc75b3a29bfc24b6ce5a557ddca9",
|
||||
"version" : "2.5.2"
|
||||
"revision" : "5d87dda90ed048f216826efbad404110141161bb",
|
||||
"version" : "2.6.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -150,10 +150,10 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
|
||||
"state" : {
|
||||
"revision" : "39de59b2d47f7ef3ca88a039dff3084688fe27f4",
|
||||
"version" : "1.5.2"
|
||||
"revision" : "23e3442166b5122f73f9e3e622cd1e4bafeab3b7",
|
||||
"version" : "1.6.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
"version" : 3
|
||||
}
|
||||
|
||||
76
.github/workflows/ci.yml
vendored
76
.github/workflows/ci.yml
vendored
@@ -46,70 +46,6 @@ jobs:
|
||||
- name: Debug
|
||||
run: make XCODEBUILD_ARGUMENT="${{ matrix.command }}" CONFIG=Debug PLATFORM="${{ matrix.platform }}" WORKSPACE=.github/package.xcworkspace xcodebuild
|
||||
|
||||
xcodebuild:
|
||||
name: xcodebuild (15)
|
||||
runs-on: macos-14
|
||||
strategy:
|
||||
matrix:
|
||||
command: [test, '']
|
||||
platform:
|
||||
- IOS
|
||||
- MAC_CATALYST
|
||||
- MACOS
|
||||
- TVOS
|
||||
# - VISIONOS # Unfortunately, visionOS on CI is too flakey
|
||||
- WATCHOS
|
||||
xcode: [15.2, 15.4]
|
||||
exclude:
|
||||
- {xcode: 15.2, command: test}
|
||||
- {xcode: 15.4, command: ''}
|
||||
- {xcode: 15.2, platform: MAC_CATALYST}
|
||||
- {xcode: 15.2, platform: TVOS}
|
||||
# - {xcode: 15.2, platform: VISIONOS}
|
||||
- {xcode: 15.2, platform: WATCHOS}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Select Xcode ${{ matrix.xcode }}
|
||||
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
|
||||
- name: Update xcbeautify
|
||||
run: brew update && brew upgrade xcbeautify
|
||||
- name: Install visionOS runtime
|
||||
if: matrix.platform == 'visionOS'
|
||||
run: |
|
||||
sudo xcodebuild -runFirstLaunch
|
||||
sudo xcrun simctl list
|
||||
sudo xcodebuild -downloadPlatform visionOS
|
||||
sudo xcodebuild -runFirstLaunch
|
||||
- name: List available devices
|
||||
run: xcrun simctl list devices available
|
||||
- name: Cache derived data
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.derivedData
|
||||
key: |
|
||||
deriveddata-xcodebuild-${{ matrix.platform }}-${{ matrix.xcode }}-${{ matrix.command }}-${{ hashFiles('**/Sources/**/*.swift', '**/Tests/**/*.swift') }}
|
||||
restore-keys: |
|
||||
deriveddata-xcodebuild-${{ matrix.platform }}-${{ matrix.xcode }}-${{ matrix.command }}-
|
||||
- name: Set IgnoreFileSystemDeviceInodeChanges flag
|
||||
run: defaults write com.apple.dt.XCBuild IgnoreFileSystemDeviceInodeChanges -bool YES
|
||||
- name: Update mtime for incremental builds
|
||||
uses: chetan/git-restore-mtime-action@v2
|
||||
- name: Debug
|
||||
run: make XCODEBUILD_ARGUMENT="${{ matrix.command }}" CONFIG=Debug PLATFORM="${{ matrix.platform }}" WORKSPACE=.github/package.xcworkspace xcodebuild
|
||||
|
||||
library-evolution:
|
||||
name: Library (evolution)
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Select Xcode 15.4
|
||||
run: sudo xcode-select -s /Applications/Xcode_15.4.app
|
||||
- name: Update xcbeautify
|
||||
run: brew update && brew upgrade xcbeautify
|
||||
- name: Build for library evolution
|
||||
run: make build-for-library-evolution
|
||||
|
||||
examples:
|
||||
name: Examples
|
||||
runs-on: macos-15
|
||||
@@ -147,15 +83,3 @@ jobs:
|
||||
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="Todos" xcodebuild-raw
|
||||
- name: VoiceMemos
|
||||
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="VoiceMemos" xcodebuild-raw
|
||||
|
||||
check-macro-compatibility:
|
||||
name: Check Macro Compatibility
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Run Swift Macro Compatibility Check
|
||||
uses: Matejkob/swift-macro-compatibility-check@v1
|
||||
with:
|
||||
run-tests: false
|
||||
major-versions-only: true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "0479414dc97c4704849540dc64b58b99ce1f1c648e4cb9269d822b21cbc51b7f",
|
||||
"originHash" : "658be5678358d678b69ea40e4be4814633be8197318d5ac54b97fb40cfb2152b",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "combine-schedulers",
|
||||
@@ -33,8 +33,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-collections",
|
||||
"state" : {
|
||||
"revision" : "c1805596154bb3a265fd91b8ac0c4433b4348fb0",
|
||||
"version" : "1.2.0"
|
||||
"revision" : "8c0c0a8b49e080e54e5e328cc552821ff07cd341",
|
||||
"version" : "1.2.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -69,8 +69,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swiftlang/swift-docc-plugin",
|
||||
"state" : {
|
||||
"revision" : "d1691545d53581400b1de9b0472d45eb25c19fed",
|
||||
"version" : "1.4.4"
|
||||
"revision" : "3e4f133a77e644a5812911a0513aeb7288b07d06",
|
||||
"version" : "1.4.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -105,8 +105,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-navigation",
|
||||
"state" : {
|
||||
"revision" : "ae208d1a5cf33aee1d43734ea780a09ada6e2a21",
|
||||
"version" : "2.3.1"
|
||||
"revision" : "4e89284c1966538109dc783497405bc680e9bc96",
|
||||
"version" : "2.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -114,8 +114,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-perception",
|
||||
"state" : {
|
||||
"revision" : "d924c62a70fca5f43872f286dbd7cef0957f1c01",
|
||||
"version" : "1.6.0"
|
||||
"revision" : "f4f57cac7d273cddf0161293d47adbb5a6ba3aed",
|
||||
"version" : "2.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -123,8 +123,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-sharing",
|
||||
"state" : {
|
||||
"revision" : "75e846ee3159dc75b3a29bfc24b6ce5a557ddca9",
|
||||
"version" : "2.5.2"
|
||||
"revision" : "5d87dda90ed048f216826efbad404110141161bb",
|
||||
"version" : "2.6.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -132,8 +132,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-snapshot-testing.git",
|
||||
"state" : {
|
||||
"revision" : "37230a37e83f1b7023be08e1b1a2603fcb1567fb",
|
||||
"version" : "1.18.4"
|
||||
"revision" : "d7e40607dcd6bc26543f5d9433103f06e0b28f8f",
|
||||
"version" : "1.18.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -159,10 +159,10 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
|
||||
"state" : {
|
||||
"revision" : "39de59b2d47f7ef3ca88a039dff3084688fe27f4",
|
||||
"version" : "1.5.2"
|
||||
"revision" : "23e3442166b5122f73f9e3e622cd1e4bafeab3b7",
|
||||
"version" : "1.6.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
"version" : 3
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "92f2ded678a41ef5d8bc6b77a6f478ed09039d89ffc674e73012e9f30791ecb5",
|
||||
"originHash" : "0e9a414ac23b15d7d00b87671275679eb6cce1d5dc101ec944f5e8aee4cb097b",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "combine-schedulers",
|
||||
@@ -33,8 +33,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-collections",
|
||||
"state" : {
|
||||
"revision" : "c1805596154bb3a265fd91b8ac0c4433b4348fb0",
|
||||
"version" : "1.2.0"
|
||||
"revision" : "8c0c0a8b49e080e54e5e328cc552821ff07cd341",
|
||||
"version" : "1.2.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -69,8 +69,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swiftlang/swift-docc-plugin",
|
||||
"state" : {
|
||||
"revision" : "d1691545d53581400b1de9b0472d45eb25c19fed",
|
||||
"version" : "1.4.4"
|
||||
"revision" : "3e4f133a77e644a5812911a0513aeb7288b07d06",
|
||||
"version" : "1.4.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -105,8 +105,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-navigation",
|
||||
"state" : {
|
||||
"revision" : "ae208d1a5cf33aee1d43734ea780a09ada6e2a21",
|
||||
"version" : "2.3.1"
|
||||
"revision" : "4e89284c1966538109dc783497405bc680e9bc96",
|
||||
"version" : "2.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -114,8 +114,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-perception",
|
||||
"state" : {
|
||||
"revision" : "d924c62a70fca5f43872f286dbd7cef0957f1c01",
|
||||
"version" : "1.6.0"
|
||||
"revision" : "f4f57cac7d273cddf0161293d47adbb5a6ba3aed",
|
||||
"version" : "2.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -123,8 +123,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-sharing",
|
||||
"state" : {
|
||||
"revision" : "75e846ee3159dc75b3a29bfc24b6ce5a557ddca9",
|
||||
"version" : "2.5.2"
|
||||
"revision" : "5d87dda90ed048f216826efbad404110141161bb",
|
||||
"version" : "2.6.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -132,8 +132,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
|
||||
"state" : {
|
||||
"revision" : "37230a37e83f1b7023be08e1b1a2603fcb1567fb",
|
||||
"version" : "1.18.4"
|
||||
"revision" : "d7e40607dcd6bc26543f5d9433103f06e0b28f8f",
|
||||
"version" : "1.18.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -150,10 +150,10 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
|
||||
"state" : {
|
||||
"revision" : "39de59b2d47f7ef3ca88a039dff3084688fe27f4",
|
||||
"version" : "1.5.2"
|
||||
"revision" : "23e3442166b5122f73f9e3e622cd1e4bafeab3b7",
|
||||
"version" : "1.6.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
"version" : 3
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ let package = Package(
|
||||
.package(url: "https://github.com/pointfreeco/swift-identified-collections", from: "1.1.0"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.2.0"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-navigation", from: "2.3.0"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-perception", from: "1.3.4"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-perception", "1.3.4"..<"3.0.0"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-sharing", "0.1.2"..<"3.0.0"),
|
||||
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.3.0"),
|
||||
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.0.0"),
|
||||
|
||||
@@ -27,7 +27,7 @@ let package = Package(
|
||||
.package(url: "https://github.com/pointfreeco/swift-identified-collections", from: "1.1.0"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.2.0"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-navigation", from: "2.3.0"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-perception", from: "1.3.4"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-perception", "1.3.4"..<"3.0.0"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-sharing", "0.1.2"..<"3.0.0"),
|
||||
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.3.0"),
|
||||
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.0.0"),
|
||||
|
||||
@@ -61,9 +61,13 @@ final class RootCore<Root: Reducer>: Core {
|
||||
self.reducer = reducer
|
||||
}
|
||||
func send(_ action: Root.Action) -> Task<Void, Never>? {
|
||||
_withoutPerceptionChecking {
|
||||
#if DEBUG
|
||||
_PerceptionLocals.$skipPerceptionChecking.withValue(true) {
|
||||
_send(action)
|
||||
}
|
||||
#else
|
||||
_send(action)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
private func _send(_ action: Root.Action) -> Task<Void, Never>? {
|
||||
self.bufferedActions.append(action)
|
||||
|
||||
@@ -92,6 +92,7 @@ extension Store where State: ObservableState {
|
||||
public struct _StoreCollection<ID: Hashable & Sendable, State, Action>: RandomAccessCollection {
|
||||
private let store: Store<IdentifiedArray<ID, State>, IdentifiedAction<ID, Action>>
|
||||
private let data: IdentifiedArray<ID, State>
|
||||
private let isInPerceptionTracking = _isInPerceptionTracking
|
||||
|
||||
#if swift(<5.10)
|
||||
@MainActor(unsafe)
|
||||
@@ -120,28 +121,37 @@ public struct _StoreCollection<ID: Hashable & Sendable, State, Action>: RandomAc
|
||||
)
|
||||
return MainActor._assumeIsolated { [uncheckedSelf = UncheckedSendable(self)] in
|
||||
let `self` = uncheckedSelf.wrappedValue
|
||||
guard self.data.indices.contains(position)
|
||||
else {
|
||||
return Store()
|
||||
}
|
||||
let elementID = self.data.ids[position]
|
||||
let scopeID = self.store.id(state: \.[id: elementID], action: \.[id: elementID])
|
||||
guard let child = self.store.children[scopeID] as? Store<State, Action>
|
||||
else {
|
||||
@MainActor
|
||||
func open(
|
||||
_ core: some Core<IdentifiedArray<ID, State>, IdentifiedAction<ID, Action>>
|
||||
) -> any Core<State, Action> {
|
||||
IfLetCore(
|
||||
base: core,
|
||||
cachedState: self.data[position],
|
||||
stateKeyPath: \.[id: elementID],
|
||||
actionKeyPath: \.[id: elementID]
|
||||
)
|
||||
var child: Store<State, Action> {
|
||||
guard self.data.indices.contains(position)
|
||||
else {
|
||||
return Store()
|
||||
}
|
||||
return self.store.scope(id: scopeID, childCore: open(self.store.core))
|
||||
let elementID = self.data.ids[position]
|
||||
let scopeID = self.store.id(state: \.[id: elementID], action: \.[id: elementID])
|
||||
guard let child = self.store.children[scopeID] as? Store<State, Action>
|
||||
else {
|
||||
@MainActor
|
||||
func open(
|
||||
_ core: some Core<IdentifiedArray<ID, State>, IdentifiedAction<ID, Action>>
|
||||
) -> any Core<State, Action> {
|
||||
IfLetCore(
|
||||
base: core,
|
||||
cachedState: self.data[position],
|
||||
stateKeyPath: \.[id: elementID],
|
||||
actionKeyPath: \.[id: elementID]
|
||||
)
|
||||
}
|
||||
return self.store.scope(id: scopeID, childCore: open(self.store.core))
|
||||
}
|
||||
return child
|
||||
}
|
||||
return child
|
||||
#if DEBUG
|
||||
return _PerceptionLocals.$isInPerceptionTracking.withValue(self.isInPerceptionTracking) {
|
||||
child
|
||||
}
|
||||
#else
|
||||
return child
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@ extension Store {
|
||||
|
||||
@_spi(Internals)
|
||||
public var _isInPerceptionTracking: Bool {
|
||||
#if !os(visionOS)
|
||||
#if DEBUG && !os(visionOS)
|
||||
return _PerceptionLocals.isInPerceptionTracking
|
||||
#else
|
||||
return false
|
||||
|
||||
@@ -167,7 +167,13 @@ public final class Store<State, Action>: _Store {
|
||||
/// it conforms to ``ObservableState``.
|
||||
/// - Returns: The return value, if any, of the `body` closure.
|
||||
public func withState<R>(_ body: (_ state: State) -> R) -> R {
|
||||
_withoutPerceptionChecking { body(self.currentState) }
|
||||
#if DEBUG
|
||||
_PerceptionLocals.$skipPerceptionChecking.withValue(true) {
|
||||
body(self.currentState)
|
||||
}
|
||||
#else
|
||||
body(self.currentState)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Sends an action to the store.
|
||||
|
||||
@@ -44,18 +44,10 @@ final class StorePerceptionTests: BaseTCATestCase {
|
||||
}
|
||||
}
|
||||
#if DEBUG && !os(visionOS)
|
||||
let previous = Perception.isPerceptionCheckingEnabled
|
||||
Perception.isPerceptionCheckingEnabled = true
|
||||
defer { Perception.isPerceptionCheckingEnabled = previous }
|
||||
XCTExpectFailure {
|
||||
render(FeatureView())
|
||||
} issueMatcher: {
|
||||
$0.compactDescription == """
|
||||
failed - Perceptible state was accessed but is not being tracked. Track changes to state by \
|
||||
wrapping your view in a 'WithPerceptionTracking' view. This must also be done for any \
|
||||
escaping, trailing closures, such as 'GeometryReader', `LazyVStack` (and all lazy \
|
||||
views), navigation APIs ('sheet', 'popover', 'fullScreenCover', etc.), and others.
|
||||
"""
|
||||
$0.compactDescription.contains("Perceptible state was accessed")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user