Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Example/AblyChatExample/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ struct ContentView: View {
}

func subscribeToPresence(room: any Room) {
room.presence.subscribe(events: [.enter, .leave, .update]) { event in
room.presence.subscribe { event in
withAnimation {
listItems.insert(
.presence(
Expand Down
8 changes: 0 additions & 8 deletions Example/AblyChatExample/Mocks/MockClients.swift
Original file line number Diff line number Diff line change
Expand Up @@ -538,14 +538,6 @@ class MockPresence: Presence {
func subscribe(_ callback: @escaping @MainActor (PresenceEvent) -> Void) -> MockSubscription {
createSubscription(callback: callback)
}

func subscribe(event _: PresenceEventType, _ callback: @escaping @MainActor (PresenceEvent) -> Void) -> MockSubscription {
createSubscription(callback: callback)
}

func subscribe(events _: [PresenceEventType], _ callback: @escaping @MainActor (PresenceEvent) -> Void) -> MockSubscription {
createSubscription(callback: callback)
}
}

class MockOccupancy: Occupancy {
Expand Down
51 changes: 0 additions & 51 deletions Sources/AblyChat/DefaultPresence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,32 +211,6 @@ internal final class DefaultPresence: Presence {
return
}

// processPresenceSubscribe is logging so we don't need to log here
let presenceEvent = processPresenceSubscribe(
PresenceMessage(ablyCocoaPresenceMessage: message),
for: event,
)
callback(presenceEvent)
}

return DefaultSubscription { [weak self] in
if let eventListener {
self?.channel.presence.unsubscribe(eventListener)
}
}
}

// (CHA-PR7b) Users may provide a listener and a list of selected presence events, to subscribe to just those events in a room.
internal func subscribe(event: PresenceEventType, _ callback: @escaping @MainActor (PresenceEvent) -> Void) -> DefaultSubscription {
fatalErrorIfEnableEventsDisabled()

logger.log(message: "Subscribing to presence events", level: .debug)

let eventListener = channel.presence.subscribe(event.toARTPresenceAction()) { [weak self] message in
guard let self else {
return
}
logger.log(message: "Received presence message: \(message)", level: .debug)
// processPresenceSubscribe is logging so we don't need to log here
let presenceEvent = processPresenceSubscribe(PresenceMessage(ablyCocoaPresenceMessage: message), for: event)
callback(presenceEvent)
Expand All @@ -249,31 +223,6 @@ internal final class DefaultPresence: Presence {
}
}

internal func subscribe(events: [PresenceEventType], _ callback: @escaping @MainActor (PresenceEvent) -> Void) -> DefaultSubscription {
fatalErrorIfEnableEventsDisabled()

logger.log(message: "Subscribing to presence events", level: .debug)

let eventListeners = events.map { event in
channel.presence.subscribe(event.toARTPresenceAction()) { [weak self] message in
guard let self else {
return
}
logger.log(message: "Received presence message: \(message)", level: .debug)
let presenceEvent = processPresenceSubscribe(PresenceMessage(ablyCocoaPresenceMessage: message), for: event)
callback(presenceEvent)
}
}

return DefaultSubscription { [weak self] in
for eventListener in eventListeners {
if let eventListener {
self?.channel.presence.unsubscribe(eventListener)
}
}
}
}

private func processPresenceGet(members: [PresenceMessage]) throws(InternalError) -> [PresenceMember] {
let presenceMembers = try members.map { member throws(InternalError) in
let presenceMember = PresenceMember(
Expand Down
96 changes: 2 additions & 94 deletions Sources/AblyChat/Presence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public protocol Presence: AnyObject, Sendable {
func leave(withData data: PresenceData) async throws(ARTErrorInfo)

/**
* Subscribes a given listener to all presence events.
* Subscribes a given listener to all presence events in the chat room.
*
* Note that it is a programmer error to call this method if presence events are not enabled in the room options. Make sure to set `enableEvents: true` in your room's presence options to use this feature (this is the default value).
*
Expand All @@ -86,34 +86,6 @@ public protocol Presence: AnyObject, Sendable {
@discardableResult
func subscribe(_ callback: @escaping @MainActor (PresenceEvent) -> Void) -> Subscription

/**
* Subscribes a given listener to a particular presence event in the chat room.
*
* Note that it is a programmer error to call this method if presence events are not enabled in the room options. Make sure to set `enableEvents: true` in your room's presence options to use this feature (this is the default value).
*
* - Parameters:
* - event: A single presence event type ``PresenceEventType`` to subscribe to.
* - callback: The listener closure for capturing room ``PresenceEvent`` events.
*
* - Returns: A subscription that can be used to unsubscribe from ``PresenceEvent`` events.
*/
@discardableResult
func subscribe(event: PresenceEventType, _ callback: @escaping @MainActor (PresenceEvent) -> Void) -> Subscription

/**
* Subscribes a given listener to different presence events in the chat room.
*
* Note that it is a programmer error to call this method if presence events are not enabled in the room options. Make sure to set `enableEvents: true` in your room's presence options to use this feature (this is the default value).
*
* - Parameters:
* - events: An array of presence event types ``PresenceEventType`` to subscribe to.
* - callback: The listener closure for capturing room ``PresenceEvent`` events.
*
* - Returns: A subscription that can be used to unsubscribe from ``PresenceEvent`` events.
*/
@discardableResult
func subscribe(events: [PresenceEventType], _ callback: @escaping @MainActor (PresenceEvent) -> Void) -> Subscription

/**
* Method to join room presence, will emit an enter event to all subscribers. Repeat calls will trigger more enter events.
* In oppose to ``enter(data:)`` it doesn't publish any custom presence data.
Expand Down Expand Up @@ -142,7 +114,7 @@ public protocol Presence: AnyObject, Sendable {
// swiftlint:disable:next missing_docs
public extension Presence {
/**
* Subscribes to all presence events.
* Subscribes to all presence events in the chat room.
*
* Note that it is a programmer error to call this method if presence events are not enabled in the room options. Make sure to set `enableEvents: true` in your room's presence options to use this feature (this is the default value).
*
Expand All @@ -167,74 +139,10 @@ public extension Presence {
return subscriptionAsyncSequence
}

/**
* Subscribes a given listener to a particular presence event in the chat room.
*
* Note that it is a programmer error to call this method if presence events are not enabled in the room options. Make sure to set `enableEvents: true` in your room's presence options to use this feature (this is the default value).
*
* - Parameters:
* - event: A single presence event type ``PresenceEventType`` to subscribe to.
* - bufferingPolicy: The ``BufferingPolicy`` for the created subscription.
*
* - Returns: A subscription `AsyncSequence` that can be used to iterate through ``PresenceEvent`` events.
*/
func subscribe(event: PresenceEventType, bufferingPolicy: BufferingPolicy) -> SubscriptionAsyncSequence<PresenceEvent> {
let subscriptionAsyncSequence = SubscriptionAsyncSequence<PresenceEvent>(bufferingPolicy: bufferingPolicy)

let subscription = subscribe(event: event) { presence in
subscriptionAsyncSequence.emit(presence)
}

subscriptionAsyncSequence.addTerminationHandler {
Task { @MainActor in
subscription.unsubscribe()
}
}

return subscriptionAsyncSequence
}

/**
* Subscribes a given listener to different presence events in the chat room.
*
* Note that it is a programmer error to call this method if presence events are not enabled in the room options. Make sure to set `enableEvents: true` in your room's presence options to use this feature (this is the default value).
*
* - Parameters:
* - events: An array of presence event types ``PresenceEventType`` to subscribe to.
* - bufferingPolicy: The ``BufferingPolicy`` for the created subscription.
*
* - Returns: A subscription `AsyncSequence` that can be used to iterate through ``PresenceEvent`` events.
*/
func subscribe(events: [PresenceEventType], bufferingPolicy: BufferingPolicy) -> SubscriptionAsyncSequence<PresenceEvent> {
let subscriptionAsyncSequence = SubscriptionAsyncSequence<PresenceEvent>(bufferingPolicy: bufferingPolicy)

let subscription = subscribe(events: events) { presence in
subscriptionAsyncSequence.emit(presence)
}

subscriptionAsyncSequence.addTerminationHandler {
Task { @MainActor in
subscription.unsubscribe()
}
}

return subscriptionAsyncSequence
}

/// Same as calling ``subscribe(bufferingPolicy:)`` with ``BufferingPolicy/unbounded``.
func subscribe() -> SubscriptionAsyncSequence<PresenceEvent> {
subscribe(bufferingPolicy: .unbounded)
}

/// Same as calling ``subscribe(event:bufferingPolicy:)`` with ``BufferingPolicy/unbounded``.
func subscribe(event: PresenceEventType) -> SubscriptionAsyncSequence<PresenceEvent> {
subscribe(event: event, bufferingPolicy: .unbounded)
}

/// Same as calling ``subscribe(events:bufferingPolicy:)`` with ``BufferingPolicy/unbounded``.
func subscribe(events: [PresenceEventType]) -> SubscriptionAsyncSequence<PresenceEvent> {
subscribe(events: events, bufferingPolicy: .unbounded)
}
}

/**
Expand Down
30 changes: 15 additions & 15 deletions Tests/AblyChatTests/DefaultPresenceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct DefaultPresenceTests {
@Test
func usersMayEnterPresence() async throws {
// Given
let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let defaultPresence = await DefaultPresence(
channel: channel,
Expand All @@ -34,7 +34,7 @@ struct DefaultPresenceTests {
@Test
func usersMayEnterPresenceWithoutData() async throws {
// Given
let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let defaultPresence = await DefaultPresence(
channel: channel,
Expand All @@ -58,7 +58,7 @@ struct DefaultPresenceTests {
@Test
func usersMayEnterPresenceWhileAttaching() async throws {
// Given
let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager()
let defaultPresence = await DefaultPresence(
Expand Down Expand Up @@ -86,7 +86,7 @@ struct DefaultPresenceTests {
let attachError = ARTErrorInfo(domain: "SomeDomain", code: 123)
let error = ARTErrorInfo(chatError: .roomTransitionedToInvalidStateForPresenceOperation(cause: attachError))

let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager(resultOfWaitToBeAbleToPerformPresenceOperations: .failure(error))
let defaultPresence = await DefaultPresence(
Expand Down Expand Up @@ -120,7 +120,7 @@ struct DefaultPresenceTests {
func failToEnterPresenceWhenRoomInInvalidState() async throws {
// Given
let error = ARTErrorInfo(chatError: .presenceOperationRequiresRoomAttach(feature: .presence))
let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager(resultOfWaitToBeAbleToPerformPresenceOperations: .failure(error))
let defaultPresence = await DefaultPresence(
Expand Down Expand Up @@ -150,7 +150,7 @@ struct DefaultPresenceTests {
@Test
func usersMayUpdatePresence() async throws {
// Given
let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager()
let defaultPresence = await DefaultPresence(
Expand All @@ -174,7 +174,7 @@ struct DefaultPresenceTests {
@Test
func usersMayUpdatePresenceWhileAttaching() async throws {
// Given
let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager()
let defaultPresence = await DefaultPresence(
Expand Down Expand Up @@ -202,7 +202,7 @@ struct DefaultPresenceTests {
let attachError = ARTErrorInfo(domain: "SomeDomain", code: 123)
let error = ARTErrorInfo(chatError: .roomTransitionedToInvalidStateForPresenceOperation(cause: attachError))

let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager(resultOfWaitToBeAbleToPerformPresenceOperations: .failure(error))
let defaultPresence = await DefaultPresence(
Expand Down Expand Up @@ -236,7 +236,7 @@ struct DefaultPresenceTests {
func failToUpdatePresenceWhenRoomInInvalidState() async throws {
// Given
let error = ARTErrorInfo(chatError: .presenceOperationRequiresRoomAttach(feature: .presence))
let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager(resultOfWaitToBeAbleToPerformPresenceOperations: .failure(error))
let defaultPresence = await DefaultPresence(
Expand Down Expand Up @@ -265,7 +265,7 @@ struct DefaultPresenceTests {
@Test
func usersMayLeavePresence() async throws {
// Given
let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager()
let defaultPresence = await DefaultPresence(
Expand All @@ -292,7 +292,7 @@ struct DefaultPresenceTests {
@Test
func ifUserIsPresent() async throws {
// Given
let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager()
let defaultPresence = await DefaultPresence(
Expand Down Expand Up @@ -320,7 +320,7 @@ struct DefaultPresenceTests {
@Test
func retrieveAllTheMembersOfThePresenceSet() async throws {
// Given
let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager()
let defaultPresence = await DefaultPresence(
Expand All @@ -346,7 +346,7 @@ struct DefaultPresenceTests {
func failToRetrieveAllTheMembersOfThePresenceSetWhenRoomInInvalidState() async throws {
// Given
let error = ARTErrorInfo(chatError: .presenceOperationRequiresRoomAttach(feature: .presence))
let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager(resultOfWaitToBeAbleToPerformPresenceOperations: .failure(error))
let defaultPresence = await DefaultPresence(
Expand All @@ -373,7 +373,7 @@ struct DefaultPresenceTests {
@Test
func retrieveAllTheMembersOfThePresenceSetWhileAttaching() async throws {
// Given
let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager()
let defaultPresence = await DefaultPresence(
Expand Down Expand Up @@ -401,7 +401,7 @@ struct DefaultPresenceTests {
let attachError = ARTErrorInfo(domain: "SomeDomain", code: 123)
let error = ARTErrorInfo(chatError: .roomTransitionedToInvalidStateForPresenceOperation(cause: attachError))

let channel = await MockRealtimeChannel(name: "basketball::$chat::$chatMessages")
let channel = await MockRealtimeChannel(name: "basketball::$chat")
let logger = TestLogger()
let roomLifecycleManager = await MockRoomLifecycleManager(resultOfWaitToBeAbleToPerformPresenceOperations: .failure(error))
let defaultPresence = await DefaultPresence(
Expand Down
9 changes: 0 additions & 9 deletions Tests/AblyChatTests/Helpers/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,6 @@ extension ARTPresenceMessage {
}
}

extension [PresenceEventType] {
static let all = [
PresenceEventType.present,
PresenceEventType.enter,
PresenceEventType.leave,
PresenceEventType.update,
]
}

/// Compares Any to another Any which is unavailable by default in swift for type safety, but useful to have in tests.
func compareAny(_ any1: Any?, with any2: Any?) -> Bool {
guard let any1, let any2 else {
Expand Down
4 changes: 2 additions & 2 deletions Tests/AblyChatTests/IntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,8 @@ struct IntegrationTests {

// MARK: - Presence

// (1) Subscribe to presence
let rxPresenceSubscription = rxRoom.presence.subscribe(events: [.enter, .leave, .update])
// (1) Subscribe to all presence events
let rxPresenceSubscription = rxRoom.presence.subscribe()

// (2) Send `.enter` presence event with custom data on the other client and check that we receive it on the subscription
try await txRoom.presence.enter(withData: ["randomData": "randomValue"])
Expand Down