diff --git a/Sources/AblyChat/DefaultMessages.swift b/Sources/AblyChat/DefaultMessages.swift index d11b283..9981c96 100644 --- a/Sources/AblyChat/DefaultMessages.swift +++ b/Sources/AblyChat/DefaultMessages.swift @@ -13,7 +13,7 @@ private struct MessageSubscriptionWrapper { @MainActor internal final class DefaultMessages: Messages, EmitsDiscontinuities { private let roomID: String - public nonisolated let channel: RealtimeChannelProtocol + public nonisolated let featureChannel: FeatureChannel private let chatAPI: ChatAPI private let clientID: String @@ -21,8 +21,8 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities { // UUID acts as a unique identifier for each listener/subscription. MessageSubscriptionWrapper houses the subscription and the timeserial of when it was attached or resumed. private var subscriptionPoints: [UUID: MessageSubscriptionWrapper] = [:] - internal nonisolated init(channel: RealtimeChannelProtocol, chatAPI: ChatAPI, roomID: String, clientID: String) async { - self.channel = channel + internal nonisolated init(featureChannel: FeatureChannel, chatAPI: ChatAPI, roomID: String, clientID: String) async { + self.featureChannel = featureChannel self.chatAPI = chatAPI self.roomID = roomID self.clientID = clientID @@ -32,6 +32,10 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities { await handleChannelEvents(roomId: roomID) } + internal nonisolated var channel: any RealtimeChannelProtocol { + featureChannel.channel + } + // (CHA-M4) Messages can be received via a subscription in realtime. internal func subscribe(bufferingPolicy: BufferingPolicy) async throws -> MessageSubscription { let uuid = UUID() @@ -100,8 +104,9 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities { } // TODO: (CHA-M7) Users may subscribe to discontinuity events to know when there’s been a break in messages that they need to resolve. Their listener will be called when a discontinuity event is triggered from the room lifecycle. - https://github.com/ably-labs/ably-chat-swift/issues/47 - internal nonisolated func subscribeToDiscontinuities() -> Subscription { - fatalError("not implemented") + internal func subscribeToDiscontinuities() async -> Subscription { + // TODO: test + await featureChannel.subscribeToDiscontinuities() } private func getBeforeSubscriptionStart(_ uuid: UUID, params: QueryOptions) async throws -> any PaginatedResult { diff --git a/Sources/AblyChat/DefaultRoomLifecycleContributor.swift b/Sources/AblyChat/DefaultRoomLifecycleContributor.swift index a236061..601850f 100644 --- a/Sources/AblyChat/DefaultRoomLifecycleContributor.swift +++ b/Sources/AblyChat/DefaultRoomLifecycleContributor.swift @@ -1,8 +1,9 @@ import Ably -internal actor DefaultRoomLifecycleContributor: RoomLifecycleContributor { +internal actor DefaultRoomLifecycleContributor: RoomLifecycleContributor, EmitsDiscontinuities { internal let channel: DefaultRoomLifecycleContributorChannel internal let feature: RoomFeature + private var discontinuitySubscriptions: [Subscription] = [] internal init(channel: DefaultRoomLifecycleContributorChannel, feature: RoomFeature) { self.channel = channel @@ -11,8 +12,17 @@ internal actor DefaultRoomLifecycleContributor: RoomLifecycleContributor { // MARK: - Discontinuities - internal func emitDiscontinuity(_: ARTErrorInfo) { - // TODO: https://github.com/ably-labs/ably-chat-swift/issues/47 + internal func emitDiscontinuity(_ error: ARTErrorInfo) { + for subscription in discontinuitySubscriptions { + subscription.emit(error) + } + } + + internal func subscribeToDiscontinuities() -> Subscription { + let subscription = Subscription(bufferingPolicy: .unbounded) + // TODO: clean up old subscriptions (https://github.com/ably-labs/ably-chat-swift/issues/36) + discontinuitySubscriptions.append(subscription) + return subscription } } diff --git a/Sources/AblyChat/RoomFeature.swift b/Sources/AblyChat/RoomFeature.swift index e2fb70f..c31a1c1 100644 --- a/Sources/AblyChat/RoomFeature.swift +++ b/Sources/AblyChat/RoomFeature.swift @@ -1,3 +1,5 @@ +import Ably + /// The features offered by a chat room. internal enum RoomFeature { case messages @@ -21,3 +23,17 @@ internal enum RoomFeature { } } } + +// TODO: what's this? +internal protocol FeatureChannel: Sendable, EmitsDiscontinuities { + var channel: RealtimeChannelProtocol { get } +} + +internal struct DefaultFeatureChannel: FeatureChannel { + internal var channel: RealtimeChannelProtocol + internal var contributor: DefaultRoomLifecycleContributor + + internal func subscribeToDiscontinuities() async -> Subscription { + await contributor.subscribeToDiscontinuities() + } +} diff --git a/Tests/AblyChatTests/Mocks/MockFeatureChannel.swift b/Tests/AblyChatTests/Mocks/MockFeatureChannel.swift new file mode 100644 index 0000000..fd09136 --- /dev/null +++ b/Tests/AblyChatTests/Mocks/MockFeatureChannel.swift @@ -0,0 +1,14 @@ +import Ably +@testable import AblyChat + +final class MockFeatureChannel: FeatureChannel { + let channel: RealtimeChannelProtocol + + init(channel: RealtimeChannelProtocol) { + self.channel = channel + } + + func subscribeToDiscontinuities() async -> Subscription { + fatalError("TODO") + } +}