From 620df5a35c4533596e1bea1183f97b743f757b14 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Tue, 8 Oct 2024 11:15:33 -0300 Subject: [PATCH 1/2] Ignore originHash when comparing lockfiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See justification in code (I’m running into the described issue when trying to switch our ably-cooca dependency to use `main` instead of a tag). --- Sources/BuildTool/BuildTool.swift | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/Sources/BuildTool/BuildTool.swift b/Sources/BuildTool/BuildTool.swift index 1a5bd27e..ec6be6cc 100644 --- a/Sources/BuildTool/BuildTool.swift +++ b/Sources/BuildTool/BuildTool.swift @@ -96,6 +96,7 @@ struct Lint: AsyncParsableCommand { enum Error: Swift.Error { case malformedSwiftVersionFile case malformedPackageManifestFile + case malformedPackageLockfile case mismatchedVersions(swiftVersionFileVersion: String, packageManifestFileVersion: String) case packageLockfilesHaveDifferentContents(paths: [String]) } @@ -152,12 +153,14 @@ struct Lint: AsyncParsableCommand { } /// Checks that the SPM-managed Package.resolved matches the Xcode-managed one. (I still don’t fully understand _why_ there are two files). + /// + /// Ignores the `originHash` property of the Packaged.resolved file, because this property seems to frequently be different between the SPM version and the Xcode version, and I don’t know enough about SPM to know what this property means or whether there’s a reproducible way to get them to match. func comparePackageLockfiles() async throws { let lockfilePaths = ["Package.resolved", "AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved"] - let lockfileContents = try await withThrowingTaskGroup(of: String.self) { group in + let lockfileContents = try await withThrowingTaskGroup(of: Data.self) { group in for lockfilePath in lockfilePaths { group.addTask { - try await loadUTF8StringFromFile(at: lockfilePath) + try await loadDataFromFile(at: lockfilePath) } } @@ -166,13 +169,30 @@ struct Lint: AsyncParsableCommand { } } - if Set(lockfileContents).count > 1 { + // Remove the `originHash` property from the Package.resolved contents before comparing (for reasons described above). + let lockfileContentsWeCareAbout = try lockfileContents.map { data in + guard var dictionary = try JSONSerialization.jsonObject(with: data) as? [String: Any] else { + throw Error.malformedPackageLockfile + } + + dictionary.removeValue(forKey: "originHash") + + // We use .sortedKeys to get a canonical JSON encoding for comparison. + return try JSONSerialization.data(withJSONObject: dictionary, options: .sortedKeys) + } + + if Set(lockfileContentsWeCareAbout).count > 1 { throw Error.packageLockfilesHaveDifferentContents(paths: lockfilePaths) } } - private func loadUTF8StringFromFile(at path: String) async throws -> String { + private func loadDataFromFile(at path: String) async throws -> Data { let (data, _) = try await URLSession.shared.data(from: .init(filePath: path)) + return data + } + + private func loadUTF8StringFromFile(at path: String) async throws -> String { + let data = try await loadDataFromFile(at: path) return try String(data: data, encoding: .utf8) } } From 7b3c97f2dbbc642116e0c10db84e64b7a76e26c5 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Tue, 8 Oct 2024 11:20:11 -0300 Subject: [PATCH 2/2] Switch to (temporarily) use `main` branch of ably-cocoa This allows us to make use of the Swift concurrency enhancements that have been added on `main`, and to identify any further concurrency enhancements that still need to be made (in response to any new concurrency errors that we get as we continue to develop the Chat SDK). --- .../xcshareddata/swiftpm/Package.resolved | 6 +++--- Example/AblyChatExample/Mocks/MockRealtime.swift | 8 ++++++++ Package.resolved | 6 +++--- Package.swift | 3 ++- .../AblyChat/AblyCocoaExtensions/Ably+Dependencies.swift | 9 +++------ Sources/BuildTool/BuildTool.swift | 2 +- Tests/AblyChatTests/Mocks/MockRealtime.swift | 4 ++++ Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift | 4 ++++ 8 files changed, 28 insertions(+), 14 deletions(-) diff --git a/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved index 9cffb1ec..74c3fc81 100644 --- a/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "fcc346d6fe86e610ac200cdbbf91c56204df67286546d5079bd9c610ee65953b", + "originHash" : "6a8d15fb1d326ac6e8a40c286c152332146d6f58c73123cb8083f68d483dd728", "pins" : [ { "identity" : "ably-cocoa", "kind" : "remoteSourceControl", "location" : "https://github.com/ably/ably-cocoa", "state" : { - "revision" : "7f639c609e50053abd4590f34333f9472645558a", - "version" : "1.2.33" + "branch" : "main", + "revision" : "ccca241a8a7f08b22a93802161460c843d9b5bf3" } }, { diff --git a/Example/AblyChatExample/Mocks/MockRealtime.swift b/Example/AblyChatExample/Mocks/MockRealtime.swift index 067e8f69..e74923a3 100644 --- a/Example/AblyChatExample/Mocks/MockRealtime.swift +++ b/Example/AblyChatExample/Mocks/MockRealtime.swift @@ -44,6 +44,10 @@ final class MockRealtime: NSObject, RealtimeClientProtocol, Sendable { fatalError("Not implemented") } + var properties: ARTChannelProperties { + fatalError("Not implemented") + } + func attach() { fatalError("Not implemented") } @@ -211,4 +215,8 @@ final class MockRealtime: NSObject, RealtimeClientProtocol, Sendable { func close() { fatalError("Not implemented") } + + func request(_: String, path _: String, params _: [String: String]?, body _: Any?, headers _: [String: String]?, callback _: @escaping ARTHTTPPaginatedCallback) throws { + fatalError("Not implemented") + } } diff --git a/Package.resolved b/Package.resolved index 9cffb1ec..03ddbb80 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "fcc346d6fe86e610ac200cdbbf91c56204df67286546d5079bd9c610ee65953b", + "originHash" : "db24f2979451a46f504f45d35893eb8501f27488ae70e1412340139a0e7551e2", "pins" : [ { "identity" : "ably-cocoa", "kind" : "remoteSourceControl", "location" : "https://github.com/ably/ably-cocoa", "state" : { - "revision" : "7f639c609e50053abd4590f34333f9472645558a", - "version" : "1.2.33" + "branch" : "main", + "revision" : "ccca241a8a7f08b22a93802161460c843d9b5bf3" } }, { diff --git a/Package.swift b/Package.swift index fc233cd2..14f5e281 100644 --- a/Package.swift +++ b/Package.swift @@ -20,7 +20,8 @@ let package = Package( dependencies: [ .package( url: "https://github.com/ably/ably-cocoa", - from: "1.2.0" + // TODO: Switch back to using a tag (https://github.com/ably-labs/ably-chat-swift/issues/80) + branch: "main" ), .package( url: "https://github.com/apple/swift-argument-parser", diff --git a/Sources/AblyChat/AblyCocoaExtensions/Ably+Dependencies.swift b/Sources/AblyChat/AblyCocoaExtensions/Ably+Dependencies.swift index b193a9fe..ae17fde3 100644 --- a/Sources/AblyChat/AblyCocoaExtensions/Ably+Dependencies.swift +++ b/Sources/AblyChat/AblyCocoaExtensions/Ably+Dependencies.swift @@ -1,10 +1,7 @@ import Ably -// TODO: remove "@unchecked Sendable" once https://github.com/ably/ably-cocoa/issues/1962 done +extension ARTRealtime: RealtimeClientProtocol {} -// This @retroactive is needed to silence the Swift 6 compiler error "extension declares a conformance of imported type 'ARTRealtimeChannels' to imported protocol 'Sendable'; this will not behave correctly if the owners of 'Ably' introduce this conformance in the future (…) add '@retroactive' to silence this warning". I don’t fully understand the implications of this but don’t really mind since both libraries are in our control. -extension ARTRealtime: RealtimeClientProtocol, @retroactive @unchecked Sendable {} +extension ARTRealtimeChannels: RealtimeChannelsProtocol {} -extension ARTRealtimeChannels: RealtimeChannelsProtocol, @retroactive @unchecked Sendable {} - -extension ARTRealtimeChannel: RealtimeChannelProtocol, @retroactive @unchecked Sendable {} +extension ARTRealtimeChannel: RealtimeChannelProtocol {} diff --git a/Sources/BuildTool/BuildTool.swift b/Sources/BuildTool/BuildTool.swift index ec6be6cc..5c451a10 100644 --- a/Sources/BuildTool/BuildTool.swift +++ b/Sources/BuildTool/BuildTool.swift @@ -154,7 +154,7 @@ struct Lint: AsyncParsableCommand { /// Checks that the SPM-managed Package.resolved matches the Xcode-managed one. (I still don’t fully understand _why_ there are two files). /// - /// Ignores the `originHash` property of the Packaged.resolved file, because this property seems to frequently be different between the SPM version and the Xcode version, and I don’t know enough about SPM to know what this property means or whether there’s a reproducible way to get them to match. + /// Ignores the `originHash` property of the Package.resolved file, because this property seems to frequently be different between the SPM version and the Xcode version, and I don’t know enough about SPM to know what this property means or whether there’s a reproducible way to get them to match. func comparePackageLockfiles() async throws { let lockfilePaths = ["Package.resolved", "AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved"] let lockfileContents = try await withThrowingTaskGroup(of: Data.self) { group in diff --git a/Tests/AblyChatTests/Mocks/MockRealtime.swift b/Tests/AblyChatTests/Mocks/MockRealtime.swift index e8c82779..6d0309d7 100644 --- a/Tests/AblyChatTests/Mocks/MockRealtime.swift +++ b/Tests/AblyChatTests/Mocks/MockRealtime.swift @@ -62,4 +62,8 @@ final class MockRealtime: NSObject, RealtimeClientProtocol, Sendable { func close() { fatalError("Not implemented") } + + func request(_: String, path _: String, params _: [String: String]?, body _: Any?, headers _: [String: String]?, callback _: @escaping ARTHTTPPaginatedCallback) throws { + fatalError("Not implemented") + } } diff --git a/Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift b/Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift index f01f70b2..5d556ae1 100644 --- a/Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift +++ b/Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift @@ -54,6 +54,10 @@ final class MockRealtimeChannel: NSObject, RealtimeChannelProtocol { fatalError("Not implemented") } + var properties: ARTChannelProperties { + fatalError("Not implemented") + } + func attach() { fatalError("Not implemented") }