From 442c7def2187ef9f9d5e843d90e061239fa56d7f Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Wed, 23 Oct 2024 16:34:13 -0700 Subject: [PATCH 1/5] Identify stores based off state identity Potential fix for #3451. --- .../xcshareddata/swiftpm/Package.resolved | 6 +++--- Package.swift | 5 ++++- Package@swift-6.0.swift | 5 ++++- .../Observation/Store+Observation.swift | 21 +++++++++++++++++-- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved index 33ea83a65a95..eb73ddbb6e14 100644 --- a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "345ca5e011bfdb9a07d4b2a72a36fac771eb8263e2cd8901042f6e807a599841", + "originHash" : "69a9ac8177a7c92b5944e4de7b7848290adceb4e10f48d9be7e95adda3bd7ff3", "pins" : [ { "identity" : "combine-schedulers", @@ -42,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-concurrency-extras", "state" : { - "revision" : "6054df64b55186f08b6d0fd87152081b8ad8d613", - "version" : "1.2.0" + "branch" : "any-hashable-sendable-flexibility", + "revision" : "fef766e0711b8946d1fe57308e56e53ac0e89569" } }, { diff --git a/Package.swift b/Package.swift index e4c6478781e4..87d2856a8925 100644 --- a/Package.swift +++ b/Package.swift @@ -21,7 +21,10 @@ let package = Package( .package(url: "https://github.com/apple/swift-collections", from: "1.1.0"), .package(url: "https://github.com/pointfreeco/combine-schedulers", from: "1.0.2"), .package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.5.4"), - .package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.2.0"), + .package( + url: "https://github.com/pointfreeco/swift-concurrency-extras", + branch: "any-hashable-sendable-flexibility" + ), .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.2"), .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.4.0"), .package(url: "https://github.com/pointfreeco/swift-identified-collections", from: "1.1.0"), diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index 466578b604b2..3e2121d36286 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -21,7 +21,10 @@ let package = Package( .package(url: "https://github.com/apple/swift-collections", from: "1.1.0"), .package(url: "https://github.com/pointfreeco/combine-schedulers", from: "1.0.2"), .package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.5.4"), - .package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.2.0"), + .package( + url: "https://github.com/pointfreeco/swift-concurrency-extras", + branch: "any-hashable-sendable-flexibility" + ), .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.2"), .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.4.0"), .package(url: "https://github.com/pointfreeco/swift-identified-collections", from: "1.1.0"), diff --git a/Sources/ComposableArchitecture/Observation/Store+Observation.swift b/Sources/ComposableArchitecture/Observation/Store+Observation.swift index 6b1ee73b2f91..921b709b7de1 100644 --- a/Sources/ComposableArchitecture/Observation/Store+Observation.swift +++ b/Sources/ComposableArchitecture/Observation/Store+Observation.swift @@ -36,7 +36,24 @@ extension Store: Hashable { } } -extension Store: Identifiable {} +extension Store: Identifiable { + public struct ID: Hashable { + fileprivate let objectIdentifier: ObjectIdentifier + fileprivate let stateIdentifier: AnyHashableSendable? + } + + public nonisolated var id: ID { + ID( + objectIdentifier: ObjectIdentifier(self), + stateIdentifier: Thread.isMainThread + ? MainActor.assumeIsolated { + ((currentState as? any Identifiable)?.id as? any Hashable) + .map(AnyHashableSendable.init) + } + : nil + ) + } +} extension Store where State: ObservableState { /// Scopes the store to optional child state and actions. @@ -395,7 +412,7 @@ extension Store where State: ObservableState { set { if newValue == nil, let childState = self.state[keyPath: state], - id == _identifiableID(childState), + id == nil || id == _identifiableID(childState), !self._isInvalidated() { self.send(action(.dismiss)) From 9554e17739f4029077dead8e73232e42eee9a7ce Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Wed, 23 Oct 2024 16:35:25 -0700 Subject: [PATCH 2/5] UIKit demo We can revert this or start maintaining these... --- .../SwiftUICaseStudies/00-RootView.swift | 4 +- ...Started-AlertsAndConfirmationDialogs.swift | 61 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/Examples/CaseStudies/SwiftUICaseStudies/00-RootView.swift b/Examples/CaseStudies/SwiftUICaseStudies/00-RootView.swift index eef92539acf8..144caa9cdd3e 100644 --- a/Examples/CaseStudies/SwiftUICaseStudies/00-RootView.swift +++ b/Examples/CaseStudies/SwiftUICaseStudies/00-RootView.swift @@ -40,7 +40,9 @@ struct RootView: View { AlertAndConfirmationDialog() } ) { store in - AlertAndConfirmationDialogView(store: store) + UIViewControllerRepresenting { + AlertAndConfirmationDialogViewController(store: store) + } } } NavigationLink("Focus State") { diff --git a/Examples/CaseStudies/SwiftUICaseStudies/01-GettingStarted-AlertsAndConfirmationDialogs.swift b/Examples/CaseStudies/SwiftUICaseStudies/01-GettingStarted-AlertsAndConfirmationDialogs.swift index 3814d141b1f3..0b2246a60a04 100644 --- a/Examples/CaseStudies/SwiftUICaseStudies/01-GettingStarted-AlertsAndConfirmationDialogs.swift +++ b/Examples/CaseStudies/SwiftUICaseStudies/01-GettingStarted-AlertsAndConfirmationDialogs.swift @@ -101,6 +101,67 @@ struct AlertAndConfirmationDialog { } } +import UIKit +final class AlertAndConfirmationDialogViewController: UIViewController { + @UIBindable var store: StoreOf + + init(store: StoreOf) { + self.store = store + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + let countLabel = UILabel() + + let alertButton = UIButton(type: .system, primaryAction: UIAction { [weak self] _ in + self?.store.send(.alertButtonTapped) + }) + alertButton.setTitle("Alert", for: .normal) + + let confirmationDialogButton = UIButton(type: .system, primaryAction: UIAction { [weak self] _ in + self?.store.send(.confirmationDialogButtonTapped) + }) + confirmationDialogButton.setTitle("Confirmation Dialog", for: .normal) + + let stack = UIStackView(arrangedSubviews: [ + countLabel, + alertButton, + confirmationDialogButton, + ]) + stack.axis = .vertical + stack.translatesAutoresizingMaskIntoConstraints = false + + view.addSubview(stack) + + NSLayoutConstraint.activate([ + stack.topAnchor.constraint(equalTo: view.topAnchor), + stack.bottomAnchor.constraint(equalTo: view.bottomAnchor), + stack.leadingAnchor.constraint(equalTo: view.leadingAnchor), + stack.trailingAnchor.constraint(equalTo: view.trailingAnchor), + ]) + + observe { [weak self] in + guard let self else { return } + countLabel.text = "Count: \(store.count)" + } + + present(item: $store.scope(state: \.alert, action: \.alert)) { store in + UIAlertController(store: store) + } + present( + item: $store.scope(state: \.confirmationDialog, action: \.confirmationDialog) + ) { store in + UIAlertController(store: store) + } + } +} + struct AlertAndConfirmationDialogView: View { @Bindable var store: StoreOf From fb63b3f7b60ca1482ce579be948210a9cdd74ef4 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Wed, 23 Oct 2024 16:52:19 -0700 Subject: [PATCH 3/5] fix --- .../xcshareddata/swiftpm/Package.resolved | 2 +- Package.resolved | 36 +++++++------------ .../Observation/Store+Observation.swift | 2 +- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved index eb73ddbb6e14..fd29319d69c3 100644 --- a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -43,7 +43,7 @@ "location" : "https://github.com/pointfreeco/swift-concurrency-extras", "state" : { "branch" : "any-hashable-sendable-flexibility", - "revision" : "fef766e0711b8946d1fe57308e56e53ac0e89569" + "revision" : "b54075c8e59df443485ca509c31ff06af3ec22dd" } }, { diff --git a/Package.resolved b/Package.resolved index 18b8e14815e2..0b7c35cdd623 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,4 @@ { - "originHash" : "e2618e836df1ca46810fbd99802b7402f1b1f9397b7b0d4d9f5ed2a60edd0a1f", "pins" : [ { "identity" : "combine-schedulers", @@ -15,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-case-paths", "state" : { - "revision" : "642e6aab8e03e5f992d9c83e38c5be98cfad5078", - "version" : "1.5.5" + "revision" : "bc92c4b27f9a84bfb498cdbfdf35d5a357e9161f", + "version" : "1.5.6" } }, { @@ -33,17 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections", "state" : { - "revision" : "9bf03ff58ce34478e66aaee630e491823326fd06", - "version" : "1.1.3" - } - }, - { - "identity" : "swift-concurrency-extras", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-concurrency-extras", - "state" : { - "revision" : "6054df64b55186f08b6d0fd87152081b8ad8d613", - "version" : "1.2.0" + "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", + "version" : "1.1.4" } }, { @@ -60,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "fd1fb25b68fdb9756cd61d23dbd9e2614b340085", - "version" : "1.4.0" + "revision" : "0fc0255e780bf742abeef29dec80924f5f0ae7b9", + "version" : "1.4.1" } }, { @@ -123,8 +113,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing", "state" : { - "revision" : "7b0bbbae90c41f848f90ac7b4df6c4f50068256d", - "version" : "1.17.5" + "revision" : "42a086182681cf661f5c47c9b7dc3931de18c6d7", + "version" : "1.17.6" } }, { @@ -132,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-syntax", "state" : { - "revision" : "515f79b522918f83483068d99c68daeb5116342d", - "version" : "600.0.0-prerelease-2024-09-04" + "revision" : "0687f71944021d616d34d922343dcef086855920", + "version" : "600.0.1" } }, { @@ -141,10 +131,10 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "bc2a151366f2cd0e347274544933bc2acb00c9fe", - "version" : "1.4.0" + "revision" : "770f990d3e4eececb57ac04a6076e22f8c97daeb", + "version" : "1.4.2" } } ], - "version" : 3 + "version" : 2 } diff --git a/Sources/ComposableArchitecture/Observation/Store+Observation.swift b/Sources/ComposableArchitecture/Observation/Store+Observation.swift index 921b709b7de1..5efebb2d759b 100644 --- a/Sources/ComposableArchitecture/Observation/Store+Observation.swift +++ b/Sources/ComposableArchitecture/Observation/Store+Observation.swift @@ -46,7 +46,7 @@ extension Store: Identifiable { ID( objectIdentifier: ObjectIdentifier(self), stateIdentifier: Thread.isMainThread - ? MainActor.assumeIsolated { + ? MainActor._assumeIsolated { ((currentState as? any Identifiable)?.id as? any Hashable) .map(AnyHashableSendable.init) } From 2289a14094ad6bcae4b4bff9a76c47847bae6e91 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Mon, 11 Nov 2024 21:26:20 -0800 Subject: [PATCH 4/5] wip --- .../xcshareddata/swiftpm/Package.resolved | 29 ++++--------------- .../xcshareddata/swiftpm/Package.resolved | 6 ++-- Package.resolved | 16 ++++++++-- Package.swift | 5 +--- Package@swift-6.0.swift | 5 +--- 5 files changed, 24 insertions(+), 37 deletions(-) diff --git a/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved b/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1b27355aecfd..e0c97b0dbda7 100644 --- a/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "a3ac4bdb0c3813f2d2206b7571a24699473e54cb56d4c1f3d55ca9677ca24e92", "pins" : [ { "identity" : "combine-schedulers", @@ -9,24 +10,6 @@ "version" : "1.0.2" } }, - { - "identity" : "swift-argument-parser", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser", - "state" : { - "revision" : "41982a3656a71c768319979febd796c6fd111d5c", - "version" : "1.5.0" - } - }, - { - "identity" : "swift-benchmark", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/swift-benchmark", - "state" : { - "revision" : "8163295f6fe82356b0bcf8e1ab991645de17d096", - "version" : "0.1.2" - } - }, { "identity" : "swift-case-paths", "kind" : "remoteSourceControl", @@ -59,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-concurrency-extras", "state" : { - "revision" : "6054df64b55186f08b6d0fd87152081b8ad8d613", - "version" : "1.2.0" + "revision" : "163409ef7dae9d960b87f34b51587b6609a76c1f", + "version" : "1.3.0" } }, { @@ -122,8 +105,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-navigation", "state" : { - "revision" : "e834b3760731160d7d448509ee6a1408c8582a6b", - "version" : "2.2.0" + "revision" : "16a27ab7ae0abfefbbcba73581b3e2380b47a579", + "version" : "2.2.2" } }, { @@ -163,5 +146,5 @@ } } ], - "version" : 2 + "version" : 3 } diff --git a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved index fd29319d69c3..2032779c62b4 100644 --- a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "69a9ac8177a7c92b5944e4de7b7848290adceb4e10f48d9be7e95adda3bd7ff3", + "originHash" : "2543a17f848dd937efce3734c88206d890e3f3b87858f4d92516e977387d1f73", "pins" : [ { "identity" : "combine-schedulers", @@ -42,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-concurrency-extras", "state" : { - "branch" : "any-hashable-sendable-flexibility", - "revision" : "b54075c8e59df443485ca509c31ff06af3ec22dd" + "revision" : "163409ef7dae9d960b87f34b51587b6609a76c1f", + "version" : "1.3.0" } }, { diff --git a/Package.resolved b/Package.resolved index 0b7c35cdd623..21a9de6f589f 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "a3ac4bdb0c3813f2d2206b7571a24699473e54cb56d4c1f3d55ca9677ca24e92", "pins" : [ { "identity" : "combine-schedulers", @@ -36,6 +37,15 @@ "version" : "1.1.4" } }, + { + "identity" : "swift-concurrency-extras", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-concurrency-extras", + "state" : { + "revision" : "163409ef7dae9d960b87f34b51587b6609a76c1f", + "version" : "1.3.0" + } + }, { "identity" : "swift-custom-dump", "kind" : "remoteSourceControl", @@ -50,8 +60,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "0fc0255e780bf742abeef29dec80924f5f0ae7b9", - "version" : "1.4.1" + "revision" : "a24a0f1037b5c65885876be89f598b6f1e70f21c", + "version" : "1.5.0" } }, { @@ -136,5 +146,5 @@ } } ], - "version" : 2 + "version" : 3 } diff --git a/Package.swift b/Package.swift index 87d2856a8925..e8c427451229 100644 --- a/Package.swift +++ b/Package.swift @@ -21,10 +21,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-collections", from: "1.1.0"), .package(url: "https://github.com/pointfreeco/combine-schedulers", from: "1.0.2"), .package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.5.4"), - .package( - url: "https://github.com/pointfreeco/swift-concurrency-extras", - branch: "any-hashable-sendable-flexibility" - ), + .package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.3.0"), .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.2"), .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.4.0"), .package(url: "https://github.com/pointfreeco/swift-identified-collections", from: "1.1.0"), diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index 3e2121d36286..8483df25a76f 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -21,10 +21,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-collections", from: "1.1.0"), .package(url: "https://github.com/pointfreeco/combine-schedulers", from: "1.0.2"), .package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.5.4"), - .package( - url: "https://github.com/pointfreeco/swift-concurrency-extras", - branch: "any-hashable-sendable-flexibility" - ), + .package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.3.0"), .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.2"), .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.4.0"), .package(url: "https://github.com/pointfreeco/swift-identified-collections", from: "1.1.0"), From 4b694bcfe3a1ef09a4ac8a3e1720c660fe892ea5 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Tue, 12 Nov 2024 10:26:20 -0800 Subject: [PATCH 5/5] wip --- .../package.xcworkspace/xcshareddata/swiftpm/Package.resolved | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved b/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved index e0c97b0dbda7..572bb9792134 100644 --- a/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,4 @@ { - "originHash" : "a3ac4bdb0c3813f2d2206b7571a24699473e54cb56d4c1f3d55ca9677ca24e92", "pins" : [ { "identity" : "combine-schedulers", @@ -146,5 +145,5 @@ } } ], - "version" : 3 + "version" : 2 }