From d4a699e4b224bb636c71394f37a83e157212be0a Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Wed, 12 Jun 2024 10:01:53 -0700 Subject: [PATCH 1/2] Support swift-syntax from 600.0.0-latest The Xcode 16 beta generates macro projects using these swift-syntax snapshots. Luckily things seem to be backwards compatible, so we can expand our supported range. --- .../xcshareddata/swiftpm/Package.resolved | 40 +++++++++---------- Package@swift-5.9.swift | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved index 216a84e12b3e..c78447aba637 100644 --- a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser", "state" : { - "revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41", - "version" : "1.3.0" + "revision" : "0fbc8848e389af3bb55c182bc19ca9d5dc2f255b", + "version" : "1.4.0" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-case-paths", "state" : { - "revision" : "e593aba2c6222daad7c4f2732a431eed2c09bb07", - "version" : "1.3.0" + "revision" : "ecb5b29e65bfc58b4c90216d4ca6ef3a573a28a7", + "version" : "1.4.1" } }, { @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections", "state" : { - "revision" : "94cf62b3ba8d4bed62680a282d4c25f9c63c2efb", - "version" : "1.1.0" + "revision" : "ee97538f5b81ae89698fd95938896dec5217b148", + "version" : "1.1.1" } }, { @@ -77,8 +77,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "d3a5af3038a09add4d7682f66555d6212058a3c0", - "version" : "1.2.2" + "revision" : "350e1e119babe8525f9bd155b76640a5de270184", + "version" : "1.3.0" } }, { @@ -113,8 +113,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-macro-testing", "state" : { - "revision" : "90e38eec4bf661ec0da1bbfd3ec507d0f0c05310", - "version" : "0.3.0" + "revision" : "5c4a1b9d7c23cd5c08ea50677d8e89080365cb00", + "version" : "0.4.0" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-perception", "state" : { - "revision" : "9b77fbd07b9529312f7e9adb10f5131acd9e2363", - "version" : "1.2.0" + "revision" : "64f7f6c28c6a4d3c4b9da2ba02383e29ab48a8cf", + "version" : "1.2.2" } }, { @@ -131,8 +131,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing.git", "state" : { - "revision" : "5b0c434778f2c1a4c9b5ebdb8682b28e84dd69bd", - "version" : "1.15.4" + "revision" : "625ccca8570773dd84a34ee51a81aa2bc5a4f97a", + "version" : "1.16.0" } }, { @@ -140,8 +140,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-syntax", "state" : { - "revision" : "08a2f0a9a30e0f705f79c9cfaca1f68b71bdc775", - "version" : "510.0.0" + "revision" : "303e5c5c36d6a558407d364878df131c3546fad8", + "version" : "510.0.2" } }, { @@ -158,8 +158,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swiftui-navigation.git", "state" : { - "revision" : "d9e72f3083c08375794afa216fb2f89c0114f303", - "version" : "1.2.1" + "revision" : "7ab04c6e2e6a73d34d5a762970ef88bf0aedb084", + "version" : "1.4.0" } }, { @@ -167,8 +167,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "b13b1d1a8e787a5ffc71ac19dcaf52183ab27ba2", - "version" : "1.1.1" + "revision" : "6f30bdba373bbd7fbfe241dddd732651f2fbd1e2", + "version" : "1.1.2" } } ], diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift index 97b54df1e0d3..4b499a7b172a 100644 --- a/Package@swift-5.9.swift +++ b/Package@swift-5.9.swift @@ -20,7 +20,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/apple/swift-collections", from: "1.1.0"), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), - .package(url: "https://github.com/apple/swift-syntax", "509.0.0"..<"511.0.0"), + .package(url: "https://github.com/apple/swift-syntax", "509.0.0"..<"601.0.0"), .package(url: "https://github.com/google/swift-benchmark", from: "0.1.0"), .package(url: "https://github.com/pointfreeco/combine-schedulers", from: "1.0.0"), .package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.3.0"), From 103a22fb3b2fe9c127a37ef9b2afefc2656cb2ff Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Wed, 12 Jun 2024 15:14:12 -0700 Subject: [PATCH 2/2] wip --- .../xcshareddata/swiftpm/Package.resolved | 20 +++++------ Examples/SyncUps/SyncUps/RecordMeeting.swift | 6 ++-- .../SyncUpsTests/AppFeatureTests.swift | 34 +++++++++++-------- .../SyncUpsTests/SyncUpDetailTests.swift | 2 +- .../SyncUpsTests/SyncUpsListTests.swift | 2 +- .../Internal/NavigationID.swift | 8 ++--- .../Internal/PresentationID.swift | 6 ++-- .../Observation/ViewAction.swift | 6 +++- .../Extensions.swift | 12 +++++++ .../ObservableStateMacro.swift | 5 ++- .../ReducerMacro.swift | 9 +++-- .../ViewActionMacro.swift | 5 ++- 12 files changed, 72 insertions(+), 43 deletions(-) diff --git a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved index c78447aba637..0afdbc9b88db 100644 --- a/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-case-paths", "state" : { - "revision" : "ecb5b29e65bfc58b4c90216d4ca6ef3a573a28a7", - "version" : "1.4.1" + "revision" : "b871e5ed11a23e52c2896a92ce2c829982ff8619", + "version" : "1.4.2" } }, { @@ -77,8 +77,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "350e1e119babe8525f9bd155b76640a5de270184", - "version" : "1.3.0" + "revision" : "00bc30ca03f98881329fab7f1bebef8eba472596", + "version" : "1.3.1" } }, { @@ -113,8 +113,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-macro-testing", "state" : { - "revision" : "5c4a1b9d7c23cd5c08ea50677d8e89080365cb00", - "version" : "0.4.0" + "revision" : "851c8b6bde2000d8051dc9aca1efee04dcc37411", + "version" : "0.4.1" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-perception", "state" : { - "revision" : "64f7f6c28c6a4d3c4b9da2ba02383e29ab48a8cf", - "version" : "1.2.2" + "revision" : "d8340521e532cffdf75a64468ff9362de8bd2bb9", + "version" : "1.2.3" } }, { @@ -131,8 +131,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing.git", "state" : { - "revision" : "625ccca8570773dd84a34ee51a81aa2bc5a4f97a", - "version" : "1.16.0" + "revision" : "8ddd519780452729c6634ad6bd0d2595938e9ea3", + "version" : "1.16.1" } }, { diff --git a/Examples/SyncUps/SyncUps/RecordMeeting.swift b/Examples/SyncUps/SyncUps/RecordMeeting.swift index c2765744ef4d..7d1bd029d97e 100644 --- a/Examples/SyncUps/SyncUps/RecordMeeting.swift +++ b/Examples/SyncUps/SyncUps/RecordMeeting.swift @@ -346,13 +346,13 @@ struct SpeakerArc: Shape { } } - private var degreesPerSpeaker: Double { + nonisolated private var degreesPerSpeaker: Double { 360 / Double(totalSpeakers) } - private var startAngle: Angle { + nonisolated private var startAngle: Angle { Angle(degrees: degreesPerSpeaker * Double(speakerIndex) + 1) } - private var endAngle: Angle { + nonisolated private var endAngle: Angle { Angle(degrees: startAngle.degrees + degreesPerSpeaker - 1) } } diff --git a/Examples/SyncUps/SyncUpsTests/AppFeatureTests.swift b/Examples/SyncUps/SyncUpsTests/AppFeatureTests.swift index 4e96faeb464c..33a267dc49b3 100644 --- a/Examples/SyncUps/SyncUpsTests/AppFeatureTests.swift +++ b/Examples/SyncUps/SyncUpsTests/AppFeatureTests.swift @@ -19,19 +19,21 @@ final class AppFeatureTests: XCTestCase { } await store.send(\.path[id:0].detail.editButtonTapped) { - $0.path[id: 0]?.detail?.destination = .edit( - SyncUpForm.State(syncUp: syncUp) - ) + $0.path[id: 0]?.modify(\.detail) { $0.destination = .edit(SyncUpForm.State(syncUp: syncUp)) } } syncUp.title = "Blob" await store.send(\.path[id:0].detail.destination.edit.binding.syncUp, syncUp) { - $0.path[id: 0]?.detail?.destination?.edit?.syncUp.title = "Blob" + $0.path[id: 0]?.modify(\.detail) { + $0.destination?.modify(\.edit) { $0.syncUp.title = "Blob" } + } } await store.send(\.path[id:0].detail.doneEditingButtonTapped) { - $0.path[id: 0]?.detail?.destination = nil - $0.path[id: 0]?.detail?.syncUp.title = "Blob" + $0.path[id: 0]?.modify(\.detail) { + $0.destination = nil + $0.syncUp.title = "Blob" + } } .finish() } @@ -51,11 +53,11 @@ final class AppFeatureTests: XCTestCase { } await store.send(\.path[id:0].detail.deleteButtonTapped) { - $0.path[id: 0]?.detail?.destination = .alert(.deleteSyncUp) + $0.path[id: 0]?.modify(\.detail) { $0.destination = .alert(.deleteSyncUp) } } await store.send(\.path[id:0].detail.destination.alert.confirmDeletion) { - $0.path[id: 0, case: \.detail]?.destination = nil + $0.path[id: 0]?.modify(\.detail) { $0.destination = nil } $0.syncUpsList.syncUps = [] } @@ -109,13 +111,15 @@ final class AppFeatureTests: XCTestCase { XCTAssertEqual($0.path.count, 1) } store.assert { - $0.path[id: 0]?.detail?.syncUp.meetings = [ - Meeting( - id: Meeting.ID(UUID(0)), - date: Date(timeIntervalSince1970: 1_234_567_890), - transcript: "I completed the project" - ) - ] + $0.path[id: 0]?.modify(\.detail) { + $0.syncUp.meetings = [ + Meeting( + id: Meeting.ID(UUID(0)), + date: Date(timeIntervalSince1970: 1_234_567_890), + transcript: "I completed the project" + ) + ] + } } } } diff --git a/Examples/SyncUps/SyncUpsTests/SyncUpDetailTests.swift b/Examples/SyncUps/SyncUpsTests/SyncUpDetailTests.swift index f6a3cc2ab646..999f32990b69 100644 --- a/Examples/SyncUps/SyncUpsTests/SyncUpDetailTests.swift +++ b/Examples/SyncUps/SyncUpsTests/SyncUpDetailTests.swift @@ -102,7 +102,7 @@ final class SyncUpDetailTests: XCTestCase { syncUp.title = "Blob's Meeting" await store.send(\.destination.edit.binding.syncUp, syncUp) { - $0.destination?.edit?.syncUp.title = "Blob's Meeting" + $0.destination?.modify(\.edit) { $0.syncUp.title = "Blob's Meeting" } } await store.send(.doneEditingButtonTapped) { diff --git a/Examples/SyncUps/SyncUpsTests/SyncUpsListTests.swift b/Examples/SyncUps/SyncUpsTests/SyncUpsListTests.swift index 86209389ab70..47d0634669b1 100644 --- a/Examples/SyncUps/SyncUpsTests/SyncUpsListTests.swift +++ b/Examples/SyncUps/SyncUpsTests/SyncUpsListTests.swift @@ -24,7 +24,7 @@ final class SyncUpsListTests: XCTestCase { syncUp.title = "Engineering" await store.send(\.destination.add.binding.syncUp, syncUp) { - $0.destination?.add?.syncUp.title = "Engineering" + $0.destination?.modify(\.add) { $0.syncUp.title = "Engineering" } } await store.send(.confirmAddSyncUpButtonTapped) { diff --git a/Sources/ComposableArchitecture/Internal/NavigationID.swift b/Sources/ComposableArchitecture/Internal/NavigationID.swift index d0e46c302433..a32c4573d991 100644 --- a/Sources/ComposableArchitecture/Internal/NavigationID.swift +++ b/Sources/ComposableArchitecture/Internal/NavigationID.swift @@ -35,10 +35,10 @@ struct NavigationIDPath: Hashable, Sendable { struct NavigationID: Hashable, @unchecked Sendable { private let kind: Kind - private let identifier: AnyHashableSendable? + private let identifier: AnyHashable? private let tag: UInt32? - enum Kind: Hashable, @unchecked Sendable { + enum Kind: Hashable { case casePath(root: Any.Type, value: Any.Type) case keyPath(AnyKeyPath) @@ -73,7 +73,7 @@ struct NavigationID: Hashable, @unchecked Sendable { self.kind = .keyPath(keyPath) self.tag = EnumMetadata(Value.self)?.tag(of: base) if let id = _identifiableID(base) ?? EnumMetadata.project(base).flatMap(_identifiableID) { - self.identifier = AnyHashableSendable(id) + self.identifier = id } else { self.identifier = nil } @@ -105,7 +105,7 @@ struct NavigationID: Hashable, @unchecked Sendable { self.kind = .casePath(root: Root.self, value: Value.self) self.tag = EnumMetadata(Root.self)?.tag(of: root) if let id = _identifiableID(root) ?? _identifiableID(value) { - self.identifier = AnyHashableSendable(id) + self.identifier = id } else { self.identifier = nil } diff --git a/Sources/ComposableArchitecture/Internal/PresentationID.swift b/Sources/ComposableArchitecture/Internal/PresentationID.swift index bbfef94902c3..90f0099167d2 100644 --- a/Sources/ComposableArchitecture/Internal/PresentationID.swift +++ b/Sources/ComposableArchitecture/Internal/PresentationID.swift @@ -6,15 +6,15 @@ extension PresentationState { } } -struct PresentationID: Hashable, Identifiable, Sendable { - private let identifier: AnyHashableSendable? +struct PresentationID: Hashable, Identifiable { + private let identifier: AnyHashable? private let tag: UInt32? private let type: Any.Type init(base: Base) { self.tag = EnumMetadata(Base.self)?.tag(of: base) if let id = _identifiableID(base) ?? EnumMetadata.project(base).flatMap(_identifiableID) { - self.identifier = AnyHashableSendable(id) + self.identifier = id } else { self.identifier = nil } diff --git a/Sources/ComposableArchitecture/Observation/ViewAction.swift b/Sources/ComposableArchitecture/Observation/ViewAction.swift index 69404ef5dd64..eb2c24f50a1a 100644 --- a/Sources/ComposableArchitecture/Observation/ViewAction.swift +++ b/Sources/ComposableArchitecture/Observation/ViewAction.swift @@ -12,7 +12,11 @@ public protocol ViewAction { public protocol ViewActionSending { associatedtype StoreState associatedtype StoreAction: ViewAction - @MainActor(unsafe) var store: Store { get } + #if swift(>=5.10) + @MainActor @preconcurrency var store: Store { get } + #else + @MainActor(unsafe) var store: Store { get } + #endif } extension ViewActionSending { diff --git a/Sources/ComposableArchitectureMacros/Extensions.swift b/Sources/ComposableArchitectureMacros/Extensions.swift index e2247d5b3a5e..8bc7b22f4e7c 100644 --- a/Sources/ComposableArchitectureMacros/Extensions.swift +++ b/Sources/ComposableArchitectureMacros/Extensions.swift @@ -284,3 +284,15 @@ extension DeclGroupSyntax { return self.is(StructDeclSyntax.self) } } + +extension AttributedTypeSyntax { + var isInout: Bool { + #if canImport(SwiftSyntax600) + self.specifiers.contains( + where: { $0.as(SimpleTypeSpecifierSyntax.self)?.specifier.tokenKind == .keyword(.inout) } + ) == true + #else + self.specifier?.tokenKind == .keyword(.inout) + #endif + } +} diff --git a/Sources/ComposableArchitectureMacros/ObservableStateMacro.swift b/Sources/ComposableArchitectureMacros/ObservableStateMacro.swift index 90a6c4b0d5d1..0fbebb5df8de 100644 --- a/Sources/ComposableArchitectureMacros/ObservableStateMacro.swift +++ b/Sources/ComposableArchitectureMacros/ObservableStateMacro.swift @@ -13,9 +13,12 @@ import SwiftDiagnostics import SwiftOperators import SwiftSyntax import SwiftSyntaxBuilder -import SwiftSyntaxMacroExpansion import SwiftSyntaxMacros +#if !canImport(SwiftSyntax600) + import SwiftSyntaxMacroExpansion +#endif + public struct ObservableStateMacro { static let moduleName = "ComposableArchitecture" diff --git a/Sources/ComposableArchitectureMacros/ReducerMacro.swift b/Sources/ComposableArchitectureMacros/ReducerMacro.swift index 92f066e46ed3..4165a30d74d5 100644 --- a/Sources/ComposableArchitectureMacros/ReducerMacro.swift +++ b/Sources/ComposableArchitectureMacros/ReducerMacro.swift @@ -2,9 +2,12 @@ import SwiftDiagnostics import SwiftOperators import SwiftSyntax import SwiftSyntaxBuilder -import SwiftSyntaxMacroExpansion import SwiftSyntaxMacros +#if !canImport(SwiftSyntax600) + import SwiftSyntaxMacroExpansion +#endif + public enum ReducerMacro { } @@ -93,7 +96,7 @@ extension ReducerMacro: MemberAttributeMacro { method.signature.parameterClause.parameters.count == 2, let state = method.signature.parameterClause.parameters.first, state.firstName.text == "into", - state.type.as(AttributedTypeSyntax.self)?.specifier?.text == "inout", + state.type.as(AttributedTypeSyntax.self)?.isInout == true, method.signature.parameterClause.parameters.last?.firstName.text == "action", method.signature.effectSpecifiers == nil, method.signature.returnClause?.type.as(IdentifierTypeSyntax.self) != nil @@ -210,7 +213,7 @@ extension ReducerMacro: MemberMacro { method.signature.parameterClause.parameters.count == 2, let state = method.signature.parameterClause.parameters.first, state.firstName.text == "into", - state.type.as(AttributedTypeSyntax.self)?.specifier?.text == "inout", + state.type.as(AttributedTypeSyntax.self)?.isInout == true, method.signature.parameterClause.parameters.last?.firstName.text == "action", method.signature.effectSpecifiers == nil, method.signature.returnClause?.type.as(IdentifierTypeSyntax.self) != nil diff --git a/Sources/ComposableArchitectureMacros/ViewActionMacro.swift b/Sources/ComposableArchitectureMacros/ViewActionMacro.swift index 04397dd20849..69e444978739 100644 --- a/Sources/ComposableArchitectureMacros/ViewActionMacro.swift +++ b/Sources/ComposableArchitectureMacros/ViewActionMacro.swift @@ -1,8 +1,11 @@ import SwiftDiagnostics import SwiftSyntax -import SwiftSyntaxMacroExpansion import SwiftSyntaxMacros +#if !canImport(SwiftSyntax600) + import SwiftSyntaxMacroExpansion +#endif + public struct ViewActionMacro: ExtensionMacro { public static func expansion( of node: AttributeSyntax,