diff --git a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved index 9f8e454e675b..e8da5c1dd9a8 100644 --- a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "9e30691675e494f8d62a6fee1540bdd1399c73ceefda0cfe638103df9a271e71", + "originHash" : "658be5678358d678b69ea40e4be4814633be8197318d5ac54b97fb40cfb2152b", "pins" : [ { "identity" : "combine-schedulers", @@ -15,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-case-paths", "state" : { - "revision" : "9810c8d6c2914de251e072312f01d3bf80071852", - "version" : "1.7.1" + "revision" : "6989976265be3f8d2b5802c722f9ba168e227c71", + "version" : "1.7.2" } }, { @@ -33,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections", "state" : { - "revision" : "8c0c0a8b49e080e54e5e328cc552821ff07cd341", - "version" : "1.2.1" + "revision" : "7b847a3b7008b2dc2f47ca3110d8c782fb2e5c7e", + "version" : "1.3.0" } }, { @@ -42,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-concurrency-extras", "state" : { - "revision" : "82a4ae7170d98d8538ec77238b7eb8e7199ef2e8", - "version" : "1.3.1" + "revision" : "5a3825302b1a0d744183200915a47b508c828e6f", + "version" : "1.3.2" } }, { @@ -60,8 +60,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "eefcdaa88d2e2fd82e3405dfb6eb45872011a0b5", - "version" : "1.9.3" + "revision" : "a10f9feeb214bc72b5337b6ef6d5a029360db4cc", + "version" : "1.10.0" } }, { @@ -96,8 +96,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-macro-testing", "state" : { - "revision" : "2de00af725ff4c43c9a90d7893835de312653169", - "version" : "0.6.3" + "revision" : "9ab11325daa51c7c5c10fcf16c92bac906717c7e", + "version" : "0.6.4" } }, { @@ -105,8 +105,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-navigation", "state" : { - "revision" : "4e89284c1966538109dc783497405bc680e9bc96", - "version" : "2.4.0" + "revision" : "91415670c91d41e8e1872ef6fe1bf118e20dee37", + "version" : "2.5.1" } }, { @@ -114,8 +114,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-perception", "state" : { - "revision" : "328a0b49e2690135c4c2660661f0ed83f16853e3", - "version" : "2.0.4" + "revision" : "4f47ebafed5f0b0172cf5c661454fa8e28fb2ac4", + "version" : "2.0.9" } }, { @@ -123,8 +123,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-sharing", "state" : { - "revision" : "5d87dda90ed048f216826efbad404110141161bb", - "version" : "2.6.0" + "revision" : "3bfc408cc2d0bee2287c174da6b1c76768377818", + "version" : "2.7.4" } }, { @@ -132,8 +132,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing.git", "state" : { - "revision" : "d7e40607dcd6bc26543f5d9433103f06e0b28f8f", - "version" : "1.18.6" + "revision" : "a8b7c5e0ed33d8ab8887d1654d9b59f2cbad529b", + "version" : "1.18.7" } }, { @@ -159,8 +159,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "23e3442166b5122f73f9e3e622cd1e4bafeab3b7", - "version" : "1.6.0" + "revision" : "4c27acf5394b645b70d8ba19dc249c0472d5f618", + "version" : "1.7.0" } } ], diff --git a/Sources/ComposableArchitecture/Store.swift b/Sources/ComposableArchitecture/Store.swift index fbc345890180..183d9d716b6d 100644 --- a/Sources/ComposableArchitecture/Store.swift +++ b/Sources/ComposableArchitecture/Store.swift @@ -436,7 +436,11 @@ public struct StorePublisher: Publisher { self.upstream.subscribe( AnySubscriber( receiveSubscription: subscriber.receive(subscription:), - receiveValue: subscriber.receive(_:), + receiveValue: { value in + _PerceptionLocals.$skipPerceptionChecking.withValue(true) { + subscriber.receive(value) + } + }, receiveCompletion: { [store = self.store] in subscriber.receive(completion: $0) _ = store diff --git a/Tests/ComposableArchitectureTests/StorePerceptionTests.swift b/Tests/ComposableArchitectureTests/StorePerceptionTests.swift index b99bb3df0320..3029841f3b03 100644 --- a/Tests/ComposableArchitectureTests/StorePerceptionTests.swift +++ b/Tests/ComposableArchitectureTests/StorePerceptionTests.swift @@ -68,6 +68,27 @@ final class StorePerceptionTests: BaseTCATestCase { render(FeatureView()) } + @MainActor + func testPerceptionCheck_ViewRepresentable_publisher() { + #if canImport(UIKit) + struct ViewRepresentable: UIViewRepresentable { + let store = Store(initialState: Feature.State()) { + Feature() + } + func makeUIView(context: Context) -> UILabel { + let label = UILabel() + let cancellable = store.publisher.sink { [weak label] state in + label?.text = "\(state.count)" + } + objc_setAssociatedObject(label, cancellableKey, cancellable, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + return label + } + func updateUIView(_ view: UILabel, context: Context) {} + } + render(ViewRepresentable()) + #endif + } + @MainActor private func render(_ view: some View) { let image = ImageRenderer(content: view).cgImage @@ -75,6 +96,8 @@ final class StorePerceptionTests: BaseTCATestCase { } } +@MainActor private let cancellableKey = malloc(1)! + @Reducer private struct Feature { @ObservableState