diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 1a5b09c5..9e384815 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -139,6 +139,21 @@ jobs: - name: Build example app run: swift run BuildTool build-example-app --platform ${{ matrix.platform }} + check-documentation: + runs-on: macos-15 + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + # This step can be removed once the runners’ default version of Xcode is 16 or above + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: 16 + + - name: Build documentation + run: swift run BuildTool build-documentation + # We use this job as a marker that all of the required checks have completed. # This allows us to configure a single required status check in our branch # protection rules instead of having to type loads of different check names @@ -152,6 +167,7 @@ jobs: - check-spm - check-xcode - check-example-app + - check-documentation steps: - name: No-op diff --git a/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6e17fa1e..396b0e70 100644 --- a/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "1ad2d7338668d15feccbf564582941161acd47349bfca8f34374e11c69677ae8", + "originHash" : "1547951218aae39e26117fe3ca69bba0858841246ead50ef24e9592cdbcfa481", "pins" : [ { "identity" : "ably-cocoa", @@ -55,6 +55,24 @@ "version" : "1.1.2" } }, + { + "identity" : "swift-docc-plugin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-docc-plugin", + "state" : { + "revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64", + "version" : "1.4.3" + } + }, + { + "identity" : "swift-docc-symbolkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-docc-symbolkit", + "state" : { + "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", + "version" : "1.0.0" + } + }, { "identity" : "table", "kind" : "remoteSourceControl", diff --git a/Package.resolved b/Package.resolved index 30ea3e1d..1ebd6d6a 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "b6d25f160b01b473629481d68d4fe734b3981fcd87079531f784c2ade3afdc4d", + "originHash" : "19ff3d71167d4ac328655c520d3210baa89ea2c1b0900fb1b5ac5c770b42fa28", "pins" : [ { "identity" : "ably-cocoa", @@ -55,6 +55,24 @@ "version" : "1.1.2" } }, + { + "identity" : "swift-docc-plugin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-docc-plugin", + "state" : { + "revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64", + "version" : "1.4.3" + } + }, + { + "identity" : "swift-docc-symbolkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-docc-symbolkit", + "state" : { + "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", + "version" : "1.0.0" + } + }, { "identity" : "table", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index 06d664d4..b3407bb0 100644 --- a/Package.swift +++ b/Package.swift @@ -35,6 +35,10 @@ let package = Package( url: "https://github.com/JanGorman/Table.git", from: "1.1.1" ), + .package( + url: "https://github.com/apple/swift-docc-plugin", + from: "1.0.0" + ), ], targets: [ .target( diff --git a/Sources/AblyChat/ChatClient.swift b/Sources/AblyChat/ChatClient.swift index 8fd9d86e..8dfa7c2a 100644 --- a/Sources/AblyChat/ChatClient.swift +++ b/Sources/AblyChat/ChatClient.swift @@ -1,15 +1,48 @@ import Ably public protocol ChatClient: AnyObject, Sendable { + /** + * Returns the rooms object, which provides access to chat rooms. + * + * - Returns: The rooms object. + */ var rooms: any Rooms { get } + + /** + * Returns the underlying connection to Ably, which can be used to monitor the clients + * connection to Ably servers. + * + * - Returns: The connection object. + */ var connection: any Connection { get } + + /** + * Returns the clientId of the current client. + * + * - Returns: The clientId. + */ var clientID: String { get } + + /** + * Returns the underlying Ably Realtime client. + * + * - Returns: The Ably Realtime client. + */ var realtime: RealtimeClient { get } + + /** + * Returns the resolved client options for the client, including any defaults that have been set. + * + * - Returns: The client options. + */ var clientOptions: ClientOptions { get } } public typealias RealtimeClient = any RealtimeClientProtocol +/** + * This is the core client for Ably chat. It provides access to chat rooms. + */ public actor DefaultChatClient: ChatClient { public let realtime: RealtimeClient public nonisolated let clientOptions: ClientOptions @@ -20,6 +53,13 @@ public actor DefaultChatClient: ChatClient { // (CHA-CS4) The chat client must allow its connection status to be observed by clients. public nonisolated let connection: any Connection + /** + * Constructor for Chat + * + * - Parameters: + * - realtime: The Ably Realtime client. + * - clientOptions: The client options. + */ public init(realtime: RealtimeClient, clientOptions: ClientOptions?) { self.realtime = realtime self.clientOptions = clientOptions ?? .init() @@ -34,8 +74,22 @@ public actor DefaultChatClient: ChatClient { } } +/** + * Configuration options for the chat client. + */ public struct ClientOptions: Sendable { + /** + * A custom log handler that will be used to log messages from the client. + * + * By default, the client will log messages to the console. + */ public var logHandler: LogHandler? + + /** + * The minimum log level at which messages will be logged. + * + * By default, LogLevel.error will be used. + */ public var logLevel: LogLevel? public init(logHandler: (any LogHandler)? = nil, logLevel: LogLevel? = nil) { diff --git a/Sources/AblyChat/Connection.swift b/Sources/AblyChat/Connection.swift index 41d0f89f..57f2ecdf 100644 --- a/Sources/AblyChat/Connection.swift +++ b/Sources/AblyChat/Connection.swift @@ -1,24 +1,72 @@ import Ably +/** + * Represents a connection to Ably. + */ public protocol Connection: AnyObject, Sendable { + /** + * The current status of the connection. + */ var status: ConnectionStatus { get async } + // TODO: (https://github.com/ably-labs/ably-chat-swift/issues/12): consider how to avoid the need for an unwrap + /** + * The current error, if any, that caused the connection to enter the current status. + */ var error: ARTErrorInfo? { get async } + + /** + * Registers a listener that will be called whenever the connection status changes. + * @param listener The function to call when the status changes. + * @returns An object that can be used to unregister the listener. + */ func onStatusChange(bufferingPolicy: BufferingPolicy) -> Subscription } +/** + * The different states that the connection can be in through its lifecycle. + */ public enum ConnectionStatus: Sendable { // (CHA-CS1a) The INITIALIZED status is a default status when the realtime client is first initialized. This value will only (likely) be seen if the realtime client doesn’t have autoconnect turned on. + + /** + * A temporary state for when the library is first initialized. + */ case initialized + // (CHA-CS1b) The CONNECTING status is used when the client is in the process of connecting to Ably servers. + + /** + * The library is currently connecting to Ably. + */ case connecting + // (CHA-CS1c) The CONNECTED status is used when the client connected to Ably servers. + + /** + * The library is currently connected to Ably. + */ case connected + // (CHA-CS1d) The DISCONNECTED status is used when the client is not currently connected to Ably servers. This state may be temporary as the underlying Realtime SDK seeks to reconnect. + + /** + * The library is currently disconnected from Ably, but will attempt to reconnect. + */ case disconnected + // (CHA-CS1e) The SUSPENDED status is used when the client is in an extended state of disconnection, but will attempt to reconnect. + + /** + * The library is in an extended state of disconnection, but will attempt to reconnect. + */ case suspended + // (CHA-CS1f) The FAILED status is used when the client is disconnected from the Ably servers due to some non-retriable failure such as authentication failure. It will not attempt to reconnect. + + /** + * The library is currently disconnected from Ably and will not attempt to reconnect. + */ case failed internal init(from realtimeConnectionState: ARTRealtimeConnectionState) { @@ -41,11 +89,30 @@ public enum ConnectionStatus: Sendable { } } +/** + * Represents a change in the status of the connection. + */ public struct ConnectionStatusChange: Sendable { + /** + * The new status of the connection. + */ public var current: ConnectionStatus + + /** + * The previous status of the connection. + */ public var previous: ConnectionStatus + // TODO: (https://github.com/ably-labs/ably-chat-swift/issues/12): consider how to avoid the need for an unwrap + /** + * An error that provides a reason why the connection has + * entered the new status, if applicable. + */ public var error: ARTErrorInfo? + + /** + * The time in milliseconds that the client will wait before attempting to reconnect. + */ public var retryIn: TimeInterval public init(current: ConnectionStatus, previous: ConnectionStatus, error: ARTErrorInfo? = nil, retryIn: TimeInterval) { diff --git a/Sources/AblyChat/DefaultMessages.swift b/Sources/AblyChat/DefaultMessages.swift index 5c2fea1b..179999fa 100644 --- a/Sources/AblyChat/DefaultMessages.swift +++ b/Sources/AblyChat/DefaultMessages.swift @@ -234,10 +234,11 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities { case .failed, .suspended: // TODO: Revisit as part of https://github.com/ably-labs/ably-chat-swift/issues/32 logger.log(message: "Channel failed to attach", level: .error) + let errorCodeCase = ErrorCode.CaseThatImpliesFixedStatusCode.messagesAttachmentFailed nillableContinuation?.resume( throwing: ARTErrorInfo.create( - withCode: ErrorCode.messagesAttachmentFailed.rawValue, - status: ErrorCode.messagesAttachmentFailed.statusCode, + withCode: errorCodeCase.toNumericErrorCode.rawValue, + status: errorCodeCase.statusCode, message: "Channel failed to attach" ) ) diff --git a/Sources/AblyChat/Dependencies.swift b/Sources/AblyChat/Dependencies.swift index 28c0891d..13cff5f8 100644 --- a/Sources/AblyChat/Dependencies.swift +++ b/Sources/AblyChat/Dependencies.swift @@ -14,7 +14,7 @@ public protocol RealtimeClientProtocol: ARTRealtimeProtocol, Sendable { var connection: Connection { get } } -/// Expresses the requirements of the object returned by ``RealtimeClientProtocol.channels``. +/// Expresses the requirements of the object returned by ``RealtimeClientProtocol/channels``. public protocol RealtimeChannelsProtocol: ARTRealtimeChannelsProtocol, Sendable { associatedtype Channel: RealtimeChannelProtocol @@ -22,7 +22,7 @@ public protocol RealtimeChannelsProtocol: ARTRealtimeChannelsProtocol, Sendable func get(_ name: String, options: ARTRealtimeChannelOptions) -> Channel } -/// Expresses the requirements of the object returned by ``RealtimeChannelsProtocol.get(_:)``. +/// Expresses the requirements of the object returned by ``RealtimeChannelsProtocol/get(_:options:)``. public protocol RealtimeChannelProtocol: ARTRealtimeChannelProtocol, Sendable {} public protocol ConnectionProtocol: ARTConnectionProtocol, Sendable {} diff --git a/Sources/AblyChat/Errors.swift b/Sources/AblyChat/Errors.swift index c955e181..5d7589bd 100644 --- a/Sources/AblyChat/Errors.swift +++ b/Sources/AblyChat/Errors.swift @@ -1,9 +1,9 @@ import Ably /** - The error domain used for the ``Ably.ARTErrorInfo`` error instances thrown by the Ably Chat SDK. + The error domain used for the `ARTErrorInfo` error instances thrown by the Ably Chat SDK. - See ``ErrorCode`` for the possible ``ARTErrorInfo.code`` values. + See ``ErrorCode`` for the possible `code` values. */ public let errorDomain = "AblyChatErrorDomain" @@ -11,9 +11,7 @@ public let errorDomain = "AblyChatErrorDomain" The error codes for errors in the ``errorDomain`` error domain. */ public enum ErrorCode: Int { - case nonspecific = 40000 - - /// ``Rooms.get(roomID:options:)`` was called with a different set of room options than was used on a previous call. You must first release the existing room instance using ``Rooms.release(roomID:)``. + /// ``Rooms/get(roomID:options:)`` was called with a different set of room options than was used on a previous call. You must first release the existing room instance using ``Rooms/release(roomID:)``. /// /// TODO this code is a guess, revisit in https://github.com/ably-labs/ably-chat-swift/issues/32 case inconsistentRoomOptions = 1 @@ -36,30 +34,118 @@ public enum ErrorCode: Int { case roomInInvalidState = 102_107 - /// The ``ARTErrorInfo.statusCode`` that should be returned for this error. + /// Has a case for each of the ``ErrorCode`` cases that imply a fixed status code. + internal enum CaseThatImpliesFixedStatusCode { + case inconsistentRoomOptions + case messagesAttachmentFailed + case presenceAttachmentFailed + case reactionsAttachmentFailed + case occupancyAttachmentFailed + case typingAttachmentFailed + case messagesDetachmentFailed + case presenceDetachmentFailed + case reactionsDetachmentFailed + case occupancyDetachmentFailed + case typingDetachmentFailed + case roomInFailedState + case roomIsReleasing + case roomIsReleased + + internal var toNumericErrorCode: ErrorCode { + switch self { + case .inconsistentRoomOptions: + .inconsistentRoomOptions + case .messagesAttachmentFailed: + .messagesAttachmentFailed + case .presenceAttachmentFailed: + .presenceAttachmentFailed + case .reactionsAttachmentFailed: + .reactionsAttachmentFailed + case .occupancyAttachmentFailed: + .occupancyAttachmentFailed + case .typingAttachmentFailed: + .typingAttachmentFailed + case .messagesDetachmentFailed: + .messagesDetachmentFailed + case .presenceDetachmentFailed: + .presenceDetachmentFailed + case .reactionsDetachmentFailed: + .reactionsDetachmentFailed + case .occupancyDetachmentFailed: + .occupancyDetachmentFailed + case .typingDetachmentFailed: + .typingDetachmentFailed + case .roomInFailedState: + .roomInFailedState + case .roomIsReleasing: + .roomIsReleasing + case .roomIsReleased: + .roomIsReleased + } + } + + /// The ``ARTErrorInfo/statusCode`` that should be returned for this error. + internal var statusCode: Int { + // These status codes are taken from the "Chat-specific Error Codes" section of the spec. + switch self { + case .inconsistentRoomOptions, + .roomInFailedState, + .roomIsReleasing, + .roomIsReleased: + 400 + case + .messagesAttachmentFailed, + .presenceAttachmentFailed, + .reactionsAttachmentFailed, + .occupancyAttachmentFailed, + .typingAttachmentFailed, + .messagesDetachmentFailed, + .presenceDetachmentFailed, + .reactionsDetachmentFailed, + .occupancyDetachmentFailed, + .typingDetachmentFailed: + 500 + } + } + } + + /// Has a case for each of the ``ErrorCode`` cases that do not imply a fixed status code. + internal enum CaseThatImpliesVariableStatusCode { + case roomInInvalidState + + internal var toNumericErrorCode: ErrorCode { + switch self { + case .roomInInvalidState: + .roomInInvalidState + } + } + } +} + +/** + * Represents a case of ``ErrorCode`` plus a status code. + */ +internal enum ErrorCodeAndStatusCode { + case fixedStatusCode(ErrorCode.CaseThatImpliesFixedStatusCode) + case variableStatusCode(ErrorCode.CaseThatImpliesVariableStatusCode, statusCode: Int) + + /// The ``ARTErrorInfo/code`` that should be returned for this error. + internal var code: ErrorCode { + switch self { + case let .fixedStatusCode(code): + code.toNumericErrorCode + case let .variableStatusCode(code, _): + code.toNumericErrorCode + } + } + + /// The ``ARTErrorInfo/statusCode`` that should be returned for this error. internal var statusCode: Int { - // These status codes are taken from the "Chat-specific Error Codes" section of the spec. switch self { - case .nonspecific, - .inconsistentRoomOptions, - .roomInFailedState, - .roomIsReleasing, - .roomIsReleased: - 400 - case - .messagesAttachmentFailed, - .presenceAttachmentFailed, - .reactionsAttachmentFailed, - .occupancyAttachmentFailed, - .typingAttachmentFailed, - .messagesDetachmentFailed, - .presenceDetachmentFailed, - .reactionsDetachmentFailed, - .occupancyDetachmentFailed, - .typingDetachmentFailed, - // CHA-RL9c - .roomInInvalidState: - 500 + case let .fixedStatusCode(code): + code.statusCode + case let .variableStatusCode(_, statusCode): + statusCode } } } @@ -77,51 +163,50 @@ internal enum ChatError { case roomIsReleasing case roomIsReleased case presenceOperationRequiresRoomAttach(feature: RoomFeature) - case presenceOperationDisallowedForCurrentRoomStatus(feature: RoomFeature) - case roomInInvalidState(cause: ARTErrorInfo?) + case roomTransitionedToInvalidStateForPresenceOperation(cause: ARTErrorInfo?) - /// The ``ARTErrorInfo.code`` that should be returned for this error. - internal var code: ErrorCode { + internal var codeAndStatusCode: ErrorCodeAndStatusCode { switch self { case .inconsistentRoomOptions: - .inconsistentRoomOptions + .fixedStatusCode(.inconsistentRoomOptions) case let .attachmentFailed(feature, _): switch feature { case .messages: - .messagesAttachmentFailed + .fixedStatusCode(.messagesAttachmentFailed) case .occupancy: - .occupancyAttachmentFailed + .fixedStatusCode(.occupancyAttachmentFailed) case .presence: - .presenceAttachmentFailed + .fixedStatusCode(.presenceAttachmentFailed) case .reactions: - .reactionsAttachmentFailed + .fixedStatusCode(.reactionsAttachmentFailed) case .typing: - .typingAttachmentFailed + .fixedStatusCode(.typingAttachmentFailed) } case let .detachmentFailed(feature, _): switch feature { case .messages: - .messagesDetachmentFailed + .fixedStatusCode(.messagesDetachmentFailed) case .occupancy: - .occupancyDetachmentFailed + .fixedStatusCode(.occupancyDetachmentFailed) case .presence: - .presenceDetachmentFailed + .fixedStatusCode(.presenceDetachmentFailed) case .reactions: - .reactionsDetachmentFailed + .fixedStatusCode(.reactionsDetachmentFailed) case .typing: - .typingDetachmentFailed + .fixedStatusCode(.typingDetachmentFailed) } case .roomInFailedState: - .roomInFailedState + .fixedStatusCode(.roomInFailedState) case .roomIsReleasing: - .roomIsReleasing + .fixedStatusCode(.roomIsReleasing) case .roomIsReleased: - .roomIsReleased - case .roomInInvalidState: - .roomInInvalidState - case .presenceOperationRequiresRoomAttach, - .presenceOperationDisallowedForCurrentRoomStatus: - .nonspecific + .fixedStatusCode(.roomIsReleased) + case .roomTransitionedToInvalidStateForPresenceOperation: + // CHA-RL9c + .variableStatusCode(.roomInInvalidState, statusCode: 500) + case .presenceOperationRequiresRoomAttach: + // CHA-PR3h, CHA-PR10h, CHA-PR6h, CHA-T2g + .variableStatusCode(.roomInInvalidState, statusCode: 400) } } @@ -160,7 +245,7 @@ internal enum ChatError { return "The \(descriptionOfFeature(feature)) feature failed to \(operationDescription)." } - /// The ``ARTErrorInfo.localizedDescription`` that should be returned for this error. + /// The ``ARTErrorInfo/localizedDescription`` that should be returned for this error. internal var localizedDescription: String { switch self { case let .inconsistentRoomOptions(requested, existing): @@ -177,28 +262,25 @@ internal enum ChatError { "Cannot perform operation because the room is in a released state." case let .presenceOperationRequiresRoomAttach(feature): "To perform this \(Self.descriptionOfFeature(feature)) operation, you must first attach the room." - case let .presenceOperationDisallowedForCurrentRoomStatus(feature): - "This \(Self.descriptionOfFeature(feature)) operation can not be performed given the current room status." - case .roomInInvalidState: + case .roomTransitionedToInvalidStateForPresenceOperation: "The room operation failed because the room was in an invalid state." } } - /// The ``ARTErrorInfo.cause`` that should be returned for this error. + /// The ``ARTErrorInfo/cause`` that should be returned for this error. internal var cause: ARTErrorInfo? { switch self { case let .attachmentFailed(_, underlyingError): underlyingError case let .detachmentFailed(_, underlyingError): underlyingError - case let .roomInInvalidState(cause): + case let .roomTransitionedToInvalidStateForPresenceOperation(cause): cause case .inconsistentRoomOptions, .roomInFailedState, .roomIsReleasing, .roomIsReleased, - .presenceOperationRequiresRoomAttach, - .presenceOperationDisallowedForCurrentRoomStatus: + .presenceOperationRequiresRoomAttach: nil } } @@ -208,7 +290,7 @@ internal extension ARTErrorInfo { convenience init(chatError: ChatError) { var userInfo: [String: Any] = [:] // TODO: copied and pasted from implementation of -[ARTErrorInfo createWithCode:status:message:requestId:] because there’s no way to pass domain; revisit in https://github.com/ably-labs/ably-chat-swift/issues/32. Also the ARTErrorInfoStatusCode variable in ably-cocoa is not public. - userInfo["ARTErrorInfoStatusCode"] = chatError.code.statusCode + userInfo["ARTErrorInfoStatusCode"] = chatError.codeAndStatusCode.statusCode userInfo[NSLocalizedDescriptionKey] = chatError.localizedDescription // TODO: This is kind of an implementation detail (that NSUnderlyingErrorKey is what populates `cause`); consider documenting in ably-cocoa as part of https://github.com/ably-labs/ably-chat-swift/issues/32. @@ -218,7 +300,7 @@ internal extension ARTErrorInfo { self.init( domain: errorDomain, - code: chatError.code.rawValue, + code: chatError.codeAndStatusCode.code.rawValue, userInfo: userInfo ) } diff --git a/Sources/AblyChat/Headers.swift b/Sources/AblyChat/Headers.swift index e64a12fa..3824cbbd 100644 --- a/Sources/AblyChat/Headers.swift +++ b/Sources/AblyChat/Headers.swift @@ -10,4 +10,19 @@ public enum HeadersValue: Sendable, Codable, Equatable { // The corresponding type in TypeScript is // Record // There may be a better way to represent it in Swift; this will do for now. Have omitted `undefined` because I don’t know how that would occur. + +/** + * Headers are a flat key-value map that can be attached to chat messages. + * + * The headers are a flat key-value map and are sent as part of the realtime + * message's extras inside the `headers` property. They can serve similar + * purposes as Metadata but as opposed to Metadata they are read by Ably and + * can be used for features such as + * [subscription filters](https://faqs.ably.com/subscription-filters). + * + * Do not use the headers for authoritative information. There is no + * server-side validation. When reading the headers treat them like user + * input. + * + */ public typealias Headers = [String: HeadersValue] diff --git a/Sources/AblyChat/Message.swift b/Sources/AblyChat/Message.swift index a9053b7c..85c4a60a 100644 --- a/Sources/AblyChat/Message.swift +++ b/Sources/AblyChat/Message.swift @@ -1,6 +1,13 @@ import Foundation +/** + * {@link Headers} type for chat messages. + */ public typealias MessageHeaders = Headers + +/** + * {@link Metadata} type for chat messages. + */ public typealias MessageMetadata = Metadata // (CHA-M2) A Message corresponds to a single message in a chat room. This is analogous to a single user-specified message on an Ably channel (NOTE: not a ProtocolMessage). diff --git a/Sources/AblyChat/Messages.swift b/Sources/AblyChat/Messages.swift index b880b83c..1ea6096a 100644 --- a/Sources/AblyChat/Messages.swift +++ b/Sources/AblyChat/Messages.swift @@ -1,15 +1,89 @@ import Ably +/** + * This interface is used to interact with messages in a chat room: subscribing + * to new messages, fetching history, or sending messages. + * + * Get an instance via {@link Room.messages}. + */ public protocol Messages: AnyObject, Sendable, EmitsDiscontinuities { + /** + * Subscribe to new messages in this chat room. + * @param listener callback that will be called + * @returns A response object that allows you to control the subscription. + */ func subscribe(bufferingPolicy: BufferingPolicy) async throws -> MessageSubscription + + /** + * Get messages that have been previously sent to the chat room, based on the provided options. + * + * @param options Options for the query. + * @returns A promise that resolves with the paginated result of messages. This paginated result can + * be used to fetch more messages if available. + */ func get(options: QueryOptions) async throws -> any PaginatedResult + + /** + * Send a message in the chat room. + * + * This method uses the Ably Chat API endpoint for sending messages. + * + * Note that the Promise may resolve before OR after the message is received + * from the realtime channel. This means you may see the message that was just + * sent in a callback to `subscribe` before the returned promise resolves. + * + * @param params an object containing {text, headers, metadata} for the message + * to be sent. Text is required, metadata and headers are optional. + * @returns A promise that resolves when the message was published. + */ func send(params: SendMessageParams) async throws -> Message + + /** + * Get the underlying Ably realtime channel used for the messages in this chat room. + * + * @returns The realtime channel. + */ var channel: RealtimeChannelProtocol { get } } +/** + * Params for sending a text message. Only `text` is mandatory. + */ public struct SendMessageParams: Sendable { + /** + * The text of the message. + */ public var text: String + + /** + * Optional metadata of the message. + * + * The metadata is a map of extra information that can be attached to chat + * messages. It is not used by Ably and is sent as part of the realtime + * message payload. Example use cases are setting custom styling like + * background or text colors or fonts, adding links to external images, + * emojis, etc. + * + * Do not use metadata for authoritative information. There is no server-side + * validation. When reading the metadata treat it like user input. + * + */ public var metadata: MessageMetadata? + + /** + * Optional headers of the message. + * + * The headers are a flat key-value map and are sent as part of the realtime + * message's extras inside the `headers` property. They can serve similar + * purposes as the metadata but they are read by Ably and can be used for + * features such as + * [subscription filters](https://faqs.ably.com/subscription-filters). + * + * Do not use the headers for authoritative information. There is no + * server-side validation. When reading the headers treat them like user + * input. + * + */ public var headers: MessageHeaders? public init(text: String, metadata: MessageMetadata? = nil, headers: MessageHeaders? = nil) { @@ -19,15 +93,46 @@ public struct SendMessageParams: Sendable { } } +/** + * Options for querying messages in a chat room. + */ public struct QueryOptions: Sendable { public enum ResultOrder: Sendable { case oldestFirst case newestFirst } + /** + * The start of the time window to query from. If provided, the response will include + * messages with timestamps equal to or greater than this value. + * + * @defaultValue The beginning of time + */ public var start: Date? + + /** + * The end of the time window to query from. If provided, the response will include + * messages with timestamps less than this value. + * + * @defaultValue Now + */ public var end: Date? + + /** + * The maximum number of messages to return in the response. + * + * @defaultValue 100 + */ public var limit: Int? + + /** + * The direction to query messages in. + * If `forwards`, the response will include messages from the start of the time window to the end. + * If `backwards`, the response will include messages from the end of the time window to the start. + * If not provided, the default is `forwards`. + * + * @defaultValue forwards + */ public var orderBy: ResultOrder? // (CHA-M5g) The subscribers subscription point must be additionally specified (internally, by us) in the fromSerial query parameter. diff --git a/Sources/AblyChat/Metadata.swift b/Sources/AblyChat/Metadata.swift index 85bc6862..53484960 100644 --- a/Sources/AblyChat/Metadata.swift +++ b/Sources/AblyChat/Metadata.swift @@ -8,4 +8,15 @@ public enum MetadataValue: Sendable, Codable, Equatable { case null } +/** + * Metadata is a map of extra information that can be attached to chat + * messages. It is not used by Ably and is sent as part of the realtime + * message payload. Example use cases are setting custom styling like + * background or text colors or fonts, adding links to external images, + * emojis, etc. + * + * Do not use metadata for authoritative information. There is no server-side + * validation. When reading the metadata treat it like user input. + * + */ public typealias Metadata = [String: MetadataValue?] diff --git a/Sources/AblyChat/Presence.swift b/Sources/AblyChat/Presence.swift index 7c58ba88..56a3d1f4 100644 --- a/Sources/AblyChat/Presence.swift +++ b/Sources/AblyChat/Presence.swift @@ -166,7 +166,7 @@ public struct PresenceEvent: Sendable { // This is a Sendable equivalent of ably-cocoa’s ARTRealtimePresenceQuery type. // -// Originally, ``Presence.get(params:)`` accepted an ARTRealtimePresenceQuery object, but I’ve changed it to accept this type, because else when you try and write an actor that implements ``Presence``, you get a compiler error like "Non-sendable type 'ARTRealtimePresenceQuery' in parameter of the protocol requirement satisfied by actor-isolated instance method 'get(params:)' cannot cross actor boundary; this is an error in the Swift 6 language mode". +// Originally, ``Presence/get(params:)`` accepted an ARTRealtimePresenceQuery object, but I’ve changed it to accept this type, because else when you try and write an actor that implements ``Presence``, you get a compiler error like "Non-sendable type 'ARTRealtimePresenceQuery' in parameter of the protocol requirement satisfied by actor-isolated instance method 'get(params:)' cannot cross actor boundary; this is an error in the Swift 6 language mode". // // Now, based on my limited understanding, you _should_ be able to send non-Sendable values from one isolation domain to another (the purpose of the "region-based isolation" and "`sending` parameters" features added in Swift 6), but to get this to work I had to mark ``Presence`` as requiring conformance to the `Actor` protocol, and since I didn’t understand _why_ I had to do that, I didn’t want to put it in the public API. // diff --git a/Sources/AblyChat/Reaction.swift b/Sources/AblyChat/Reaction.swift index 9f6bd720..4e9f9cee 100644 --- a/Sources/AblyChat/Reaction.swift +++ b/Sources/AblyChat/Reaction.swift @@ -1,15 +1,49 @@ import Foundation +/** + * {@link Headers} type for chat messages. + */ public typealias ReactionHeaders = Headers + +/** + * {@link Metadata} type for chat messages. + */ public typealias ReactionMetadata = Metadata // (CHA-ER2) A Reaction corresponds to a single reaction in a chat room. This is analogous to a single user-specified message on an Ably channel (NOTE: not a ProtocolMessage). + +/** + * Represents a room-level reaction. + */ public struct Reaction: Sendable { + /** + * The type of the reaction, for example "like" or "love". + */ public var type: String + + /** + * Metadata of the reaction. If no metadata was set this is an empty object. + */ public var metadata: ReactionMetadata + + /** + * Headers of the reaction. If no headers were set this is an empty object. + */ public var headers: ReactionHeaders + + /** + * The timestamp at which the reaction was sent. + */ public var createdAt: Date + + /** + * The clientId of the user who sent the reaction. + */ public var clientID: String + + /** + * Whether the reaction was sent by the current user. + */ public var isSelf: Bool public init(type: String, metadata: ReactionMetadata, headers: ReactionHeaders, createdAt: Date, clientID: String, isSelf: Bool) { diff --git a/Sources/AblyChat/RoomFeature.swift b/Sources/AblyChat/RoomFeature.swift index 5b9968f8..6366374e 100644 --- a/Sources/AblyChat/RoomFeature.swift +++ b/Sources/AblyChat/RoomFeature.swift @@ -41,12 +41,11 @@ internal protocol FeatureChannel: Sendable, EmitsDiscontinuities { /// Waits until we can perform presence operations on the contributors of this room without triggering an implicit attach. /// - /// Implements the checks described by CHA-PR3d, CHA-PR3e, CHA-PR3f, and CHA-PR3g (and similar ones described by other functionality that performs contributor presence operations). Namely: + /// Implements the checks described by CHA-PR3d, CHA-PR3e, and CHA-PR3h (and similar ones described by other functionality that performs contributor presence operations). Namely: /// - /// - CHA-RL9, which is invoked by CHA-PR3d, CHA-PR10d, CHA-PR6c, CHA-T2c: If the room is in the ATTACHING status, it waits for the next room status change. If the new status is ATTACHED, it returns. Else, it throws an `ARTErrorInfo` derived from ``ChatError.roomInInvalidState(cause:)``. - /// - CHA-PR3e, CHA-PR11e, CHA-PR6d, CHA-T2d: If the room is in the ATTACHED status, it returns immediately. - /// - CHA-PR3f, CHA-PR11f, CHA-PR6e, CHA-T2e: If the room is in the DETACHED status, it throws an `ARTErrorInfo` derived from ``ChatError.presenceOperationRequiresRoomAttach(feature:)``. - /// - // CHA-PR3g, CHA-PR11g, CHA-PR6f, CHA-T2f: If the room is in any other status, it throws an `ARTErrorInfo` derived from ``ChatError.presenceOperationDisallowedForCurrentRoomStatus(feature:)``. + /// - CHA-RL9, which is invoked by CHA-PR3d, CHA-PR10d, CHA-PR6c, CHA-T2c: If the room is in the ATTACHING status, it waits for the next room status change. If the new status is ATTACHED, it returns. Else, it throws an `ARTErrorInfo` derived from ``ChatError/roomTransitionedToInvalidStateForPresenceOperation(cause:)``. + /// - CHA-PR3e, CHA-PR10e, CHA-PR6d, CHA-T2d: If the room is in the ATTACHED status, it returns immediately. + /// - CHA-PR3h, CHA-PR10h, CHA-PR6h, CHA-T2g: If the room is in any other status, it throws an `ARTErrorInfo` derived from ``ChatError/presenceOperationRequiresRoomAttach(feature:)``. /// /// - Parameters: /// - requester: The room feature that wishes to perform a presence operation. This is only used for customising the message of the thrown error. diff --git a/Sources/AblyChat/RoomLifecycleManager.swift b/Sources/AblyChat/RoomLifecycleManager.swift index de05474e..a596fca1 100644 --- a/Sources/AblyChat/RoomLifecycleManager.swift +++ b/Sources/AblyChat/RoomLifecycleManager.swift @@ -18,7 +18,7 @@ internal protocol RoomLifecycleContributorChannel: Sendable { var state: ARTRealtimeChannelState { get async } var errorReason: ARTErrorInfo? { get async } - /// Equivalent to subscribing to a `RealtimeChannelProtocol` object’s state changes via its `on(_:)` method. The subscription should use the ``BufferingPolicy.unbounded`` buffering policy. + /// Equivalent to subscribing to a `RealtimeChannelProtocol` object’s state changes via its `on(_:)` method. The subscription should use the ``BufferingPolicy/unbounded`` buffering policy. /// /// It is marked as `async` purely to make it easier to write mocks for this method (i.e. to use an actor as a mock). func subscribeToState() async -> Subscription @@ -84,7 +84,7 @@ internal actor DefaultRoomLifecycleManager! // TODO: clean up old subscriptions (https://github.com/ably-labs/ably-chat-swift/issues/36) @@ -387,7 +387,7 @@ internal actor DefaultRoomLifecycleManager Subscription { @@ -1225,17 +1225,14 @@ internal actor DefaultRoomLifecycleManager Subscription } +/** + * Params for sending a room-level reactions. Only `type` is mandatory. + */ public struct SendReactionParams: Sendable { + /** + * The type of the reaction, for example an emoji or a short string such as + * "like". + * + * It is the only mandatory parameter to send a room-level reaction. + */ public var type: String + + /** + * Optional metadata of the reaction. + * + * The metadata is a map of extra information that can be attached to the + * room reaction. It is not used by Ably and is sent as part of the realtime + * message payload. Example use cases are custom animations or other effects. + * + * Do not use metadata for authoritative information. There is no server-side + * validation. When reading the metadata treat it like user input. + * + */ public var metadata: ReactionMetadata? + + /** + * Optional headers of the room reaction. + * + * The headers are a flat key-value map and are sent as part of the realtime + * message's `extras` inside the `headers` property. They can serve similar + * purposes as the metadata but they are read by Ably and can be used for + * features such as + * [subscription filters](https://faqs.ably.com/subscription-filters). + * + * Do not use the headers for authoritative information. There is no + * server-side validation. When reading the headers treat them like user + * input. + * + */ public var headers: ReactionHeaders? public init(type: String, metadata: ReactionMetadata? = nil, headers: ReactionHeaders? = nil) { diff --git a/Sources/BuildTool/BuildTool.swift b/Sources/BuildTool/BuildTool.swift index 6591e42e..4b1b8652 100644 --- a/Sources/BuildTool/BuildTool.swift +++ b/Sources/BuildTool/BuildTool.swift @@ -14,6 +14,7 @@ struct BuildTool: AsyncParsableCommand { GenerateMatrices.self, Lint.self, SpecCoverage.self, + BuildDocumentation.self, ] ) } @@ -616,7 +617,7 @@ struct SpecCoverage: AsyncParsableCommand { var name: String /** - * The path of this target’s sources, relative to ``PackageDescribeOutput.path``. + * The path of this target’s sources, relative to ``PackageDescribeOutput/path``. */ var path: String @@ -650,3 +651,38 @@ struct SpecCoverage: AsyncParsableCommand { } } } + +@available(macOS 14, *) +struct BuildDocumentation: AsyncParsableCommand { + static let configuration = CommandConfiguration( + abstract: "Build documentation for the library" + ) + + mutating func run() async throws { + // For now, this is intended to just perform some validation of the documentation comments. We’ll generate HTML output in https://github.com/ably/ably-chat-swift/issues/2. + + try await ProcessRunner.run( + executableName: "swift", + arguments: [ + "package", + "generate-documentation", + + "--product", "AblyChat", + + // Useful because it alerts us about links to nonexistent symbols. + "--warnings-as-errors", + + // Outputs the following information about which symbols have been documented and to what level of detail: + // + // - a table at the end of the CLI output + // - as a JSON file in ./.build/plugins/Swift-DocC/outputs/AblyChat.doccarchive/documentation-coverage.json + // + // I do not yet know how to make use of these (there’s all sorts of unexpected symbols that we didn’t directly declare there, e.g. `compactMap(_:)`), but maybe it’ll be a bit helpful still. + "--experimental-documentation-coverage", + + // Increases the detail level of the aforementioned coverage table in CLI output. + "--coverage-summary-level", "detailed", + ] + ) + } +} diff --git a/Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift b/Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift index b6cc7bf9..3d1dedc8 100644 --- a/Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift +++ b/Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift @@ -145,7 +145,7 @@ struct DefaultRoomLifecycleManagerTests { await #expect { try await manager.performAttachOperation() } throws: { error in - isChatError(error, withCode: .roomIsReleasing) + isChatError(error, withCodeAndStatusCode: .fixedStatusCode(.roomIsReleasing)) } } @@ -160,7 +160,7 @@ struct DefaultRoomLifecycleManagerTests { await #expect { try await manager.performAttachOperation() } throws: { error in - isChatError(error, withCode: .roomIsReleased) + isChatError(error, withCodeAndStatusCode: .fixedStatusCode(.roomIsReleased)) } } @@ -373,7 +373,7 @@ struct DefaultRoomLifecycleManagerTests { } for error in await [suspendedRoomStatusChange.error, manager.roomStatus.error, roomAttachError] { - #expect(isChatError(error, withCode: .messagesAttachmentFailed, cause: contributorAttachError)) + #expect(isChatError(error, withCodeAndStatusCode: .fixedStatusCode(.messagesAttachmentFailed), cause: contributorAttachError)) } // and: @@ -454,7 +454,7 @@ struct DefaultRoomLifecycleManagerTests { } for error in await [failedStatusChange.error, manager.roomStatus.error, roomAttachError] { - #expect(isChatError(error, withCode: .messagesAttachmentFailed, cause: contributorAttachError)) + #expect(isChatError(error, withCodeAndStatusCode: .fixedStatusCode(.messagesAttachmentFailed), cause: contributorAttachError)) } } @@ -561,7 +561,7 @@ struct DefaultRoomLifecycleManagerTests { await #expect { try await manager.performDetachOperation() } throws: { error in - isChatError(error, withCode: .roomIsReleasing) + isChatError(error, withCodeAndStatusCode: .fixedStatusCode(.roomIsReleasing)) } } @@ -576,7 +576,7 @@ struct DefaultRoomLifecycleManagerTests { await #expect { try await manager.performDetachOperation() } throws: { error in - isChatError(error, withCode: .roomIsReleased) + isChatError(error, withCodeAndStatusCode: .fixedStatusCode(.roomIsReleased)) } } @@ -595,7 +595,7 @@ struct DefaultRoomLifecycleManagerTests { await #expect { try await manager.performDetachOperation() } throws: { error in - isChatError(error, withCode: .roomInFailedState) + isChatError(error, withCodeAndStatusCode: .fixedStatusCode(.roomInFailedState)) } } @@ -694,7 +694,7 @@ struct DefaultRoomLifecycleManagerTests { let failedStatusChange = try #require(await maybeFailedStatusChange) for maybeError in [maybeRoomDetachError, failedStatusChange.error] { - #expect(isChatError(maybeError, withCode: .presenceDetachmentFailed, cause: contributor1DetachError)) + #expect(isChatError(maybeError, withCodeAndStatusCode: .fixedStatusCode(.presenceDetachmentFailed), cause: contributor1DetachError)) } } @@ -1095,7 +1095,7 @@ struct DefaultRoomLifecycleManagerTests { #expect(roomStatus.isFailed) for error in [roomStatus.error, failedStatusChange.error] { - #expect(isChatError(error, withCode: .presenceDetachmentFailed, cause: contributor1DetachError)) + #expect(isChatError(error, withCodeAndStatusCode: .fixedStatusCode(.presenceDetachmentFailed), cause: contributor1DetachError)) } for contributor in contributors { @@ -2087,7 +2087,7 @@ struct DefaultRoomLifecycleManagerTests { let contributorAttachError = ARTErrorInfo.createUnknownError() // arbitrary contributorAttachOperation.complete(behavior: .completeAndChangeState(.failure(contributorAttachError), newState: .failed)) - // Then: The call to `waitToBeAbleToPerformPresenceOperations(requestedByFeature:)` fails with a `roomInInvalidState` error, whose cause is the error associated with the room status change + // Then: The call to `waitToBeAbleToPerformPresenceOperations(requestedByFeature:)` fails with a `roomInInvalidState` error with status code 500, whose cause is the error associated with the room status change var caughtError: Error? do { try await waitToBeAbleToPerformPresenceOperationsResult @@ -2096,7 +2096,7 @@ struct DefaultRoomLifecycleManagerTests { } let expectedCause = ARTErrorInfo(chatError: .attachmentFailed(feature: .messages, underlyingError: contributorAttachError)) // using our knowledge of CHA-RL1h4 - #expect(isChatError(caughtError, withCode: .roomInInvalidState, cause: expectedCause)) + #expect(isChatError(caughtError, withCodeAndStatusCode: .variableStatusCode(.roomInInvalidState, statusCode: 500), cause: expectedCause)) } // @specPartial CHA-PR3e - Tests the wait described in the spec point, but not that the feature actually performs this wait nor the side effect. TODO change this to a specOneOf once the feature is implemented @@ -2115,40 +2115,15 @@ struct DefaultRoomLifecycleManagerTests { try await manager.waitToBeAbleToPerformPresenceOperations(requestedByFeature: .messages /* arbitrary */ ) } - // @specPartial CHA-PR3f - Tests the wait described in the spec point, but not that the feature actually performs this wait. TODO change this to a specOneOf once the feature is implemented - // @specPartial CHA-PR10f - Tests the wait described in the spec point, but not that the feature actually performs this wait. TODO change this to a specOneOf once the feature is implemented - // @specPartial CHA-PR6e - Tests the wait described in the spec point, but not that the feature actually performs this wait. TODO change this to a specOneOf once the feature is implemented - // @specPartial CHA-T2e - Tests the wait described in the spec point, but not that the feature actually performs this wait. TODO change this to a specOneOf once the feature is implemented - @Test - func waitToBeAbleToPerformPresenceOperations_whenDetached() async throws { - // Given: A DefaultRoomLifecycleManager in the DETACHED status - let manager = await createManager( - forTestingWhatHappensWhenCurrentlyIn: .detached - ) - - // (Note: I wanted to use #expect(…, throws:) below, but for some reason it made the compiler _crash_! No idea why. So, gave up on that.) - - // When: `waitToBeAbleToPerformPresenceOperations(requestedByFeature:)` is called on the lifecycle manager - var caughtError: ARTErrorInfo? - do { - try await manager.waitToBeAbleToPerformPresenceOperations(requestedByFeature: .messages /* arbitrary */ ) - } catch { - caughtError = error - } - - // Then: It throws a presenceOperationRequiresRoomAttach error for that feature (which we just check via its error code, i.e. `.nonspecific`, and its message) - #expect(isChatError(caughtError, withCode: .nonspecific, message: "To perform this messages operation, you must first attach the room.")) - } - - // @specPartial CHA-PR3f - Tests the wait described in the spec point, but not that the feature actually performs this wait. TODO change this to a specOneOf once the feature is implemented - // @specPartial CHA-PR10f - Tests the wait described in the spec point, but not that the feature actually performs this wait. TODO change this to a specOneOf once the feature is implemented - // @specPartial CHA-PR6e - Tests the wait described in the spec point, but not that the feature actually performs this wait. TODO change this to a specOneOf once the feature is implemented - // @specPartial CHA-T2e - Tests the wait described in the spec point, but not that the feature actually performs this wait. TODO change this to a specOneOf once the feature is implemented + // @specPartial CHA-PR3h - Tests the wait described in the spec point, but not that the feature actually performs this wait. TODO change this to a specOneOf once the feature is implemented + // @specPartial CHA-PR10h - Tests the wait described in the spec point, but not that the feature actually performs this wait. TODO change this to a specOneOf once the feature is implemented + // @specPartial CHA-PR6h - Tests the wait described in the spec point, but not that the feature actually performs this wait. TODO change this to a specOneOf once the feature is implemented + // @specPartial CHA-T2g - Tests the wait described in the spec point, but not that the feature actually performs this wait. TODO change this to a specOneOf once the feature is implemented @Test func waitToBeAbleToPerformPresenceOperations_whenAnyOtherStatus() async throws { - // Given: A DefaultRoomLifecycleManager in a status other than ATTACHING, ATTACHED or DETACHED + // Given: A DefaultRoomLifecycleManager in a status other than ATTACHING or ATTACHED let manager = await createManager( - forTestingWhatHappensWhenCurrentlyIn: .detaching(detachOperationID: .init()) // arbitrary given the above constraints + forTestingWhatHappensWhenCurrentlyIn: .detached // arbitrary given the above constraints ) // (Note: I wanted to use #expect(…, throws:) below, but for some reason it made the compiler _crash_! No idea why. So, gave up on that.) @@ -2161,7 +2136,7 @@ struct DefaultRoomLifecycleManagerTests { caughtError = error } - // Then: It throws a presenceOperationDisallowedForCurrentRoomStatus error for that feature (which we just check via its error code, i.e. `.nonspecific`, and its message) - #expect(isChatError(caughtError, withCode: .nonspecific, message: "This messages operation can not be performed given the current room status.")) + // Then: It throws a roomInInvalidState error for that feature, with status code 400, and a message explaining that the room must first be attached + #expect(isChatError(caughtError, withCodeAndStatusCode: .variableStatusCode(.roomInInvalidState, statusCode: 400), message: "To perform this messages operation, you must first attach the room.")) } } diff --git a/Tests/AblyChatTests/DefaultRoomsTests.swift b/Tests/AblyChatTests/DefaultRoomsTests.swift index 4cebc330..a7291a1b 100644 --- a/Tests/AblyChatTests/DefaultRoomsTests.swift +++ b/Tests/AblyChatTests/DefaultRoomsTests.swift @@ -78,7 +78,7 @@ struct DefaultRoomsTests { } // Then: It throws an inconsistentRoomOptions error - #expect(isChatError(caughtError, withCode: .inconsistentRoomOptions)) + #expect(isChatError(caughtError, withCodeAndStatusCode: .fixedStatusCode(.inconsistentRoomOptions))) } // MARK: - Release a room diff --git a/Tests/AblyChatTests/Helpers/Helpers.swift b/Tests/AblyChatTests/Helpers/Helpers.swift index 1d0fc482..289adcd1 100644 --- a/Tests/AblyChatTests/Helpers/Helpers.swift +++ b/Tests/AblyChatTests/Helpers/Helpers.swift @@ -4,14 +4,14 @@ import Ably /** Tests whether a given optional `Error` is an `ARTErrorInfo` in the chat error domain with a given code and cause. Can optionally pass a message and it will check that it matches. */ -func isChatError(_ maybeError: (any Error)?, withCode code: AblyChat.ErrorCode, cause: ARTErrorInfo? = nil, message: String? = nil) -> Bool { +func isChatError(_ maybeError: (any Error)?, withCodeAndStatusCode codeAndStatusCode: AblyChat.ErrorCodeAndStatusCode, cause: ARTErrorInfo? = nil, message: String? = nil) -> Bool { guard let ablyError = maybeError as? ARTErrorInfo else { return false } return ablyError.domain == AblyChat.errorDomain as String - && ablyError.code == code.rawValue - && ablyError.statusCode == code.statusCode + && ablyError.code == codeAndStatusCode.code.rawValue + && ablyError.statusCode == codeAndStatusCode.statusCode && ablyError.cause == cause && { guard let message else { diff --git a/docs-coverage-report b/docs-coverage-report new file mode 100644 index 00000000..fceaed85 --- /dev/null +++ b/docs-coverage-report @@ -0,0 +1,479 @@ +Extracting symbol information for 'AblyChat'... +Finished extracting symbol information for 'AblyChat'. (0.51s) +Building documentation for 'AblyChat'... + --- Experimental coverage output enabled. --- + | Abstract | Curated | Code Listing +Types | 6.0% (4/67) | 87% (58/67) | 0.0% (0/67) +Members | 0.59% (2/341) | 34% (116/341) | 0.0% (0/341) +Globals | 3.4% (1/29) | 93% (27/29) | 0.0% (0/29) + + +Symbol Name Kind Abstract? Curated? Code Listing? Parameters Language USR +!=(_:_:) | Operator | false | false | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/Action-swift.enum/!=(_:_:) +!=(_:_:) | Operator | false | false | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/QueryOptions/ResultOrder/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatus/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/DiscontinuityEvent/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/HeadersValue/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Message/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageAction/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MetadataValue/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/OccupancyOptions/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/PresenceCustomData/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/PresenceEventType/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/PresenceOptions/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/RoomOptions/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/RoomReactionsOptions/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/RoomStatusChange/!=(_:_:) +!=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/TypingOptions/!=(_:_:) +...(_:) | Operator | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/...(_:)-34lg7 +...(_:) | Operator | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/...(_:)-6og6k +...(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/...(_:_:) +..<(_:) | Operator | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/.._(_:) +..<(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/.._(_:_:) +<=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/_=(_:_:)-4geju +>(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/_(_:_:) +>=(_:_:) | Operator | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/_=(_:_:)-35mc8 +AblyChat | Module | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat +Actor Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/DefaultChatClient/Actor-Implementations +AsyncIteratorProtocol Implemen | CollectionGroup | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/AsyncIterator/AsyncIteratorProtocol-Implementations +AsyncIteratorProtocol Implemen | CollectionGroup | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Subscription/AsyncIterator/AsyncIteratorProtocol-Implementations +AsyncSequence Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/AsyncSequence-Implementations +AsyncSequence Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Subscription/AsyncSequence-Implementations +BufferingPolicy | Enumeration | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/BufferingPolicy +BufferingPolicy.bufferingNewes | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/BufferingPolicy/bufferingNewest(_:) +BufferingPolicy.bufferingOldes | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/BufferingPolicy/bufferingOldest(_:) +BufferingPolicy.unbounded | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/BufferingPolicy/unbounded +Channel | Associated Type | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RealtimeChannelsProtocol/Channel +Channels | Associated Type | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RealtimeClientProtocol/Channels-swift.associatedtype +ChatClient | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/ChatClient +ClientOptions | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/ClientOptions +Comparable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/Comparable-Implementations +Connection | Associated Type | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RealtimeClientProtocol/Connection-swift.associatedtype +Connection | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Connection +ConnectionProtocol | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionProtocol +ConnectionStatus | Enumeration | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatus +ConnectionStatus.connected | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatus/connected +ConnectionStatus.connecting | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatus/connecting +ConnectionStatus.disconnected | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatus/disconnected +ConnectionStatus.failed | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatus/failed +ConnectionStatus.initialized | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatus/initialized +ConnectionStatus.suspended | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatus/suspended +ConnectionStatusChange | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatusChange +DefaultChatClient | Class | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/DefaultChatClient +DiscontinuityEvent | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/DiscontinuityEvent +EmitsDiscontinuities | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/EmitsDiscontinuities +Equatable Implementations | CollectionGroup | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/Action-swift.enum/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/QueryOptions/ResultOrder/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatus/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/DiscontinuityEvent/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/HeadersValue/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Message/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/MessageAction/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/MetadataValue/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/OccupancyOptions/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceCustomData/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceEventType/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceOptions/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomOptions/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomReactionsOptions/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatusChange/Equatable-Implementations +Equatable Implementations | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/TypingOptions/Equatable-Implementations +ErrorCode | Enumeration | true | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode +ErrorCode.inconsistentRoomOpti | Enumeration Case | true | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/inconsistentRoomOptions +ErrorCode.messagesAttachmentFa | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/messagesAttachmentFailed +ErrorCode.messagesDetachmentFa | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/messagesDetachmentFailed +ErrorCode.occupancyAttachmentF | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/occupancyAttachmentFailed +ErrorCode.occupancyDetachmentF | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/occupancyDetachmentFailed +ErrorCode.presenceAttachmentFa | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/presenceAttachmentFailed +ErrorCode.presenceDetachmentFa | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/presenceDetachmentFailed +ErrorCode.reactionsAttachmentF | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/reactionsAttachmentFailed +ErrorCode.reactionsDetachmentF | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/reactionsDetachmentFailed +ErrorCode.roomInFailedState | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/roomInFailedState +ErrorCode.roomInInvalidState | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/roomInInvalidState +ErrorCode.roomIsReleased | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/roomIsReleased +ErrorCode.roomIsReleasing | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/roomIsReleasing +ErrorCode.typingAttachmentFail | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/typingAttachmentFailed +ErrorCode.typingDetachmentFail | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/typingDetachmentFailed +Headers | Type Alias | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Headers +HeadersValue | Enumeration | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/HeadersValue +HeadersValue.bool(_:) | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/HeadersValue/bool(_:) +HeadersValue.null | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/HeadersValue/null +HeadersValue.number(_:) | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/HeadersValue/number(_:) +HeadersValue.string(_:) | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/HeadersValue/string(_:) +LogContext | Type Alias | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/LogContext +LogHandler | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/LogHandler +LogLevel | Enumeration | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/LogLevel +LogLevel.debug | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/debug +LogLevel.error | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/error +LogLevel.info | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/info +LogLevel.silent | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/silent +LogLevel.trace | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/trace +LogLevel.warn | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/LogLevel/warn +Message | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Message +MessageAction | Enumeration | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/MessageAction +MessageAction.create | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/MessageAction/create +MessageHeaders | Type Alias | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/MessageHeaders +MessageMetadata | Type Alias | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/MessageMetadata +MessageSubscription | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription +MessageSubscription.AsyncItera | Structure | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/AsyncIterator +MessageSubscription.Element | Type Alias | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/Element +Messages | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Messages +Metadata | Type Alias | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Metadata +MetadataValue | Enumeration | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/MetadataValue +MetadataValue.bool(_:) | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/MetadataValue/bool(_:) +MetadataValue.null | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/MetadataValue/null +MetadataValue.number(_:) | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/MetadataValue/number(_:) +MetadataValue.string(_:) | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/MetadataValue/string(_:) +Occupancy | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Occupancy +OccupancyEvent | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/OccupancyEvent +OccupancyOptions | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/OccupancyOptions +PaginatedResult | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/PaginatedResult +Presence | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Presence +PresenceCustomData | Enumeration | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceCustomData +PresenceCustomData.bool(_:) | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceCustomData/bool(_:) +PresenceCustomData.null | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceCustomData/null +PresenceCustomData.number(_:) | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceCustomData/number(_:) +PresenceCustomData.string(_:) | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceCustomData/string(_:) +PresenceData | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceData +PresenceEvent | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceEvent +PresenceEventType | Enumeration | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceEventType +PresenceEventType.enter | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceEventType/enter +PresenceEventType.leave | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceEventType/leave +PresenceEventType.present | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceEventType/present +PresenceEventType.update | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceEventType/update +PresenceMember | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember +PresenceMember.Action | Enumeration | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/Action-swift.enum +PresenceMember.Action.absent | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/Action-swift.enum/absent +PresenceMember.Action.enter | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/Action-swift.enum/enter +PresenceMember.Action.leave | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/Action-swift.enum/leave +PresenceMember.Action.present | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/Action-swift.enum/present +PresenceMember.Action.unknown | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/Action-swift.enum/unknown +PresenceMember.Action.update | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/Action-swift.enum/update +PresenceOptions | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceOptions +PresenceQuery | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceQuery +QueryOptions | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/QueryOptions +QueryOptions.ResultOrder | Enumeration | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/QueryOptions/ResultOrder +QueryOptions.ResultOrder.newes | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/QueryOptions/ResultOrder/newestFirst +QueryOptions.ResultOrder.oldes | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/QueryOptions/ResultOrder/oldestFirst +RawRepresentable Implementatio | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/RawRepresentable-Implementations +RawRepresentable Implementatio | CollectionGroup | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/MessageAction/RawRepresentable-Implementations +Reaction | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Reaction +ReactionHeaders | Type Alias | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/ReactionHeaders +ReactionMetadata | Type Alias | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/ReactionMetadata +RealtimeChannelProtocol | Protocol | true | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RealtimeChannelProtocol +RealtimeChannelsProtocol | Protocol | true | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RealtimeChannelsProtocol +RealtimeClient | Type Alias | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RealtimeClient +RealtimeClientProtocol | Protocol | true | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RealtimeClientProtocol +Room | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Room +RoomOptions | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomOptions +RoomReactions | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomReactions +RoomReactionsOptions | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomReactionsOptions +RoomStatus | Enumeration | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus +RoomStatus.attached | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/attached +RoomStatus.attaching(error:) | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/attaching(error:) +RoomStatus.detached | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/detached +RoomStatus.detaching | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/detaching +RoomStatus.failed(error:) | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/failed(error:) +RoomStatus.initialized | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/initialized +RoomStatus.released | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/released +RoomStatus.releasing | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/releasing +RoomStatus.suspended(error:) | Enumeration Case | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/suspended(error:) +RoomStatusChange | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatusChange +Rooms | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Rooms +SendMessageParams | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/SendMessageParams +SendReactionParams | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/SendReactionParams +Subscription | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Subscription +Subscription.AsyncIterator | Structure | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Subscription/AsyncIterator +T | Associated Type | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PaginatedResult/T +Typing | Protocol | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Typing +TypingEvent | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/TypingEvent +TypingOptions | Structure | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/TypingOptions +UserCustomData | Type Alias | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/UserCustomData +action | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceEvent/action +action | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/action-swift.property +adjacentPairs() | Instance Method | false | true | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/adjacentPairs() +adjacentPairs() | Instance Method | false | true | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/adjacentPairs() +allSatisfy(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/allSatisfy(_:) +allSatisfy(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/allSatisfy(_:) +assertIsolated(_:file:line:) | Instance Method | false | true | false | 0.0% (0/3) | Swift | doc://AblyChat/documentation/AblyChat/DefaultChatClient/assertIsolated(_:file:line:) +assumeIsolated(_:file:line:) | Instance Method | false | true | false | 0.0% (0/3) | Swift | doc://AblyChat/documentation/AblyChat/DefaultChatClient/assumeIsolated(_:file:line:) +attach() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Room/attach() +buffer(policy:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/buffer(policy:) +buffer(policy:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/buffer(policy:) +channel | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Messages/channel +channel | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Occupancy/channel +channel | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomReactions/channel +channel | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Typing/channel +channels | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RealtimeClientProtocol/channels-swift.property +characters | Instance Property | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Subscription/characters +chunked(by:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/chunked(by:) +chunked(by:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/chunked(by:) +chunked(by:into:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/chunked(by:into:)-3k4p4 +chunked(by:into:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/chunked(by:into:)-th3z +chunked(by:into:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/chunked(by:into:)-7va5o +chunked(by:into:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/chunked(by:into:)-9n9nd +chunked(into:by:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/chunked(into:by:) +chunked(into:by:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/chunked(into:by:) +chunked(into:on:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/chunked(into:on:) +chunked(into:on:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/chunked(into:on:) +chunked(on:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/chunked(on:) +chunked(on:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/chunked(on:) +chunks(ofCount:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/chunks(ofCount:) +chunks(ofCount:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/chunks(ofCount:) +chunks(ofCount:into:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/chunks(ofCount:into:) +chunks(ofCount:into:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/chunks(ofCount:into:) +chunks(ofCount:or:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/chunks(ofCount:or:)-2d1i1 +chunks(ofCount:or:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/chunks(ofCount:or:)-636zz +chunks(ofCount:or:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/chunks(ofCount:or:)-3b23p +chunks(ofCount:or:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/chunks(ofCount:or:)-8wcxv +chunks(ofCount:or:into:) | Instance Method | false | true | false | 0.0% (0/3) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/chunks(ofCount:or:into:)-1ca76 +chunks(ofCount:or:into:) | Instance Method | false | true | false | 0.0% (0/3) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/chunks(ofCount:or:into:)-5m4k2 +chunks(ofCount:or:into:) | Instance Method | false | true | false | 0.0% (0/3) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/chunks(ofCount:or:into:)-5hxmy +chunks(ofCount:or:into:) | Instance Method | false | true | false | 0.0% (0/3) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/chunks(ofCount:or:into:)-5yq0d +clientID | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ChatClient/clientID +clientID | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/DefaultChatClient/clientID +clientID | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Message/clientID +clientID | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceEvent/clientID +clientID | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/clientID +clientID | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceQuery/clientID +clientID | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Reaction/clientID +clientOptions | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ChatClient/clientOptions +clientOptions | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/DefaultChatClient/clientOptions +clientOptions | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Rooms/clientOptions +compactMap(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/compactMap(_:)-25xyi +compactMap(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/compactMap(_:)-u5g5 +compactMap(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/compactMap(_:)-20s9k +compactMap(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/compactMap(_:)-2yaac +compacted() | Instance Method | false | true | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/compacted() +compacted() | Instance Method | false | true | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/compacted() +connection | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ChatClient/connection +connection | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/DefaultChatClient/connection +connection | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RealtimeClientProtocol/connection-swift.property +connectionID | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceQuery/connectionID +connections | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/OccupancyEvent/connections +contains(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/contains(_:) +contains(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/contains(_:) +contains(where:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/contains(where:) +contains(where:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/contains(where:) +createdAt | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Message/createdAt +createdAt | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Reaction/createdAt +current | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatusChange/current +current | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PaginatedResult/current +current | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatusChange/current +currentlyTyping | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/TypingEvent/currentlyTyping +data | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceEvent/data +data | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/data +debounce(for:tolerance:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/debounce(for:tolerance:) +debounce(for:tolerance:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/debounce(for:tolerance:) +debounce(for:tolerance:clock:) | Instance Method | false | true | false | 0.0% (0/3) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/debounce(for:tolerance:clock:) +debounce(for:tolerance:clock:) | Instance Method | false | true | false | 0.0% (0/3) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/debounce(for:tolerance:clock:) +detach() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Room/detach() +drop(while:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/drop(while:) +drop(while:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/drop(while:) +dropFirst(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/dropFirst(_:) +dropFirst(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/dropFirst(_:) +encode(to:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageAction/encode(to:) +end | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/QueryOptions/end +enter | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceOptions/enter +enter(data:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Presence/enter(data:) +error | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Connection/error +error | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatusChange/error +error | Instance Property | true | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/DiscontinuityEvent/error +errorDomain | Global Variable | true | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/errorDomain +extras | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/extras +filter(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/filter(_:) +filter(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/filter(_:) +finish() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/finish() +first | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PaginatedResult/first +first(where:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/first(where:) +first(where:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/first(where:) +flatMap(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/flatMap(_:)-45wm0 +flatMap(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/flatMap(_:)-5au6 +flatMap(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/flatMap(_:)-7whl +flatMap(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/flatMap(_:)-8eeza +flatMap(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/flatMap(_:)-2ugjf +flatMap(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/flatMap(_:)-6scz4 +flatMap(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/flatMap(_:)-9syjt +flatMap(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/flatMap(_:)-qsrn +get() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Occupancy/get() +get() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Presence/get() +get() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Typing/get() +get(_:options:) | Instance Method | false | false | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/RealtimeChannelsProtocol/get(_:options:) +get(options:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Messages/get(options:) +get(params:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Presence/get(params:) +get(roomID:options:) | Instance Method | false | false | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Rooms/get(roomID:options:) +getPreviousMessages(params:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/getPreviousMessages(params:) +hasNext | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PaginatedResult/hasNext +hash(into:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/hash(into:) +hash(into:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageAction/hash(into:) +hashValue | Instance Property | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/hashValue +hashValue | Instance Property | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/MessageAction/hashValue +headers | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Message/headers +headers | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Reaction/headers +headers | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/SendMessageParams/headers +headers | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/SendReactionParams/headers +id | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Message/id +init() | Initializer | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/OccupancyOptions/init() +init() | Initializer | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/RoomReactionsOptions/init() +init(action:clientID:timestamp | Initializer | false | false | false | 0.0% (0/4) | Swift | doc://AblyChat/documentation/AblyChat/PresenceEvent/init(action:clientID:timestamp:data:) +init(clientID:data:action:extr | Initializer | false | false | false | 0.0% (0/5) | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/init(clientID:data:action:extras:updatedAt:) +init(connections:presenceMembe | Initializer | false | false | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/OccupancyEvent/init(connections:presenceMembers:) +init(current:previous:) | Initializer | false | false | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/RoomStatusChange/init(current:previous:) +init(current:previous:error:re | Initializer | false | false | false | 0.0% (0/4) | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatusChange/init(current:previous:error:retryIn:) +init(currentlyTyping:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/TypingEvent/init(currentlyTyping:) +init(enter:subscribe:) | Initializer | false | false | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/PresenceOptions/init(enter:subscribe:) +init(error:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/DiscontinuityEvent/init(error:) +init(from:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/HeadersValue/init(from:) +init(from:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Message/init(from:) +init(from:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MetadataValue/init(from:) +init(from:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/OccupancyEvent/init(from:) +init(from:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/PresenceCustomData/init(from:) +init(from:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/PresenceData/init(from:) +init(from:) | Initializer | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageAction/init(from:) +init(logHandler:logLevel:) | Initializer | false | false | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/ClientOptions/init(logHandler:logLevel:) +init(mockAsyncSequence:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/init(mockAsyncSequence:) +init(mockAsyncSequence:mockGet | Initializer | false | false | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/init(mockAsyncSequence:mockGetPreviousMessages:) +init(presence:typing:reactions | Initializer | false | false | false | 0.0% (0/4) | Swift | doc://AblyChat/documentation/AblyChat/RoomOptions/init(presence:typing:reactions:occupancy:) +init(rawValue:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/ErrorCode/init(rawValue:) +init(rawValue:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageAction/init(rawValue:) +init(realtime:clientOptions:) | Initializer | false | false | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/DefaultChatClient/init(realtime:clientOptions:) +init(serial:latestAction:clien | Initializer | false | false | false | 0.0% (0/8) | Swift | doc://AblyChat/documentation/AblyChat/Message/init(serial:latestAction:clientID:roomID:text:createdAt:metadata:headers:) +init(start:end:limit:orderBy:) | Initializer | false | false | false | 0.0% (0/4) | Swift | doc://AblyChat/documentation/AblyChat/QueryOptions/init(start:end:limit:orderBy:) +init(text:metadata:headers:) | Initializer | false | false | false | 0.0% (0/3) | Swift | doc://AblyChat/documentation/AblyChat/SendMessageParams/init(text:metadata:headers:) +init(timeout:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/TypingOptions/init(timeout:) +init(type:metadata:headers:) | Initializer | false | false | false | 0.0% (0/3) | Swift | doc://AblyChat/documentation/AblyChat/SendReactionParams/init(type:metadata:headers:) +init(type:metadata:headers:cre | Initializer | false | false | false | 0.0% (0/6) | Swift | doc://AblyChat/documentation/AblyChat/Reaction/init(type:metadata:headers:createdAt:clientID:isSelf:) +init(userCustomData:) | Initializer | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/PresenceData/init(userCustomData:) +interspersed(every:with:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/interspersed(every:with:)-10pdq +interspersed(every:with:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/interspersed(every:with:)-2t9bk +interspersed(every:with:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/interspersed(every:with:)-3p3gl +interspersed(every:with:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/interspersed(every:with:)-4mr2k +interspersed(every:with:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/interspersed(every:with:)-9rfgk +interspersed(every:with:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/interspersed(every:with:)-23bxp +interspersed(every:with:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/interspersed(every:with:)-2axou +interspersed(every:with:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/interspersed(every:with:)-386uy +interspersed(every:with:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/interspersed(every:with:)-4ah2l +interspersed(every:with:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/interspersed(every:with:)-7eg0h +isAttaching | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/isAttaching +isFailed | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/isFailed +isLast | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PaginatedResult/isLast +isSelf | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Reaction/isSelf +isSuspended | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatus/isSuspended +isUserPresent(clientID:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Presence/isUserPresent(clientID:) +items | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PaginatedResult/items +joined() | Instance Method | false | true | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/joined() +joined(separator:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/joined(separator:) +latestAction | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Message/latestAction +leave(data:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Presence/leave(data:) +limit | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceQuery/limit +limit | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/QueryOptions/limit +lines | Instance Property | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Subscription/lines +log(message:level:context:) | Instance Method | false | false | false | 0.0% (0/3) | Swift | doc://AblyChat/documentation/AblyChat/LogHandler/log(message:level:context:) +logHandler | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ClientOptions/logHandler +logLevel | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ClientOptions/logLevel +makeAsyncIterator() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/makeAsyncIterator() +makeAsyncIterator() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/makeAsyncIterator() +map(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/map(_:)-2y8s +map(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/map(_:)-7dm8z +map(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/map(_:)-2x606 +map(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/map(_:)-3wv9v +max() | Instance Method | false | true | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/max() +max(by:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/max(by:) +max(by:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/max(by:) +messages | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Room/messages +metadata | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Message/metadata +metadata | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Reaction/metadata +metadata | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/SendMessageParams/metadata +metadata | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/SendReactionParams/metadata +min() | Instance Method | false | true | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/min() +min(by:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/min(by:) +min(by:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/min(by:) +next | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PaginatedResult/next +next() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/AsyncIterator/next() +next() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/AsyncIterator/next() +next(isolation:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/AsyncIterator/next(isolation:) +next(isolation:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/AsyncIterator/next(isolation:) +occupancy | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Room/occupancy +occupancy | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomOptions/occupancy +onStatusChange(bufferingPolicy | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Connection/onStatusChange(bufferingPolicy:) +onStatusChange(bufferingPolicy | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Room/onStatusChange(bufferingPolicy:) +options | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Room/options +orderBy | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/QueryOptions/orderBy +preconditionIsolated(_:file:li | Instance Method | false | true | false | 0.0% (0/3) | Swift | doc://AblyChat/documentation/AblyChat/DefaultChatClient/preconditionIsolated(_:file:line:) +prefix(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/prefix(_:) +prefix(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/prefix(_:) +prefix(while:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/prefix(while:) +prefix(while:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/prefix(while:) +presence | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Room/presence +presence | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomOptions/presence +presenceMembers | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/OccupancyEvent/presenceMembers +previous | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatusChange/previous +previous | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomStatusChange/previous +reactions | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Room/reactions +reactions | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomOptions/reactions +realtime | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ChatClient/realtime +realtime | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/DefaultChatClient/realtime +reduce(_:_:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/reduce(_:_:) +reduce(_:_:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/reduce(_:_:) +reduce(into:_:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/reduce(into:_:) +reduce(into:_:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/reduce(into:_:) +reductions(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/reductions(_:) +reductions(_:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/reductions(_:) +reductions(_:_:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/reductions(_:_:)-69bs5 +reductions(_:_:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/reductions(_:_:)-7v522 +reductions(_:_:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/reductions(_:_:)-9h9bs +reductions(_:_:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/reductions(_:_:)-9ls0a +reductions(into:_:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/reductions(into:_:)-7cdn8 +reductions(into:_:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/reductions(into:_:)-7ctfy +reductions(into:_:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/reductions(into:_:)-1jdvi +reductions(into:_:) | Instance Method | false | true | false | 0.0% (0/2) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/reductions(into:_:)-2mnn7 +release(roomID:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Rooms/release(roomID:) +removeDuplicates() | Instance Method | false | true | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/removeDuplicates() +removeDuplicates() | Instance Method | false | true | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/removeDuplicates() +removeDuplicates(by:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/MessageSubscription/removeDuplicates(by:) +removeDuplicates(by:) | Instance Method | false | true | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Subscription/removeDuplicates(by:) +retryIn | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ConnectionStatusChange/retryIn +roomID | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Message/roomID +roomID | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Room/roomID +rooms | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/ChatClient/rooms +rooms | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/DefaultChatClient/rooms +send(params:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Messages/send(params:) +send(params:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/RoomReactions/send(params:) +serial | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Message/serial +start | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/QueryOptions/start +start() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Typing/start() +status | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Connection/status +status | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Room/status +stop() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/Typing/stop() +subscribe | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceOptions/subscribe +subscribe(bufferingPolicy:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Messages/subscribe(bufferingPolicy:) +subscribe(bufferingPolicy:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Occupancy/subscribe(bufferingPolicy:) +subscribe(bufferingPolicy:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/RoomReactions/subscribe(bufferingPolicy:) +subscribe(bufferingPolicy:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Typing/subscribe(bufferingPolicy:) +subscribe(event:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Presence/subscribe(event:) +subscribe(events:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Presence/subscribe(events:) +subscribeToDiscontinuities() | Instance Method | false | false | false | (0/0) | Swift | doc://AblyChat/documentation/AblyChat/EmitsDiscontinuities/subscribeToDiscontinuities() +text | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Message/text +text | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/SendMessageParams/text +timeout | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/TypingOptions/timeout +timestamp | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceEvent/timestamp +type | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Reaction/type +type | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/SendReactionParams/type +typing | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/Room/typing +typing | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/RoomOptions/typing +unicodeScalars | Instance Property | false | true | false | - | Swift | doc://AblyChat/documentation/AblyChat/Subscription/unicodeScalars +update(data:) | Instance Method | false | false | false | 0.0% (0/1) | Swift | doc://AblyChat/documentation/AblyChat/Presence/update(data:) +updatedAt | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceMember/updatedAt +userCustomData | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceData/userCustomData +value | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceCustomData/value +waitForSync | Instance Property | false | false | false | - | Swift | doc://AblyChat/documentation/AblyChat/PresenceQuery/waitForSync + +Finished building documentation for 'AblyChat' (0.26s) +Generated documentation archive at: + /Users/lawrence/code/work/ably/ably-chat-swift/.build/plugins/Swift-DocC/outputs/AblyChat.doccarchive