From 781dbf9e23bbdc8896a87edbe804992732763280 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Tue, 14 Jan 2025 17:13:08 -0300 Subject: [PATCH 1/3] Remove comments about adding properties to ably-cocoa protocols We won't be able to do this; see message of ably-cocoa commit e216ae5. The approach currently taken by the Chat SDK (adding these properties and methods onto its own Swift protocol that uses associated types) is the one that we should continue. --- Sources/AblyChat/Dependencies.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Sources/AblyChat/Dependencies.swift b/Sources/AblyChat/Dependencies.swift index 13cff5f..c108cfb 100644 --- a/Sources/AblyChat/Dependencies.swift +++ b/Sources/AblyChat/Dependencies.swift @@ -7,10 +7,7 @@ public protocol RealtimeClientProtocol: ARTRealtimeProtocol, Sendable { associatedtype Channels: RealtimeChannelsProtocol associatedtype Connection: ConnectionProtocol - // It’s not clear to me why ARTRealtimeProtocol doesn’t include this property. I briefly tried adding it but ran into compilation failures that it wasn’t immediately obvious how to fix. var channels: Channels { get } - - // TODO: Expose `Connection` on ARTRealtimeProtocol so it can be used from RealtimeClientProtocol - https://github.com/ably-labs/ably-chat-swift/issues/123 var connection: Connection { get } } @@ -18,7 +15,6 @@ public protocol RealtimeClientProtocol: ARTRealtimeProtocol, Sendable { public protocol RealtimeChannelsProtocol: ARTRealtimeChannelsProtocol, Sendable { associatedtype Channel: RealtimeChannelProtocol - // It’s not clear to me why ARTRealtimeChannelsProtocol doesn’t include this function (https://github.com/ably/ably-cocoa/issues/1968). func get(_ name: String, options: ARTRealtimeChannelOptions) -> Channel } From 446946173fd8ff0635a5d7d644eda78b4c46ddba Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Tue, 14 Jan 2025 17:13:57 -0300 Subject: [PATCH 2/3] Add missing comment --- Sources/AblyChat/Dependencies.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/AblyChat/Dependencies.swift b/Sources/AblyChat/Dependencies.swift index c108cfb..0ad6410 100644 --- a/Sources/AblyChat/Dependencies.swift +++ b/Sources/AblyChat/Dependencies.swift @@ -21,6 +21,7 @@ public protocol RealtimeChannelsProtocol: ARTRealtimeChannelsProtocol, Sendable /// Expresses the requirements of the object returned by ``RealtimeChannelsProtocol/get(_:options:)``. public protocol RealtimeChannelProtocol: ARTRealtimeChannelProtocol, Sendable {} +/// Expresses the requirements of the object returned by ``RealtimeClientProtocol/connection``. public protocol ConnectionProtocol: ARTConnectionProtocol, Sendable {} /// Like (a subset of) `ARTRealtimeChannelOptions` but with value semantics. (It’s unfortunate that `ARTRealtimeChannelOptions` doesn’t have a `-copy` method.) From a339784b718a722f4fb8a8be624167140f6a8d65 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Thu, 16 Jan 2025 12:02:44 -0300 Subject: [PATCH 3/3] Unpin ably-cocoa This reverts b261155. Handle ably-cocoa's new commit e216ae5, which reverted the backwards compatibility-breaking commit 26d9bf7. To handle e216ae5, we need to add the realtime client's `presence` member to our requirements. --- .../xcshareddata/swiftpm/Package.resolved | 6 +- .../AblyChatExample/Mocks/MockClients.swift | 8 +- .../AblyChatExample/Mocks/MockRealtime.swift | 100 +++++++++++++++++- Package.resolved | 6 +- Package.swift | 7 +- .../Ably+Dependencies.swift | 2 + Sources/AblyChat/Dependencies.swift | 9 +- Sources/AblyChat/Messages.swift | 2 +- Sources/AblyChat/Occupancy.swift | 2 +- Sources/AblyChat/Room.swift | 2 +- Sources/AblyChat/RoomFeature.swift | 4 +- Sources/AblyChat/RoomReactions.swift | 2 +- Sources/AblyChat/Typing.swift | 2 +- .../Mocks/MockFeatureChannel.swift | 4 +- .../Mocks/MockRealtimeChannel.swift | 4 +- .../Mocks/MockRealtimePresence.swift | 100 ++++++++++++++++++ 16 files changed, 228 insertions(+), 32 deletions(-) create mode 100644 Tests/AblyChatTests/Mocks/MockRealtimePresence.swift diff --git a/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved index 10daf15..84f8b30 100644 --- a/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "20eda2776391659360fe1f5d5d8095daadf2aa748972649241e4432cdb687f89", + "originHash" : "77e4b2b661f9de38584f7d61ba62b95f5fe6ef751d01e208399bed3950246e2b", "pins" : [ { "identity" : "ably-cocoa", "kind" : "remoteSourceControl", "location" : "https://github.com/ably/ably-cocoa", "state" : { - "revision" : "5ba7809f5de6e885e8bd4788f0e807f66fc1875b", - "version" : "1.2.36" + "revision" : "35805d0e96a1df5b4dc0f6d82afe22ab753c15bd", + "version" : "1.2.37" } }, { diff --git a/Example/AblyChatExample/Mocks/MockClients.swift b/Example/AblyChatExample/Mocks/MockClients.swift index 86c8209..2cca15b 100644 --- a/Example/AblyChatExample/Mocks/MockClients.swift +++ b/Example/AblyChatExample/Mocks/MockClients.swift @@ -88,7 +88,7 @@ actor MockRoom: Room { actor MockMessages: Messages { let clientID: String let roomID: String - let channel: RealtimeChannelProtocol + let channel: any RealtimeChannelProtocol private let mockSubscriptions = MockSubscriptionStorage() @@ -146,7 +146,7 @@ actor MockMessages: Messages { actor MockRoomReactions: RoomReactions { let clientID: String let roomID: String - let channel: RealtimeChannelProtocol + let channel: any RealtimeChannelProtocol private let mockSubscriptions = MockSubscriptionStorage() @@ -193,7 +193,7 @@ actor MockRoomReactions: RoomReactions { actor MockTyping: Typing { let clientID: String let roomID: String - let channel: RealtimeChannelProtocol + let channel: any RealtimeChannelProtocol private let mockSubscriptions = MockSubscriptionStorage() @@ -356,7 +356,7 @@ actor MockPresence: Presence { actor MockOccupancy: Occupancy { let clientID: String let roomID: String - let channel: RealtimeChannelProtocol + let channel: any RealtimeChannelProtocol private let mockSubscriptions = MockSubscriptionStorage() diff --git a/Example/AblyChatExample/Mocks/MockRealtime.swift b/Example/AblyChatExample/Mocks/MockRealtime.swift index e22f258..58d5b28 100644 --- a/Example/AblyChatExample/Mocks/MockRealtime.swift +++ b/Example/AblyChatExample/Mocks/MockRealtime.swift @@ -105,9 +105,7 @@ final class MockRealtime: NSObject, RealtimeClientProtocol, Sendable { fatalError("Not implemented") } - var presence: ARTRealtimePresenceProtocol { - fatalError("Not implemented") - } + let presence = RealtimePresence() var errorReason: ARTErrorInfo? { fatalError("Not implemented") @@ -250,6 +248,102 @@ final class MockRealtime: NSObject, RealtimeClientProtocol, Sendable { } } + final class RealtimePresence: RealtimePresenceProtocol { + var syncComplete: Bool { + fatalError("Not implemented") + } + + func get(_: @escaping ARTPresenceMessagesCallback) { + fatalError("Not implemented") + } + + func get(_: ARTRealtimePresenceQuery, callback _: @escaping ARTPresenceMessagesCallback) { + fatalError("Not implemented") + } + + func enter(_: Any?) { + fatalError("Not implemented") + } + + func enter(_: Any?, callback _: ARTCallback? = nil) { + fatalError("Not implemented") + } + + func update(_: Any?) { + fatalError("Not implemented") + } + + func update(_: Any?, callback _: ARTCallback? = nil) { + fatalError("Not implemented") + } + + func leave(_: Any?) { + fatalError("Not implemented") + } + + func leave(_: Any?, callback _: ARTCallback? = nil) { + fatalError("Not implemented") + } + + func enterClient(_: String, data _: Any?) { + fatalError("Not implemented") + } + + func enterClient(_: String, data _: Any?, callback _: ARTCallback? = nil) { + fatalError("Not implemented") + } + + func updateClient(_: String, data _: Any?) { + fatalError("Not implemented") + } + + func updateClient(_: String, data _: Any?, callback _: ARTCallback? = nil) { + fatalError("Not implemented") + } + + func leaveClient(_: String, data _: Any?) { + fatalError("Not implemented") + } + + func leaveClient(_: String, data _: Any?, callback _: ARTCallback? = nil) { + fatalError("Not implemented") + } + + func subscribe(_: @escaping ARTPresenceMessageCallback) -> ARTEventListener? { + fatalError("Not implemented") + } + + func subscribe(attachCallback _: ARTCallback?, callback _: @escaping ARTPresenceMessageCallback) -> ARTEventListener? { + fatalError("Not implemented") + } + + func subscribe(_: ARTPresenceAction, callback _: @escaping ARTPresenceMessageCallback) -> ARTEventListener? { + fatalError("Not implemented") + } + + func subscribe(_: ARTPresenceAction, onAttach _: ARTCallback?, callback _: @escaping ARTPresenceMessageCallback) -> ARTEventListener? { + fatalError("Not implemented") + } + + func unsubscribe() { + fatalError("Not implemented") + } + + func unsubscribe(_: ARTEventListener) { + fatalError("Not implemented") + } + + func unsubscribe(_: ARTPresenceAction, listener _: ARTEventListener) { + fatalError("Not implemented") + } + + func history(_: @escaping ARTPaginatedPresenceCallback) {} + + func history(_: ARTRealtimeHistoryQuery?, callback _: @escaping ARTPaginatedPresenceCallback) throws { + fatalError("Not implemented") + } + } + required init(options _: ARTClientOptions) {} required init(key _: String) {} diff --git a/Package.resolved b/Package.resolved index 04ec577..b2ad76d 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "f5dd87027f1300f852cf86019686409a47c851bc11433a4138c8956b8a09ad6f", + "originHash" : "8c7acde3826a921568cc75459ff2052ebec85d53398758c637099c1128f45e32", "pins" : [ { "identity" : "ably-cocoa", "kind" : "remoteSourceControl", "location" : "https://github.com/ably/ably-cocoa", "state" : { - "revision" : "5ba7809f5de6e885e8bd4788f0e807f66fc1875b", - "version" : "1.2.36" + "revision" : "35805d0e96a1df5b4dc0f6d82afe22ab753c15bd", + "version" : "1.2.37" } }, { diff --git a/Package.swift b/Package.swift index 499bf38..29e6052 100644 --- a/Package.swift +++ b/Package.swift @@ -20,12 +20,7 @@ let package = Package( dependencies: [ .package( url: "https://github.com/ably/ably-cocoa", - /* - The upcoming ably-cocoa 1.2.37 will revert a change on which the Chat SDK depends. It will not be possible to make a single version of the Chat SDK work with ably-cocoa versions 1.2.36 and 1.2.37. - - So, in order to make sure that the Chat SDK continues to work once ably-cocoa 1.2.37 is released, let's temporarily lock the ably-cocoa dependency to 1.2.36, and release a new version of the Chat SDK. Then, once ably-cocoa 1.2.37 is released, we can release another version of the Chat SDK that requires 1.2.37 and above. - */ - exact: "1.2.36" + from: "1.2.37" ), .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 057cc2a..e55bdb0 100644 --- a/Sources/AblyChat/AblyCocoaExtensions/Ably+Dependencies.swift +++ b/Sources/AblyChat/AblyCocoaExtensions/Ably+Dependencies.swift @@ -6,4 +6,6 @@ extension ARTRealtimeChannels: RealtimeChannelsProtocol {} extension ARTRealtimeChannel: RealtimeChannelProtocol {} +extension ARTRealtimePresence: RealtimePresenceProtocol {} + extension ARTConnection: ConnectionProtocol {} diff --git a/Sources/AblyChat/Dependencies.swift b/Sources/AblyChat/Dependencies.swift index 0ad6410..1c3172e 100644 --- a/Sources/AblyChat/Dependencies.swift +++ b/Sources/AblyChat/Dependencies.swift @@ -19,7 +19,14 @@ public protocol RealtimeChannelsProtocol: ARTRealtimeChannelsProtocol, Sendable } /// Expresses the requirements of the object returned by ``RealtimeChannelsProtocol/get(_:options:)``. -public protocol RealtimeChannelProtocol: ARTRealtimeChannelProtocol, Sendable {} +public protocol RealtimeChannelProtocol: ARTRealtimeChannelProtocol, Sendable { + associatedtype Presence: RealtimePresenceProtocol + + var presence: Presence { get } +} + +/// Expresses the requirements of the object returned by ``RealtimeChannelProtocol/presence``. +public protocol RealtimePresenceProtocol: ARTRealtimePresenceProtocol, Sendable {} /// Expresses the requirements of the object returned by ``RealtimeClientProtocol/connection``. public protocol ConnectionProtocol: ARTConnectionProtocol, Sendable {} diff --git a/Sources/AblyChat/Messages.swift b/Sources/AblyChat/Messages.swift index 5a7114c..366b732 100644 --- a/Sources/AblyChat/Messages.swift +++ b/Sources/AblyChat/Messages.swift @@ -51,7 +51,7 @@ public protocol Messages: AnyObject, Sendable, EmitsDiscontinuities { * * - Returns: The realtime channel. */ - var channel: RealtimeChannelProtocol { get } + var channel: any RealtimeChannelProtocol { get } } public extension Messages { diff --git a/Sources/AblyChat/Occupancy.swift b/Sources/AblyChat/Occupancy.swift index 8d1bb75..135e8f1 100644 --- a/Sources/AblyChat/Occupancy.swift +++ b/Sources/AblyChat/Occupancy.swift @@ -34,7 +34,7 @@ public protocol Occupancy: AnyObject, Sendable, EmitsDiscontinuities { * * - Returns: The underlying Ably channel for occupancy events. */ - var channel: RealtimeChannelProtocol { get } + var channel: any RealtimeChannelProtocol { get } } public extension Occupancy { diff --git a/Sources/AblyChat/Room.swift b/Sources/AblyChat/Room.swift index 80e168b..97b1b9e 100644 --- a/Sources/AblyChat/Room.swift +++ b/Sources/AblyChat/Room.swift @@ -300,7 +300,7 @@ internal actor DefaultRoom } private struct FeatureChannelPartialDependencies { - internal var channel: RealtimeChannelProtocol + internal var channel: any RealtimeChannelProtocol internal var contributor: DefaultRoomLifecycleContributor } diff --git a/Sources/AblyChat/RoomFeature.swift b/Sources/AblyChat/RoomFeature.swift index 69ab9bf..61d19bb 100644 --- a/Sources/AblyChat/RoomFeature.swift +++ b/Sources/AblyChat/RoomFeature.swift @@ -46,7 +46,7 @@ internal enum RoomFeature: CaseIterable { /// - the discontinuities emitted by the room lifecycle /// - the presence-readiness wait mechanism supplied by the room lifecycle internal protocol FeatureChannel: Sendable, EmitsDiscontinuities { - var channel: RealtimeChannelProtocol { get } + var channel: any RealtimeChannelProtocol { get } /// Waits until we can perform presence operations on the contributors of this room without triggering an implicit attach. /// @@ -62,7 +62,7 @@ internal protocol FeatureChannel: Sendable, EmitsDiscontinuities { } internal struct DefaultFeatureChannel: FeatureChannel { - internal var channel: RealtimeChannelProtocol + internal var channel: any RealtimeChannelProtocol internal var contributor: DefaultRoomLifecycleContributor internal var roomLifecycleManager: RoomLifecycleManager diff --git a/Sources/AblyChat/RoomReactions.swift b/Sources/AblyChat/RoomReactions.swift index f4828b2..ff69c38 100644 --- a/Sources/AblyChat/RoomReactions.swift +++ b/Sources/AblyChat/RoomReactions.swift @@ -22,7 +22,7 @@ public protocol RoomReactions: AnyObject, Sendable, EmitsDiscontinuities { * * - Returns: The realtime channel. */ - var channel: RealtimeChannelProtocol { get } + var channel: any RealtimeChannelProtocol { get } /** * Subscribes a given listener to receive room-level reactions. diff --git a/Sources/AblyChat/Typing.swift b/Sources/AblyChat/Typing.swift index 49b9d43..5c2065f 100644 --- a/Sources/AblyChat/Typing.swift +++ b/Sources/AblyChat/Typing.swift @@ -54,7 +54,7 @@ public protocol Typing: AnyObject, Sendable, EmitsDiscontinuities { * * - Returns: The Ably realtime channel. */ - var channel: RealtimeChannelProtocol { get } + var channel: any RealtimeChannelProtocol { get } } public extension Typing { diff --git a/Tests/AblyChatTests/Mocks/MockFeatureChannel.swift b/Tests/AblyChatTests/Mocks/MockFeatureChannel.swift index 2b66506..e3893e9 100644 --- a/Tests/AblyChatTests/Mocks/MockFeatureChannel.swift +++ b/Tests/AblyChatTests/Mocks/MockFeatureChannel.swift @@ -2,12 +2,12 @@ import Ably @testable import AblyChat final actor MockFeatureChannel: FeatureChannel { - let channel: RealtimeChannelProtocol + let channel: any RealtimeChannelProtocol private var discontinuitySubscriptions = SubscriptionStorage() private let resultOfWaitToBeAbleToPerformPresenceOperations: Result? init( - channel: RealtimeChannelProtocol, + channel: any RealtimeChannelProtocol, resultOfWaitToBeAblePerformPresenceOperations: Result? = nil ) { self.channel = channel diff --git a/Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift b/Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift index 071bc4a..0f92c5a 100644 --- a/Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift +++ b/Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift @@ -2,9 +2,7 @@ import Ably import AblyChat final class MockRealtimeChannel: NSObject, RealtimeChannelProtocol { - var presence: ARTRealtimePresenceProtocol { - fatalError("Not implemented") - } + let presence = MockRealtimePresence() private let attachSerial: String? private let channelSerial: String? diff --git a/Tests/AblyChatTests/Mocks/MockRealtimePresence.swift b/Tests/AblyChatTests/Mocks/MockRealtimePresence.swift new file mode 100644 index 0000000..47257aa --- /dev/null +++ b/Tests/AblyChatTests/Mocks/MockRealtimePresence.swift @@ -0,0 +1,100 @@ +import Ably +import AblyChat + +final class MockRealtimePresence: RealtimePresenceProtocol { + var syncComplete: Bool { + fatalError("Not implemented") + } + + func get(_: @escaping ARTPresenceMessagesCallback) { + fatalError("Not implemented") + } + + func get(_: ARTRealtimePresenceQuery, callback _: @escaping ARTPresenceMessagesCallback) { + fatalError("Not implemented") + } + + func enter(_: Any?) { + fatalError("Not implemented") + } + + func enter(_: Any?, callback _: ARTCallback? = nil) { + fatalError("Not implemented") + } + + func update(_: Any?) { + fatalError("Not implemented") + } + + func update(_: Any?, callback _: ARTCallback? = nil) { + fatalError("Not implemented") + } + + func leave(_: Any?) { + fatalError("Not implemented") + } + + func leave(_: Any?, callback _: ARTCallback? = nil) { + fatalError("Not implemented") + } + + func enterClient(_: String, data _: Any?) { + fatalError("Not implemented") + } + + func enterClient(_: String, data _: Any?, callback _: ARTCallback? = nil) { + fatalError("Not implemented") + } + + func updateClient(_: String, data _: Any?) { + fatalError("Not implemented") + } + + func updateClient(_: String, data _: Any?, callback _: ARTCallback? = nil) { + fatalError("Not implemented") + } + + func leaveClient(_: String, data _: Any?) { + fatalError("Not implemented") + } + + func leaveClient(_: String, data _: Any?, callback _: ARTCallback? = nil) { + fatalError("Not implemented") + } + + func subscribe(_: @escaping ARTPresenceMessageCallback) -> ARTEventListener? { + fatalError("Not implemented") + } + + func subscribe(attachCallback _: ARTCallback?, callback _: @escaping ARTPresenceMessageCallback) -> ARTEventListener? { + fatalError("Not implemented") + } + + func subscribe(_: ARTPresenceAction, callback _: @escaping ARTPresenceMessageCallback) -> ARTEventListener? { + fatalError("Not implemented") + } + + func subscribe(_: ARTPresenceAction, onAttach _: ARTCallback?, callback _: @escaping ARTPresenceMessageCallback) -> ARTEventListener? { + fatalError("Not implemented") + } + + func unsubscribe() { + fatalError("Not implemented") + } + + func unsubscribe(_: ARTEventListener) { + fatalError("Not implemented") + } + + func unsubscribe(_: ARTPresenceAction, listener _: ARTEventListener) { + fatalError("Not implemented") + } + + func history(_: @escaping ARTPaginatedPresenceCallback) { + fatalError("Not implemented") + } + + func history(_: ARTRealtimeHistoryQuery?, callback _: @escaping ARTPaginatedPresenceCallback) throws { + fatalError("Not implemented") + } +}