diff --git a/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved b/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved index 98bcdc5b5f49..f5c649f2c58b 100644 --- a/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "92f2ded678a41ef5d8bc6b77a6f478ed09039d89ffc674e73012e9f30791ecb5", "pins" : [ { "identity" : "combine-schedulers", @@ -14,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-case-paths", "state" : { - "revision" : "bc92c4b27f9a84bfb498cdbfdf35d5a357e9161f", - "version" : "1.5.6" + "revision" : "9810c8d6c2914de251e072312f01d3bf80071852", + "version" : "1.7.1" } }, { @@ -32,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections", "state" : { - "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", - "version" : "1.1.4" + "revision" : "c1805596154bb3a265fd91b8ac0c4433b4348fb0", + "version" : "1.2.0" } }, { @@ -59,8 +60,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "85f89f5d0ce5a18945f65371d40ca997da85a41a", - "version" : "1.6.3" + "revision" : "4c90d6b2b9bf0911af87b103bb40f41771891596", + "version" : "1.9.2" } }, { @@ -68,8 +69,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-docc-plugin", "state" : { - "revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64", - "version" : "1.4.3" + "revision" : "d1691545d53581400b1de9b0472d45eb25c19fed", + "version" : "1.4.4" } }, { @@ -86,8 +87,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-identified-collections", "state" : { - "revision" : "2f5ab6e091dd032b63dacbda052405756010dc3b", - "version" : "1.1.0" + "revision" : "322d9ffeeba85c9f7c4984b39422ec7cc3c56597", + "version" : "1.1.1" } }, { @@ -95,8 +96,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-macro-testing", "state" : { - "revision" : "20c1a8f3b624fb5d1503eadcaa84743050c350f4", - "version" : "0.5.2" + "revision" : "2de00af725ff4c43c9a90d7893835de312653169", + "version" : "0.6.3" } }, { @@ -104,8 +105,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-navigation", "state" : { - "revision" : "e28911721538fa0c2439e92320bad13e3200866f", - "version" : "2.2.3" + "revision" : "ae208d1a5cf33aee1d43734ea780a09ada6e2a21", + "version" : "2.3.1" } }, { @@ -113,8 +114,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-perception", "state" : { - "revision" : "8d52279b9809ef27eabe7d5420f03734528f19da", - "version" : "1.4.1" + "revision" : "d924c62a70fca5f43872f286dbd7cef0957f1c01", + "version" : "1.6.0" } }, { @@ -122,8 +123,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-sharing", "state" : { - "revision" : "c5ea46f0712cd3b639e2c7d6bf3f193116e0ff8d", - "version" : "2.0.2" + "revision" : "75e846ee3159dc75b3a29bfc24b6ce5a557ddca9", + "version" : "2.5.2" } }, { @@ -131,8 +132,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing", "state" : { - "revision" : "42a086182681cf661f5c47c9b7dc3931de18c6d7", - "version" : "1.17.6" + "revision" : "37230a37e83f1b7023be08e1b1a2603fcb1567fb", + "version" : "1.18.4" } }, { @@ -140,8 +141,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-syntax", "state" : { - "revision" : "0687f71944021d616d34d922343dcef086855920", - "version" : "600.0.1" + "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", + "version" : "601.0.1" } }, { @@ -149,8 +150,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "a3f634d1a409c7979cabc0a71b3f26ffa9fc8af1", - "version" : "1.4.3" + "revision" : "39de59b2d47f7ef3ca88a039dff3084688fe27f4", + "version" : "1.5.2" } } ], diff --git a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved index 776ea708829d..59644b749e41 100644 --- a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "3ebb70e4ef5203d6f8bebecc1bec69837803b880f6f397952a6f49aa1f6fe107", + "originHash" : "0479414dc97c4704849540dc64b58b99ce1f1c648e4cb9269d822b21cbc51b7f", "pins" : [ { "identity" : "combine-schedulers", @@ -15,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-case-paths", "state" : { - "revision" : "19b7263bacb9751f151ec0c93ec816fe1ef67c7b", - "version" : "1.6.1" + "revision" : "9810c8d6c2914de251e072312f01d3bf80071852", + "version" : "1.7.1" } }, { @@ -33,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections", "state" : { - "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", - "version" : "1.1.4" + "revision" : "c1805596154bb3a265fd91b8ac0c4433b4348fb0", + "version" : "1.2.0" } }, { @@ -60,8 +60,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "121a428c505c01c4ce02d5ada1c8fc3da93afce9", - "version" : "1.8.0" + "revision" : "4c90d6b2b9bf0911af87b103bb40f41771891596", + "version" : "1.9.2" } }, { @@ -69,8 +69,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-docc-plugin", "state" : { - "revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64", - "version" : "1.4.3" + "revision" : "d1691545d53581400b1de9b0472d45eb25c19fed", + "version" : "1.4.4" } }, { @@ -96,8 +96,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-macro-testing", "state" : { - "revision" : "0b80a098d4805a21c412b65f01ffde7b01aab2fa", - "version" : "0.6.0" + "revision" : "2de00af725ff4c43c9a90d7893835de312653169", + "version" : "0.6.3" } }, { @@ -105,8 +105,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-navigation", "state" : { - "revision" : "db6bc9dbfed001f21e6728fd36413d9342c235b4", - "version" : "2.3.0" + "revision" : "ae208d1a5cf33aee1d43734ea780a09ada6e2a21", + "version" : "2.3.1" } }, { @@ -114,8 +114,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-perception", "state" : { - "revision" : "21811d6230a625fa0f2e6ffa85be857075cc02c4", - "version" : "1.5.0" + "revision" : "d924c62a70fca5f43872f286dbd7cef0957f1c01", + "version" : "1.6.0" } }, { @@ -123,8 +123,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-sharing", "state" : { - "revision" : "2c840cf2ae0526ad6090e7796c4e13d9a2339f4a", - "version" : "2.3.3" + "revision" : "75e846ee3159dc75b3a29bfc24b6ce5a557ddca9", + "version" : "2.5.2" } }, { @@ -132,8 +132,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing.git", "state" : { - "revision" : "b2d4cb30735f4fbc3a01963a9c658336dd21e9ba", - "version" : "1.18.1" + "revision" : "37230a37e83f1b7023be08e1b1a2603fcb1567fb", + "version" : "1.18.4" } }, { @@ -141,8 +141,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-syntax", "state" : { - "revision" : "0687f71944021d616d34d922343dcef086855920", - "version" : "600.0.1" + "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", + "version" : "601.0.1" } }, { @@ -164,5 +164,5 @@ } } ], - "version" : 3 + "version" : 2 } diff --git a/Package.resolved b/Package.resolved index 697d34d6a929..f5c649f2c58b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -15,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-case-paths", "state" : { - "revision" : "41b89b8b68d8c56c622dbb7132258f1a3e638b25", - "version" : "1.7.0" + "revision" : "9810c8d6c2914de251e072312f01d3bf80071852", + "version" : "1.7.1" } }, { @@ -33,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections", "state" : { - "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", - "version" : "1.1.4" + "revision" : "c1805596154bb3a265fd91b8ac0c4433b4348fb0", + "version" : "1.2.0" } }, { @@ -69,8 +69,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-docc-plugin", "state" : { - "revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64", - "version" : "1.4.3" + "revision" : "d1691545d53581400b1de9b0472d45eb25c19fed", + "version" : "1.4.4" } }, { @@ -87,8 +87,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-identified-collections", "state" : { - "revision" : "2f5ab6e091dd032b63dacbda052405756010dc3b", - "version" : "1.1.0" + "revision" : "322d9ffeeba85c9f7c4984b39422ec7cc3c56597", + "version" : "1.1.1" } }, { @@ -105,8 +105,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-navigation", "state" : { - "revision" : "db6bc9dbfed001f21e6728fd36413d9342c235b4", - "version" : "2.3.0" + "revision" : "ae208d1a5cf33aee1d43734ea780a09ada6e2a21", + "version" : "2.3.1" } }, { @@ -123,8 +123,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-sharing", "state" : { - "revision" : "c5ea46f0712cd3b639e2c7d6bf3f193116e0ff8d", - "version" : "2.0.2" + "revision" : "75e846ee3159dc75b3a29bfc24b6ce5a557ddca9", + "version" : "2.5.2" } }, { @@ -150,10 +150,10 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "a3f634d1a409c7979cabc0a71b3f26ffa9fc8af1", - "version" : "1.4.3" + "revision" : "39de59b2d47f7ef3ca88a039dff3084688fe27f4", + "version" : "1.5.2" } } ], - "version" : 3 + "version" : 2 } diff --git a/Sources/ComposableArchitecture/Store.swift b/Sources/ComposableArchitecture/Store.swift index a4c169091695..c5f5917efd23 100644 --- a/Sources/ComposableArchitecture/Store.swift +++ b/Sources/ComposableArchitecture/Store.swift @@ -83,55 +83,6 @@ import SwiftUI /// } /// ``` /// -/// ### Thread safety -/// -/// The `Store` class is not thread-safe, and so all interactions with an instance of ``Store`` -/// (including all of its child stores) must be done on the same thread the store was created on. -/// Further, if the store is powering a SwiftUI or UIKit view, as is customary, then all -/// interactions must be done on the _main_ thread. -/// -/// The reason stores are not thread-safe is due to the fact that when an action is sent to a store, -/// a reducer is run on the current state, and this process cannot be done from multiple threads. -/// It is possible to make this process thread-safe by introducing locks or queues, but this -/// introduces new complications: -/// -/// * If done simply with `DispatchQueue.main.async` you will incur a thread hop even when you are -/// already on the main thread. This can lead to unexpected behavior in UIKit and SwiftUI, where -/// sometimes you are required to do work synchronously, such as in animation blocks. -/// -/// * It is possible to create a scheduler that performs its work immediately when on the main -/// thread and otherwise uses `DispatchQueue.main.async` (_e.g._, see Combine Schedulers' -/// [UIScheduler][uischeduler]). -/// -/// This introduces a lot more complexity, and should probably not be adopted without having a very -/// good reason. -/// -/// This is why we require all actions be sent from the same thread. This requirement is in the same -/// spirit of how `URLSession` and other Apple APIs are designed. Those APIs tend to deliver their -/// outputs on whatever thread is most convenient for them, and then it is your responsibility to -/// dispatch back to the main queue if that's what you need. The Composable Architecture makes you -/// responsible for making sure to send actions on the main thread. If you are using an effect that -/// may deliver its output on a non-main thread, you must explicitly perform `.receive(on:)` in -/// order to force it back on the main thread. -/// -/// This approach makes the fewest number of assumptions about how effects are created and -/// transformed, and prevents unnecessary thread hops and re-dispatching. It also provides some -/// testing benefits. If your effects are not responsible for their own scheduling, then in tests -/// all of the effects would run synchronously and immediately. You would not be able to test how -/// multiple in-flight effects interleave with each other and affect the state of your application. -/// However, by leaving scheduling out of the ``Store`` we get to test these aspects of our effects -/// if we so desire, or we can ignore if we prefer. We have that flexibility. -/// -/// [uischeduler]: https://github.com/pointfreeco/combine-schedulers/blob/main/Sources/CombineSchedulers/UIScheduler.swift -/// -/// #### Thread safety checks -/// -/// The store performs some basic thread safety checks in order to help catch mistakes. Stores -/// constructed via the initializer ``init(initialState:reducer:withDependencies:)`` are assumed -/// to run only on the main thread, and so a check is executed immediately to make sure that is the -/// case. Further, all actions sent to the store and all scopes (see ``scope(state:action:)-90255``) -/// of the store are also checked to make sure that work is performed on the main thread. -/// /// ### ObservableObject conformance /// /// The store conforms to `ObservableObject` but is _not_ observable via the `@ObservedObject` @@ -222,12 +173,6 @@ public final class Store { /// .task { await store.send(.task).finish() } /// ``` /// - /// > Important: The ``Store`` is not thread safe and you should only send actions to it from the - /// > main thread. If you want to send actions on background threads due to the fact that the - /// > reducer is performing computationally expensive work, then a better way to handle this is to - /// > wrap that work in an ``Effect`` that is performed on a background thread so that the - /// > result can be fed back into the store. - /// /// - Parameter action: An action. /// - Returns: A ``StoreTask`` that represents the lifecycle of the effect executed when /// sending the action. diff --git a/Sources/ComposableArchitecture/ViewStore.swift b/Sources/ComposableArchitecture/ViewStore.swift index 6fbb362698df..3ff0e373bc7c 100644 --- a/Sources/ComposableArchitecture/ViewStore.swift +++ b/Sources/ComposableArchitecture/ViewStore.swift @@ -56,11 +56,6 @@ import SwiftUI /// self.viewStore.send(.incrementButtonTapped) /// } /// ``` -/// -/// > Important: The `ViewStore` class is not thread-safe, and all interactions with it (and the -/// > store it was derived from) must happen on the same thread. Further, for SwiftUI applications, -/// > all interactions must happen on the _main_ thread. See the documentation of the ``Store`` -/// > class for more information as to why this decision was made. @available( iOS, deprecated: 9999,