diff --git a/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved b/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved index bc4d7f7382d2..98bcdc5b5f49 100644 --- a/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/combine-schedulers", "state" : { - "revision" : "9fa31f4403da54855f1e2aeaeff478f4f0e40b13", - "version" : "1.0.2" + "revision" : "5928286acce13def418ec36d05a001a9641086f2", + "version" : "1.0.3" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-clocks", "state" : { - "revision" : "b9b24b69e2adda099a1fa381cda1eeec272d5b53", - "version" : "1.0.5" + "revision" : "cc46202b53476d64e824e0b6612da09d84ffde8e", + "version" : "1.0.6" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-concurrency-extras", "state" : { - "revision" : "163409ef7dae9d960b87f34b51587b6609a76c1f", - "version" : "1.3.0" + "revision" : "82a4ae7170d98d8538ec77238b7eb8e7199ef2e8", + "version" : "1.3.1" } }, { @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "5526c8a27675dc7b18d6fa643abfb64bcb200b77", - "version" : "1.6.2" + "revision" : "85f89f5d0ce5a18945f65371d40ca997da85a41a", + "version" : "1.6.3" } }, { @@ -104,8 +104,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-navigation", "state" : { - "revision" : "16a27ab7ae0abfefbbcba73581b3e2380b47a579", - "version" : "2.2.2" + "revision" : "e28911721538fa0c2439e92320bad13e3200866f", + "version" : "2.2.3" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-sharing", "state" : { - "revision" : "524e1a6868f0c9eebe8002e3f604912865521beb", - "version" : "1.0.4" + "revision" : "c5ea46f0712cd3b639e2c7d6bf3f193116e0ff8d", + "version" : "2.0.2" } }, { diff --git a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved index 968137e746cf..94998dfcc7d3 100644 --- a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "be743d77a36d5e157b8f2f96e0924c48c1b3fc8eaaf625f6a20688077102dd90", + "originHash" : "7bde44da63d64357331b7d73c1c3f9d1aa732b1793a1d1fc5879f2c192bef69c", "pins" : [ { "identity" : "combine-schedulers", @@ -123,8 +123,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-sharing", "state" : { - "revision" : "dbcd9d49089c4b0d171bc28244f21f4cf9813b11", - "version" : "1.1.2" + "revision" : "c5ea46f0712cd3b639e2c7d6bf3f193116e0ff8d", + "version" : "2.0.2" } }, { diff --git a/Examples/CaseStudies/SwiftUICaseStudiesTests/01-GettingStarted-AnimationsTests.swift b/Examples/CaseStudies/SwiftUICaseStudiesTests/01-GettingStarted-AnimationsTests.swift index b5a05e09ab16..83f7e962c0c6 100644 --- a/Examples/CaseStudies/SwiftUICaseStudiesTests/01-GettingStarted-AnimationsTests.swift +++ b/Examples/CaseStudies/SwiftUICaseStudiesTests/01-GettingStarted-AnimationsTests.swift @@ -56,7 +56,7 @@ struct AnimationTests { $0.circleColor = .black } - await clock.run() + await clock.run(timeout: .seconds(7)) } @Test diff --git a/Examples/SyncUps/SyncUps/AppFeature.swift b/Examples/SyncUps/SyncUps/AppFeature.swift index b2e0558aa361..3ee5cdd034cd 100644 --- a/Examples/SyncUps/SyncUps/AppFeature.swift +++ b/Examples/SyncUps/SyncUps/AppFeature.swift @@ -54,9 +54,7 @@ struct AppView: View { var body: some View { NavigationStack(path: $store.scope(state: \.path, action: \.path)) { - SyncUpsListView( - store: store.scope(state: \.syncUpsList, action: \.syncUpsList) - ) + SyncUpsListView(store: store.scope(state: \.syncUpsList, action: \.syncUpsList)) } destination: { store in switch store.case { case let .detail(store): diff --git a/Examples/SyncUps/SyncUps/RecordMeeting.swift b/Examples/SyncUps/SyncUps/RecordMeeting.swift index 63a6adbf5e91..2f0a5e95e395 100644 --- a/Examples/SyncUps/SyncUps/RecordMeeting.swift +++ b/Examples/SyncUps/SyncUps/RecordMeeting.swift @@ -72,7 +72,7 @@ struct RecordMeeting { ? speechClient.requestAuthorization() : speechClient.authorizationStatus() - await withTaskGroup(of: Void.self) { group in + await withDiscardingTaskGroup { group in if authorization == .authorized { group.addTask { await startSpeechRecognition(send: send) diff --git a/Package.resolved b/Package.resolved index 38987b9792ad..9b3471c17b71 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "daf106e4a50354e80d4874c54dee4b63ae0e8650346403e3489835d2a6005ea8", + "originHash" : "6727aa1791df9992e75965cc70f604fddc462c64604478ad596f7f96230963a6", "pins" : [ { "identity" : "combine-schedulers", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/combine-schedulers", "state" : { - "revision" : "9fa31f4403da54855f1e2aeaeff478f4f0e40b13", - "version" : "1.0.2" + "revision" : "5928286acce13def418ec36d05a001a9641086f2", + "version" : "1.0.3" } }, { @@ -24,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-clocks", "state" : { - "revision" : "b9b24b69e2adda099a1fa381cda1eeec272d5b53", - "version" : "1.0.5" + "revision" : "cc46202b53476d64e824e0b6612da09d84ffde8e", + "version" : "1.0.6" } }, { @@ -42,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-concurrency-extras", "state" : { - "revision" : "163409ef7dae9d960b87f34b51587b6609a76c1f", - "version" : "1.3.0" + "revision" : "82a4ae7170d98d8538ec77238b7eb8e7199ef2e8", + "version" : "1.3.1" } }, { @@ -60,8 +60,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "5526c8a27675dc7b18d6fa643abfb64bcb200b77", - "version" : "1.6.2" + "revision" : "85f89f5d0ce5a18945f65371d40ca997da85a41a", + "version" : "1.6.3" } }, { @@ -105,8 +105,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-navigation", "state" : { - "revision" : "16a27ab7ae0abfefbbcba73581b3e2380b47a579", - "version" : "2.2.2" + "revision" : "e28911721538fa0c2439e92320bad13e3200866f", + "version" : "2.2.3" } }, { @@ -123,8 +123,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-sharing", "state" : { - "revision" : "524e1a6868f0c9eebe8002e3f604912865521beb", - "version" : "1.0.4" + "revision" : "c5ea46f0712cd3b639e2c7d6bf3f193116e0ff8d", + "version" : "2.0.2" } }, { diff --git a/Package.swift b/Package.swift index 6f531d62f48f..387d16f78460 100644 --- a/Package.swift +++ b/Package.swift @@ -28,7 +28,7 @@ let package = Package( .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.2.0"), .package(url: "https://github.com/pointfreeco/swift-navigation", from: "2.2.2"), .package(url: "https://github.com/pointfreeco/swift-perception", from: "1.3.4"), - .package(url: "https://github.com/pointfreeco/swift-sharing", "0.1.2"..<"2.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"), .package(url: "https://github.com/swiftlang/swift-syntax", "509.0.0"..<"601.0.0"), diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index bbccd895f980..15705d2b5210 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -28,7 +28,7 @@ let package = Package( .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.2.0"), .package(url: "https://github.com/pointfreeco/swift-navigation", from: "2.2.2"), .package(url: "https://github.com/pointfreeco/swift-perception", from: "1.3.4"), - .package(url: "https://github.com/pointfreeco/swift-sharing", "0.1.2"..<"2.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"), .package(url: "https://github.com/swiftlang/swift-syntax", "509.0.0"..<"601.0.0"), diff --git a/Sources/ComposableArchitecture/Documentation.docc/Articles/MigrationGuides.md b/Sources/ComposableArchitecture/Documentation.docc/Articles/MigrationGuides.md index 0b32ad55472d..2bb4f6e3d05e 100644 --- a/Sources/ComposableArchitecture/Documentation.docc/Articles/MigrationGuides.md +++ b/Sources/ComposableArchitecture/Documentation.docc/Articles/MigrationGuides.md @@ -14,6 +14,7 @@ APIs, and these guides contain tips to do so. ## Topics +- - - - diff --git a/Sources/ComposableArchitecture/Documentation.docc/Articles/MigrationGuides/MigratingTo1.17.1.md b/Sources/ComposableArchitecture/Documentation.docc/Articles/MigrationGuides/MigratingTo1.17.1.md new file mode 100644 index 000000000000..6b0dcb653bcf --- /dev/null +++ b/Sources/ComposableArchitecture/Documentation.docc/Articles/MigrationGuides/MigratingTo1.17.1.md @@ -0,0 +1,30 @@ +# Migrating to 1.17.1 + +The Sharing library has graduated, with backwards-incompatible changes, to 2.0, and the Composable +Architecture has been updated to extend support to this new version. + +## Overview + +The [Sharing][sharing-gh] package is a general purpose, state-sharing and persistence toolkit that +works on all platforms supported by Swift, including iOS/macOS, Linux, Windows, Wasm, and more. + +A [2.0][2.0-release] has introduced new features and functionality, and the Composable Architecture +1.17.1 includes support for this release. + +While many of Sharing 2.0's APIs are backwards-compatible with 1.0, if you have defined any of your +own custom persistence strategies via the `SharedKey` or `SharedReaderKey` protocols, you will need +to migrate them in order to support the brand new error handling and async functionality. + +If you are not ready to migrate, then you can add an explicit dependency on the library to pin to +any version less than 2.0: + +```swift +.package(url: "https://github.com/pointfreeco/swift-sharing", from: "0.1.0"), +``` + +If you are ready to upgrade to 2.0, then you can follow the [2.0 migration guide][2.0-migration] +from that package. + +[sharing-gh]: https://github.com/pointfreeco/swift-sharing +[1.0-migration]: https://swiftpackageindex.com/pointfreeco/swift-sharing/main/documentation/sharing/migratingto2.0 +[2.0-release]: https://github.com/pointfreeco/swift-sharing/releases/2.0.0 diff --git a/Sources/ComposableArchitecture/Sharing/AppStorageKeyPathKey.swift b/Sources/ComposableArchitecture/Sharing/AppStorageKeyPathKey.swift index d771ca70aebf..001435cacf2b 100644 --- a/Sources/ComposableArchitecture/Sharing/AppStorageKeyPathKey.swift +++ b/Sources/ComposableArchitecture/Sharing/AppStorageKeyPathKey.swift @@ -39,30 +39,57 @@ public struct AppStorageKeyPathKey: Sendable { @available(*, deprecated, message: "Use an 'AppStorageKey', instead") extension AppStorageKeyPathKey: SharedKey, Hashable { - public func load(initialValue _: Value?) -> Value? { - self.store.wrappedValue[keyPath: self.keyPath] - } + #if canImport(Sharing2) + public func load(context: LoadContext, continuation: LoadContinuation) { + continuation.resume(returning: self.store.wrappedValue[keyPath: self.keyPath]) + } - public func save(_ newValue: Value, immediately: Bool) { - SharedAppStorageLocals.$isSetting.withValue(true) { - self.store.wrappedValue[keyPath: self.keyPath] = newValue + public func subscribe(context: LoadContext, subscriber: SharedSubscriber) + -> SharedSubscription + { + let observer = self.store.wrappedValue.observe(self.keyPath, options: .new) { _, change in + guard + !SharedAppStorageLocals.isSetting + else { return } + subscriber.yield(with: Result { change.newValue }) + } + return SharedSubscription { + observer.invalidate() + } } - } - public func subscribe( - initialValue: Value?, - didSet receiveValue: @escaping @Sendable (_ newValue: Value?) -> Void - ) -> SharedSubscription { - let observer = self.store.wrappedValue.observe(self.keyPath, options: .new) { _, change in - guard - !SharedAppStorageLocals.isSetting - else { return } - receiveValue(change.newValue ?? initialValue) + public func save(_ value: Value, context: SaveContext, continuation: SaveContinuation) { + SharedAppStorageLocals.$isSetting.withValue(true) { + self.store.wrappedValue[keyPath: self.keyPath] = value + } + continuation.resume() } - return SharedSubscription { - observer.invalidate() + #else + public func load(initialValue _: Value?) -> Value? { + self.store.wrappedValue[keyPath: self.keyPath] } - } + + public func subscribe( + initialValue: Value?, + didSet receiveValue: @escaping @Sendable (_ newValue: Value?) -> Void + ) -> SharedSubscription { + let observer = self.store.wrappedValue.observe(self.keyPath, options: .new) { _, change in + guard + !SharedAppStorageLocals.isSetting + else { return } + receiveValue(change.newValue ?? initialValue) + } + return SharedSubscription { + observer.invalidate() + } + } + + public func save(_ newValue: Value, immediately: Bool) { + SharedAppStorageLocals.$isSetting.withValue(true) { + self.store.wrappedValue[keyPath: self.keyPath] = newValue + } + } + #endif } // NB: This is mainly used for tests, where observer notifications can bleed across cases.