From dd0946a73ba75c4b2e6070bfad95f915eb2588b6 Mon Sep 17 00:00:00 2001 From: Marat Al Date: Wed, 11 Dec 2024 14:15:56 +0100 Subject: [PATCH] Added documentation comments (based on JS repo in 69ea478). Adopted to swift docc syntax. --- .github/workflows/check.yaml | 54 ++ .../xcshareddata/swiftpm/Package.resolved | 20 +- Package.resolved | 20 +- Package.swift | 4 + Sources/AblyChat/BufferingPolicy.swift | 6 +- Sources/AblyChat/ChatClient.swift | 54 ++ Sources/AblyChat/Connection.swift | 73 ++- Sources/AblyChat/Dependencies.swift | 4 +- Sources/AblyChat/EmitsDiscontinuities.swift | 14 +- Sources/AblyChat/Errors.swift | 66 ++- Sources/AblyChat/Events.swift | 8 + Sources/AblyChat/Headers.swift | 15 + Sources/AblyChat/Logging.swift | 11 + Sources/AblyChat/Message.swift | 62 ++- Sources/AblyChat/Messages.swift | 109 +++- Sources/AblyChat/Metadata.swift | 10 + Sources/AblyChat/Occupancy.swift | 40 +- Sources/AblyChat/Presence.swift | 172 ++++++- Sources/AblyChat/Reaction.swift | 34 ++ Sources/AblyChat/Room.swift | 104 +++- Sources/AblyChat/RoomFeature.swift | 4 +- Sources/AblyChat/RoomLifecycleManager.swift | 6 +- Sources/AblyChat/RoomOptions.swift | 54 ++ Sources/AblyChat/RoomReactions.swift | 64 ++- Sources/AblyChat/RoomStatus.swift | 39 +- Sources/AblyChat/Rooms.swift | 44 ++ Sources/AblyChat/Typing.swift | 53 +- Sources/BuildTool/BuildTool.swift | 38 +- docs-coverage-report | 479 ++++++++++++++++++ 29 files changed, 1620 insertions(+), 41 deletions(-) create mode 100644 docs-coverage-report diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 15e16609..c10952b1 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -178,6 +178,59 @@ jobs: - name: Build example app run: swift run BuildTool build-example-app --platform ${{ matrix.platform }} + check-documentation: + runs-on: macos-15 + + permissions: + deployments: write + id-token: write + contents: read + + 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 + + # Dry run upload-action to get base-path url + - name: Dry-Run Upload (to get url) + id: preupload + uses: ably/sdk-upload-action@v2 + with: + mode: preempt + githubToken: ${{ secrets.GITHUB_TOKEN }} + artifactName: AblyChat + + # Build the documentation using Swift DocC + - name: Build documentation + run: | + PR_NUMBER=${{ github.event.pull_request.number }} + swift package generate-documentation --target AblyChat --disable-indexing \ + --hosting-base-path "${{ steps.preupload.outputs.base-path }}" \ + --transform-for-static-hosting + working-directory: ${{ github.workspace }} + + # Configure AWS credentials for uploading documentation + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: eu-west-2 + role-to-assume: arn:aws:iam::${{ secrets.ABLY_AWS_ACCOUNT_ID_SDK }}:role/ably-sdk-builds-ably-chat-swift + role-session-name: "${{ github.run_id }}-${{ github.run_number }}" + + # Upload the generated documentation + - name: Upload Documentation + uses: ably/sdk-upload-action@v2 + with: + sourcePath: .build/plugins/Swift-DocC/outputs/AblyChat.doccarchive # Path to the Swift DocC output folder + githubToken: ${{ secrets.GITHUB_TOKEN }} + artifactName: AblyChat # Optional root-level directory name + landingPagePath: /documentation/ablychat + # 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 @@ -193,6 +246,7 @@ jobs: - build-and-test-xcode - build-release-configuration-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 3cdaa6ea..1df10344 100644 --- a/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/AblyChat.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "1d806168af5c6dfac34494fc5941d89fb9d4f96e4803f3a16742053db45c2b49", + "originHash" : "7cf5aad170e2009b8ed30136589be3e449ac79a630487eb65f95143b02db577f", "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 dff5a973..2a3a03ca 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "87bdf964454a6a566cd3557b1ae36935d1d19b6a8a8073b9fce67464d6593572", + "originHash" : "f5dd87027f1300f852cf86019686409a47c851bc11433a4138c8956b8a09ad6f", "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 f4db8fb3..b0763ba1 100644 --- a/Package.swift +++ b/Package.swift @@ -34,6 +34,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/BufferingPolicy.swift b/Sources/AblyChat/BufferingPolicy.swift index b9440903..c7678026 100644 --- a/Sources/AblyChat/BufferingPolicy.swift +++ b/Sources/AblyChat/BufferingPolicy.swift @@ -1,5 +1,7 @@ -// Describes what to do with realtime events that come in faster than the consumer of an `AsyncSequence` can handle them. -// (This is the same as `AsyncStream.Continuation.BufferingPolicy` but with the generic type parameter `T` removed.) +/** + * Describes what to do with realtime events that come in faster than the consumer of an `AsyncSequence` can handle them. + * (This is the same as `AsyncStream.Continuation.BufferingPolicy` but with the generic type parameter `T` removed.) + */ public enum BufferingPolicy: Sendable { case unbounded case bufferingOldest(Int) diff --git a/Sources/AblyChat/ChatClient.swift b/Sources/AblyChat/ChatClient.swift index 8fd9d86e..f880dd77 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 565ff047..e05579d8 100644 --- a/Sources/AblyChat/Connection.swift +++ b/Sources/AblyChat/Connection.swift @@ -1,11 +1,31 @@ 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 } + + /** + * Subscribes a given listener to a connection status changes. + * + * - Parameters: + * - bufferingPolicy: The ``BufferingPolicy`` for the created subscription. + * + * - Returns: A subscription `AsyncSequence` that can be used to iterate through ``ConnectionStatusChange`` events. + */ func onStatusChange(bufferingPolicy: BufferingPolicy) -> Subscription - /// Same as calling ``onStatusChange(bufferingPolicy:)`` with ``BufferingPolicy.unbounded``. + + /// Same as calling ``onStatusChange(bufferingPolicy:)`` with ``BufferingPolicy/unbounded``. /// /// The `Connection` protocol provides a default implementation of this method. func onStatusChange() -> Subscription @@ -17,18 +37,50 @@ public extension Connection { } } +/** + * 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) { @@ -51,11 +103,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/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/EmitsDiscontinuities.swift b/Sources/AblyChat/EmitsDiscontinuities.swift index ff42808d..d893e93e 100644 --- a/Sources/AblyChat/EmitsDiscontinuities.swift +++ b/Sources/AblyChat/EmitsDiscontinuities.swift @@ -9,9 +9,21 @@ public struct DiscontinuityEvent: Sendable, Equatable { } } +/** + * An interface to be implemented by objects that can emit discontinuities to listeners. + */ public protocol EmitsDiscontinuities { + /** + * Subscribes a given listener to a detected discontinuity. + * + * - Parameters: + * - bufferingPolicy: The ``BufferingPolicy`` for the created subscription. + * + * - Returns: A subscription `AsyncSequence` that can be used to iterate through ``DiscontinuityEvent`` events. + */ func onDiscontinuity(bufferingPolicy: BufferingPolicy) async -> Subscription - /// Same as calling ``onDiscontinuity(bufferingPolicy:)`` with ``BufferingPolicy.unbounded``. + + /// Same as calling ``onDiscontinuity(bufferingPolicy:)`` with ``BufferingPolicy/unbounded``. /// /// The `EmitsDiscontinuities` protocol provides a default implementation of this method. func onDiscontinuity() async -> Subscription diff --git a/Sources/AblyChat/Errors.swift b/Sources/AblyChat/Errors.swift index 22fed11c..9f3e9ef5 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" @@ -14,22 +14,74 @@ public enum ErrorCode: Int { /// The user attempted to perform an invalid action. case badRequest = 40000 + /** + * The messages feature failed to attach. + */ case messagesAttachmentFailed = 102_001 + + /** + * The presence feature failed to attach. + */ case presenceAttachmentFailed = 102_002 + + /** + * The reactions feature failed to attach. + */ case reactionsAttachmentFailed = 102_003 + + /** + * The occupancy feature failed to attach. + */ case occupancyAttachmentFailed = 102_004 + + /** + * The typing feature failed to attach. + */ case typingAttachmentFailed = 102_005 + /** + * The messages feature failed to detach. + */ case messagesDetachmentFailed = 102_050 + + /** + * The presence feature failed to detach. + */ case presenceDetachmentFailed = 102_051 + + /** + * The reactions feature failed to detach. + */ case reactionsDetachmentFailed = 102_052 + + /** + * The occupancy feature failed to detach. + */ case occupancyDetachmentFailed = 102_053 + + /** + * The typing feature failed to detach. + */ case typingDetachmentFailed = 102_054 + /** + * Cannot perform operation because the room is in a failed state. + */ case roomInFailedState = 102_101 + + /** + * Cannot perform operation because the room is in a releasing state. + */ case roomIsReleasing = 102_102 + + /** + * Cannot perform operation because the room is in a released state. + */ case roomIsReleased = 102_103 + /** + * Room was released before the operation could complete. + */ case roomReleasedBeforeOperationCompleted = 102_106 case roomInInvalidState = 102_107 @@ -87,7 +139,7 @@ public enum ErrorCode: Int { } } - /// The ``ARTErrorInfo.statusCode`` that should be returned for this error. + /// 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 { @@ -133,7 +185,7 @@ internal enum ErrorCodeAndStatusCode { case fixedStatusCode(ErrorCode.CaseThatImpliesFixedStatusCode) case variableStatusCode(ErrorCode.CaseThatImpliesVariableStatusCode, statusCode: Int) - /// The ``ARTErrorInfo.code`` that should be returned for this error. + /// The ``ARTErrorInfo/code`` that should be returned for this error. internal var code: ErrorCode { switch self { case let .fixedStatusCode(code): @@ -143,7 +195,7 @@ internal enum ErrorCodeAndStatusCode { } } - /// The ``ARTErrorInfo.statusCode`` that should be returned for this error. + /// The ``ARTErrorInfo/statusCode`` that should be returned for this error. internal var statusCode: Int { switch self { case let .fixedStatusCode(code): @@ -252,7 +304,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): @@ -276,7 +328,7 @@ internal enum ChatError { } } - /// 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): diff --git a/Sources/AblyChat/Events.swift b/Sources/AblyChat/Events.swift index 2bfa5aa1..56c182c5 100644 --- a/Sources/AblyChat/Events.swift +++ b/Sources/AblyChat/Events.swift @@ -1,6 +1,12 @@ import Ably +/** + * Chat Message Actions. + */ public enum MessageAction: String, Codable, Sendable { + /** + * Action applied to a new message. + */ case create = "message.create" internal static func fromRealtimeAction(_ action: ARTMessageAction) -> Self? { @@ -21,7 +27,9 @@ public enum MessageAction: String, Codable, Sendable { } } +/// Realtime chat message names. internal enum RealtimeMessageName: String, Sendable { + /// Represents a regular chat message. case chatMessage = "chat.message" } diff --git a/Sources/AblyChat/Headers.swift b/Sources/AblyChat/Headers.swift index e64a12fa..7fbd9a52 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/Logging.swift b/Sources/AblyChat/Logging.swift index dbfd41e1..2d6c7d64 100644 --- a/Sources/AblyChat/Logging.swift +++ b/Sources/AblyChat/Logging.swift @@ -3,9 +3,20 @@ import os public typealias LogContext = [String: any Sendable] public protocol LogHandler: AnyObject, Sendable { + /** + * A function that can be used to handle log messages. + * + * - Parameters: + * - message: The message to log. + * - level: The log level of the message. + * - context: The context of the log message as key-value pairs. + */ func log(message: String, level: LogLevel, context: LogContext?) } +/** + * Represents the different levels of logging that can be used. + */ public enum LogLevel: Sendable, Comparable { case trace case debug diff --git a/Sources/AblyChat/Message.swift b/Sources/AblyChat/Message.swift index b781bad5..9017313c 100644 --- a/Sources/AblyChat/Message.swift +++ b/Sources/AblyChat/Message.swift @@ -1,20 +1,80 @@ import Foundation +/** + * ``Headers`` type for chat messages. + */ public typealias MessageHeaders = Headers + +/** + * ``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). +/** + * Represents a single message in a chat room. + */ public struct Message: Sendable, Codable, Identifiable, Equatable { // id to meet Identifiable conformance. 2 messages in the same channel cannot have the same serial. public var id: String { serial } + /** + * The unique identifier of the message. + */ public var serial: String + + /** + * The action type of the message. This can be used to determine if the message was created, updated, or deleted. + */ public var action: MessageAction + + /** + * The clientId of the user who created the message. + */ + public var clientID: String + + /** + * The roomId of the chat room to which the message belongs. + */ public var roomID: String + + /** + * The text of the message. + */ public var text: String + + /** + * The timestamp at which the message was created. + */ public var createdAt: Date? + + /** + * The metadata of a chat message. Allows for attaching extra info to a message, + * which can be used for various features such as animations, effects, or simply + * to link it to other resources such as images, relative points in time, etc. + * + * Metadata is part of the Ably Pub/sub message content and is not read by Ably. + * + * This value is always set. If there is no metadata, this is an empty object. + * + * 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 + + /** + * The headers of a chat message. Headers enable attaching extra info to a message, + * which can be used for various features such as linking to a relative point in + * time of a livestream video or flagging this message as important or pinned. + * + * Headers are part of the Ably realtime message extras.headers and they can be used + * for [Filtered Subscriptions](https://faqs.ably.com/subscription-filters) and similar. + * + * This value is always set. If there are no headers, this is an empty object. + * + * 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(serial: String, action: MessageAction, clientID: String, roomID: String, text: String, createdAt: Date?, metadata: MessageMetadata, headers: MessageHeaders) { diff --git a/Sources/AblyChat/Messages.swift b/Sources/AblyChat/Messages.swift index 6b4965ce..e333568a 100644 --- a/Sources/AblyChat/Messages.swift +++ b/Sources/AblyChat/Messages.swift @@ -1,13 +1,56 @@ 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 ``Room/messages``. + */ public protocol Messages: AnyObject, Sendable, EmitsDiscontinuities { + /** + * Subscribe to new messages in this chat room. + * + * - Parameters: + * - bufferingPolicy: The ``BufferingPolicy`` for the created subscription. + * + * - Returns: A subscription ``MessageSubscription`` that can be used to iterate through new messages. + */ func subscribe(bufferingPolicy: BufferingPolicy) async throws -> MessageSubscription - /// Same as calling ``subscribe(bufferingPolicy:)`` with ``BufferingPolicy.unbounded``. + + /// Same as calling ``subscribe(bufferingPolicy:)`` with ``BufferingPolicy/unbounded``. /// /// The `Messages` protocol provides a default implementation of this method. func subscribe() async throws -> MessageSubscription + + /** + * Get messages that have been previously sent to the chat room, based on the provided options. + * + * - Parameters: + * - options: Options for the query. + * + * - Returns: A paginated result object that 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. + * + * - Parameters: + * - params: An object containing `text`, `headers` and `metadata` for the message. + * + * - Returns: The published message. + * + * - Note: It is possible to receive your own message via the messages subscription before this method returns. + */ 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 } } @@ -17,9 +60,44 @@ public extension Messages { } } +/** + * 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) { @@ -29,15 +107,44 @@ public struct SendMessageParams: Sendable { } } +/** + * Options for querying messages in a chat room. + */ public struct QueryOptions: Sendable { public enum OrderBy: 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. + * + * Defaults to 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. + * + * Defaults to the current time. + */ public var end: Date? + + /** + * The maximum number of messages to return in the response. + * + * Defaults to 100. + */ public var limit: Int? + + /** + * The direction to query messages in. + * If ``OrderBy/oldestFirst``, the response will include messages from the start of the time window to the end. + * If ``OrderBy/newestFirst``, the response will include messages from the end of the time window to the start. + * If not provided, the default is ``OrderBy/newestFirst`. + */ public var orderBy: OrderBy? // (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..dfeba280 100644 --- a/Sources/AblyChat/Metadata.swift +++ b/Sources/AblyChat/Metadata.swift @@ -8,4 +8,14 @@ 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/Occupancy.swift b/Sources/AblyChat/Occupancy.swift index d3004f75..3779fd4a 100644 --- a/Sources/AblyChat/Occupancy.swift +++ b/Sources/AblyChat/Occupancy.swift @@ -1,12 +1,39 @@ import Ably +/** + * This interface is used to interact with occupancy in a chat room: subscribing to occupancy updates and + * fetching the current room occupancy metrics. + * + * Get an instance via ``Room/occupancy``. + */ public protocol Occupancy: AnyObject, Sendable, EmitsDiscontinuities { + /** + * Subscribes a given listener to occupancy updates of the chat room. + * + * - Parameters: + * - bufferingPolicy: The ``BufferingPolicy`` for the created subscription. + * + * - Returns: A subscription `AsyncSequence` that can be used to iterate through ``OccupancyEvent`` events. + */ func subscribe(bufferingPolicy: BufferingPolicy) async -> Subscription - /// Same as calling ``subscribe(bufferingPolicy:)`` with ``BufferingPolicy.unbounded``. + + /// Same as calling ``subscribe(bufferingPolicy:)`` with ``BufferingPolicy/unbounded``. /// /// The `Occupancy` protocol provides a default implementation of this method. func subscribe() async -> Subscription + + /** + * Get the current occupancy of the chat room. + * + * - Returns: A current occupancy of the chat room. + */ func get() async throws -> OccupancyEvent + + /** + * Get underlying Ably channel for occupancy events. + * + * - Returns: The underlying Ably channel for occupancy events. + */ var channel: RealtimeChannelProtocol { get } } @@ -17,8 +44,19 @@ public extension Occupancy { } // (CHA-O2) The occupancy event format is shown here (https://sdk.ably.com/builds/ably/specification/main/chat-features/#chat-structs-occupancy-event) + +/** + * Represents the occupancy of a chat room. + */ public struct OccupancyEvent: Sendable, Encodable, Decodable { + /** + * The number of connections to the chat room. + */ public var connections: Int + + /** + * The number of presence members in the chat room - members who have entered presence. + */ public var presenceMembers: Int public init(connections: Int, presenceMembers: Int) { diff --git a/Sources/AblyChat/Presence.swift b/Sources/AblyChat/Presence.swift index bed24de3..5c755940 100644 --- a/Sources/AblyChat/Presence.swift +++ b/Sources/AblyChat/Presence.swift @@ -2,23 +2,124 @@ import Ably public typealias PresenceData = JSONValue +/** + * This interface is used to interact with presence in a chat room: subscribing to presence events, + * fetching presence members, or sending presence events (`enter`, `update`, `leave`). + * + * Get an instance via ``Room/presence``. + */ public protocol Presence: AnyObject, Sendable, EmitsDiscontinuities { + /** + * Same as ``get(params:)``, but with defaults params. + */ func get() async throws -> [PresenceMember] + + /** + * Method to get list of the current online users and returns the latest presence messages associated to it. + * + * - Parameters: + * - params: ``PresenceQuery`` that control how the presence set is retrieved. + * + * - Returns: An array of ``PresenceMember``s. + * + * - Throws: An `ARTErrorInfo`. + */ func get(params: PresenceQuery) async throws -> [PresenceMember] + + /** + * Method to check if user with supplied clientId is online. + * + * - Parameters: + * - clientID: The client ID to check if it is present in the room. + * + * - Returns: A boolean value indicating whether the user is present in the room. + * + * - Throws: An `ARTErrorInfo`. + */ func isUserPresent(clientID: String) async throws -> Bool + + /** + * Method to join room presence, will emit an enter event to all subscribers. Repeat calls will trigger more enter events. + * + * - Parameters: + * - data: The users data, a JSON serializable object that will be sent to all subscribers. + * + * - Throws: An `ARTErrorInfo`. + */ func enter(data: PresenceData) async throws - func enter() async throws + + /** + * Method to update room presence, will emit an update event to all subscribers. If the user is not present, it will be treated as a join event. + * + * - Parameters: + * - data: The users data, a JSON serializable object that will be sent to all subscribers. + * + * - Throws: An `ARTErrorInfo`. + */ func update(data: PresenceData) async throws - func update() async throws + + /** + * Method to leave room presence, will emit a leave event to all subscribers. If the user is not present, it will be treated as a no-op. + * + * - Parameters: + * - data: The users data, a JSON serializable object that will be sent to all subscribers. + * + * - Throws: An `ARTErrorInfo`. + */ func leave(data: PresenceData) async throws - func leave() async throws + + /** + * Subscribes a given listener to a particular presence event in the chat room. + * + * - Parameters: + * - event: A single presence event type ``PresenceEventType`` to subscribe to. + * - bufferingPolicy: The ``BufferingPolicy`` for the created subscription. + * + * - Returns: A subscription `AsyncSequence` that can be used to iterate through ``PresenceEvent`` events. + */ func subscribe(event: PresenceEventType, bufferingPolicy: BufferingPolicy) async -> Subscription - /// Same as calling ``subscribe(event:bufferingPolicy:)`` with ``BufferingPolicy.unbounded``. + + /** + * Subscribes a given listener to different presence events in the chat room. + * + * - Parameters: + * - events: An array of presence event types ``PresenceEventType`` to subscribe to. + * - bufferingPolicy: The ``BufferingPolicy`` for the created subscription. + * + * - Returns: A subscription `AsyncSequence` that can be used to iterate through ``PresenceEvent`` events. + */ + func subscribe(events: [PresenceEventType], bufferingPolicy: BufferingPolicy) async -> Subscription + + /** + * Method to join room presence, will emit an enter event to all subscribers. Repeat calls will trigger more enter events. + * In oppose to ``enter(data:)`` it doesn't publish any custom presence data. + * + * - Throws: An `ARTErrorInfo`. + */ + func enter() async throws + + /** + * Method to update room presence, will emit an update event to all subscribers. If the user is not present, it will be treated as a join event. + * In oppose to ``update(data:)`` it doesn't publish any custom presence data. + * + * - Throws: An `ARTErrorInfo`. + */ + func update() async throws + + /** + * Method to leave room presence, will emit a leave event to all subscribers. If the user is not present, it will be treated as a no-op. + * In oppose to ``leave(data:)`` it doesn't publish any custom presence data. + * + * - Throws: An `ARTErrorInfo`. + */ + func leave() async throws + + /// Same as calling ``subscribe(event:bufferingPolicy:)`` with ``BufferingPolicy/unbounded``. /// /// The `Presence` protocol provides a default implementation of this method. func subscribe(event: PresenceEventType) async -> Subscription - func subscribe(events: [PresenceEventType], bufferingPolicy: BufferingPolicy) async -> Subscription - /// Same as calling ``subscribe(events:bufferingPolicy:)`` with ``BufferingPolicy.unbounded``. + + /// Same as calling ``subscribe(events:bufferingPolicy:)`` with ``BufferingPolicy/unbounded``. /// /// The `Presence` protocol provides a default implementation of this method. func subscribe(events: [PresenceEventType]) async -> Subscription @@ -34,6 +135,9 @@ public extension Presence { } } +/** + * Type for PresenceMember + */ public struct PresenceMember: Sendable { public enum Action: Sendable { case present @@ -70,19 +174,53 @@ public struct PresenceMember: Sendable { self.updatedAt = updatedAt } + /** + * The clientId of the presence member. + */ public var clientID: String - // `nil` means that there is no presence data; this is different to a `JSONValue` of case `.null` + + /** + * The data associated with the presence member. + * `nil` means that there is no presence data; this is different to a `JSONValue` of case `.null` + */ public var data: PresenceData? + + /** + * The current state of the presence member. + */ public var action: Action + // TODO: (https://github.com/ably-labs/ably-chat-swift/issues/13): try to improve this type + + /** + * The extras associated with the presence member. + */ public var extras: (any Sendable)? public var updatedAt: Date } +/** + * Enum representing presence events. + */ public enum PresenceEventType: Sendable { + /** + * Event triggered when a user enters. + */ case enter + + /** + * Event triggered when a user leaves. + */ case leave + + /** + * Event triggered when a user updates their presence data. + */ case update + + /** + * Event triggered when a user initially subscribes to presence. + */ case present internal func toARTPresenceAction() -> ARTPresenceAction { @@ -99,10 +237,28 @@ public enum PresenceEventType: Sendable { } } +/** + * Type for PresenceEvent + */ public struct PresenceEvent: Sendable { + /** + * The type of the presence event. + */ public var action: PresenceEventType + + /** + * The clientId of the client that triggered the presence event. + */ public var clientID: String + + /** + * The timestamp of the presence event. + */ public var timestamp: Date + + /** + * The data associated with the presence event. + */ public var data: PresenceData? public init(action: PresenceEventType, clientID: String, timestamp: Date, data: PresenceData?) { @@ -115,7 +271,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..dde293cd 100644 --- a/Sources/AblyChat/Reaction.swift +++ b/Sources/AblyChat/Reaction.swift @@ -1,15 +1,49 @@ import Foundation +/** + * ``Headers`` type for chat reactions. + */ public typealias ReactionHeaders = Headers + +/** + * ``Metadata`` type for chat reactions. + */ 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/Room.swift b/Sources/AblyChat/Room.swift index cf1c6bff..80e168b1 100644 --- a/Sources/AblyChat/Room.swift +++ b/Sources/AblyChat/Room.swift @@ -1,25 +1,107 @@ import Ably +/** + * Represents a chat room. + */ public protocol Room: AnyObject, Sendable { + /** + * The unique identifier of the room. + * + * - Returns: The room identifier. + */ var roomID: String { get } + + /** + * Allows you to send, subscribe-to and query messages in the room. + * + * - Returns: The messages instance for the room. + */ var messages: any Messages { get } - // To access this property if presence is not enabled for the room is a programmer error, and will lead to `fatalError` being called. + + /** + * Allows you to subscribe to presence events in the room. + * + * - Note: To access this property if presence is not enabled for the room is a programmer error, and will lead to `fatalError` being called. + * + * - Returns: The presence instance for the room. + */ var presence: any Presence { get } - // To access this property if reactions are not enabled for the room is a programmer error, and will lead to `fatalError` being called. + + /** + * Allows you to interact with room-level reactions. + * + * - Note: To access this property if presence is not enabled for the room is a programmer error, and will lead to `fatalError` being called. + * + * - Returns: The room reactions instance for the room. + */ var reactions: any RoomReactions { get } - // To access this property if typing is not enabled for the room is a programmer error, and will lead to `fatalError` being called. + + /** + * Allows you to interact with typing events in the room. + * + * - Note: To access this property if presence is not enabled for the room is a programmer error, and will lead to `fatalError` being called. + * + * - Returns: The typing instance for the room. + */ var typing: any Typing { get } - // To access this property if occupancy is not enabled for the room is a programmer error, and will lead to `fatalError` being called. + + /** + * Allows you to interact with occupancy metrics for the room. + * + * - Note: To access this property if presence is not enabled for the room is a programmer error, and will lead to `fatalError` being called. + * + * - Returns: The occupancy instance for the room. + */ var occupancy: any Occupancy { get } - // TODO: change to `status` + + /** + * The current status of the room. + * + * - Returns: The current room status. + */ var status: RoomStatus { get async } + + /** + * Subscribes a given listener to the room status changes. + * + * - Parameters: + * - bufferingPolicy: The ``BufferingPolicy`` for the created subscription. + * + * - Returns: A subscription `AsyncSequence` that can be used to iterate through ``RoomStatusChange`` events. + */ func onStatusChange(bufferingPolicy: BufferingPolicy) async -> Subscription - /// Same as calling ``onStatusChange(bufferingPolicy:)`` with ``BufferingPolicy.unbounded``. + + /// Same as calling ``onStatusChange(bufferingPolicy:)`` with ``BufferingPolicy/unbounded``. /// /// The `Room` protocol provides a default implementation of this method. func onStatusChange() async -> Subscription + + /** + * Attaches to the room to receive events in realtime. + * + * If a room fails to attach, it will enter either the ``RoomStatus/suspended(error:)`` or ``RoomStatus/failed(error:)`` state. + * + * If the room enters the failed state, then it will not automatically retry attaching and intervention is required. + * + * If the room enters the suspended state, then the call to attach will throw `ARTErrorInfo` with the cause of the suspension. However, + * the room will automatically retry attaching after a delay. + * + * - Throws: An `ARTErrorInfo`. + */ func attach() async throws + + /** + * Detaches from the room to stop receiving events in realtime. + * + * - Throws: An `ARTErrorInfo`. + */ func detach() async throws + + /** + * Returns the room options. + * + * - Returns: A copy of the options used to create the room. + */ var options: RoomOptions { get } } @@ -34,8 +116,18 @@ internal protocol InternalRoom: Room { func release() async } +/** + * Represents a change in the status of the room. + */ public struct RoomStatusChange: Sendable, Equatable { + /** + * The new status of the room. + */ public var current: RoomStatus + + /** + * The previous status of the room. + */ public var previous: RoomStatus public init(current: RoomStatus, previous: RoomStatus) { diff --git a/Sources/AblyChat/RoomFeature.swift b/Sources/AblyChat/RoomFeature.swift index 5a81432f..69ab9bf7 100644 --- a/Sources/AblyChat/RoomFeature.swift +++ b/Sources/AblyChat/RoomFeature.swift @@ -52,9 +52,9 @@ internal protocol FeatureChannel: Sendable, EmitsDiscontinuities { /// /// 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.roomTransitionedToInvalidStateForPresenceOperation(cause:)``. + /// - 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:)``. + /// - 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 f917e891..38590596 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) @@ -397,7 +397,7 @@ internal actor DefaultRoomLifecycleManager Subscription { diff --git a/Sources/AblyChat/RoomOptions.swift b/Sources/AblyChat/RoomOptions.swift index 8259b8e5..c64d0246 100644 --- a/Sources/AblyChat/RoomOptions.swift +++ b/Sources/AblyChat/RoomOptions.swift @@ -1,9 +1,31 @@ import Foundation +/** + * Represents the options for a given chat room. + */ public struct RoomOptions: Sendable, Equatable { + /** + * The presence options for the room. To enable presence in the room, set this property. + * Alternatively, you may use ``RoomOptions/allFeaturesEnabled`` to enable presence with the default options. + */ public var presence: PresenceOptions? + + /** + * The typing options for the room. To enable typing in the room, set this property. + * Alternatively, you may use ``RoomOptions/allFeaturesEnabled`` to enable typing with the default options. + */ public var typing: TypingOptions? + + /** + * The reactions options for the room. To enable reactions in the room, set this property. + * Alternatively, you may use ``RoomOptions/allFeaturesEnabled`` to enable reactions with the default options. + */ public var reactions: RoomReactionsOptions? + + /** + * The occupancy options for the room. To enable occupancy in the room, set this property. + * Alternatively, you may use ``RoomOptions/allFeaturesEnabled`` to enable occupancy with the default options. + */ public var occupancy: OccupancyOptions? /// A `RoomOptions` which enables all room features, using the default settings for each feature. @@ -25,8 +47,25 @@ public struct RoomOptions: Sendable, Equatable { // (CHA-PR9) Users may configure their presence options via the RoomOptions provided at room configuration time. // (CHA-PR9a) Setting enter to false prevents the user from entering presence by means of the ChannelMode on the underlying realtime channel. Entering presence will result in an error. The default is true. // (CHA-PR9b) Setting subscribe to false prevents the user from subscribing to presence by means of the ChannelMode on the underlying realtime channel. This does not prevent them from receiving their own presence messages, but they will not receive them from others. The default is true. + +/** + * Represents the presence options for a chat room. + */ public struct PresenceOptions: Sendable, Equatable { + /** + * Whether the underlying Realtime channel should use the presence enter mode, allowing entry into presence. + * This property does not affect the presence lifecycle, and users must still call ``Presence/enter()`` + * in order to enter presence. + * Defaults to true. + */ public var enter = true + + /** + * Whether the underlying Realtime channel should use the presence subscribe mode, allowing subscription to presence. + * This property does not affect the presence lifecycle, and users must still call ``Presence/subscribe(events:)`` + * in order to subscribe to presence. + * Defaults to true. + */ public var subscribe = true public init(enter: Bool = true, subscribe: Bool = true) { @@ -36,7 +75,16 @@ public struct PresenceOptions: Sendable, Equatable { } // (CHA-T3) Users may configure a timeout interval for when they are typing. This configuration is provided as part of the RoomOptions typing.timeoutMs property, or idiomatic equivalent. The default is 5000ms. + +/** + * Represents the typing options for a chat room. + */ public struct TypingOptions: Sendable, Equatable { + /** + * The timeout for typing events in seconds. If ``Typing/start()`` is not called for this amount of time, a stop + * typing event will be fired, resulting in the user being removed from the currently typing set. + * Defaults to 5 seconds. + */ public var timeout: TimeInterval = 5 public init(timeout: TimeInterval = 5) { @@ -44,10 +92,16 @@ public struct TypingOptions: Sendable, Equatable { } } +/** + * Represents the reactions options for a chat room. + */ public struct RoomReactionsOptions: Sendable, Equatable { public init() {} } +/** + * Represents the occupancy options for a chat room. + */ public struct OccupancyOptions: Sendable, Equatable { public init() {} } diff --git a/Sources/AblyChat/RoomReactions.swift b/Sources/AblyChat/RoomReactions.swift index b804d0fe..48701edc 100644 --- a/Sources/AblyChat/RoomReactions.swift +++ b/Sources/AblyChat/RoomReactions.swift @@ -1,10 +1,40 @@ import Ably +/** + * This interface is used to interact with room-level reactions in a chat room: subscribing to reactions and sending them. + * + * Get an instance via ``Room/reactions``. + */ public protocol RoomReactions: AnyObject, Sendable, EmitsDiscontinuities { + /** + * Send a reaction to the room including some metadata. + * + * - Parameters: + * - params: An object containing `type` and optional `headers` with `metadata`. + * + * - Note: It is possible to receive your own reaction via the reactions subscription before this method returns. + */ func send(params: SendReactionParams) async throws + + /** + * Returns an instance of the Ably realtime channel used for room-level reactions. + * Avoid using this directly unless special features that cannot otherwise be implemented are needed. + * + * - Returns: The realtime channel. + */ var channel: RealtimeChannelProtocol { get } + + /** + * Subscribes a given listener to receive room-level reactions. + * + * - Parameters: + * - bufferingPolicy: The ``BufferingPolicy`` for the created subscription. + * + * - Returns: A subscription `AsyncSequence` that can be used to iterate through ``Reaction`` events. + */ func subscribe(bufferingPolicy: BufferingPolicy) async -> Subscription - /// Same as calling ``subscribe(bufferingPolicy:)`` with ``BufferingPolicy.unbounded``. + + /// Same as calling ``subscribe(bufferingPolicy:)`` with ``BufferingPolicy/unbounded``. /// /// The `RoomReactions` protocol provides a default implementation of this method. func subscribe() async -> Subscription @@ -16,9 +46,41 @@ public extension RoomReactions { } } +/** + * 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/AblyChat/RoomStatus.swift b/Sources/AblyChat/RoomStatus.swift index 6b6f6e45..7d934018 100644 --- a/Sources/AblyChat/RoomStatus.swift +++ b/Sources/AblyChat/RoomStatus.swift @@ -1,15 +1,52 @@ import Ably -// TODO: rename +/** + * The different states that a room can be in throughout its lifecycle. + */ public enum RoomStatus: Sendable, Equatable { + /** + * A temporary state for when the room object is first initialized. + */ case initialized + + /** + * The library is currently attempting to attach the room. + */ case attaching(error: ARTErrorInfo?) + + /** + * The room is currently attached and receiving events. + */ case attached + + /** + * The room is currently detaching and will not receive events. + */ case detaching + + /** + * The room is currently detached and will not receive events. + */ case detached + + /** + * The room is in an extended state of detachment, but will attempt to re-attach when able. + */ case suspended(error: ARTErrorInfo) + + /** + * The room is currently detached and will not attempt to re-attach. User intervention is required. + */ case failed(error: ARTErrorInfo) + + /** + * The room is in the process of releasing. Attempting to use a room in this state may result in undefined behavior. + */ case releasing + + /** + * The room has been released and is no longer usable. + */ case released internal var error: ARTErrorInfo? { diff --git a/Sources/AblyChat/Rooms.swift b/Sources/AblyChat/Rooms.swift index fc3eba98..3a061e21 100644 --- a/Sources/AblyChat/Rooms.swift +++ b/Sources/AblyChat/Rooms.swift @@ -1,8 +1,52 @@ import Ably +/** + * Manages the lifecycle of chat rooms. + */ public protocol Rooms: AnyObject, Sendable { + /** + * Gets a room reference by ID. The Rooms class ensures that only one reference + * exists for each room. A new reference object is created if it doesn't already + * exist, or if the one used previously was released using ``release(roomID:)``. + * + * Always call `release(roomID:)` after the ``Room`` object is no longer needed. + * + * If a call to this method is made for a room that is currently being released, then this it returns only when + * the release operation is complete. + * + * If a call to this method is made, followed by a subsequent call to `release(roomID:)` before the `get(roomID:options:)` returns, then the + * promise will throw an error. + * + * - Parameters: + * - roomID: The ID of the room. + * - options: The options for the room. + * + * - Returns: A new or existing `Room` object. + * + * - Throws: `ARTErrorInfo` if a room with the same ID but different options already exists. + */ func get(roomID: String, options: RoomOptions) async throws -> any Room + + /** + * Release the ``Room`` object if it exists. This method only releases the reference + * to the Room object from the Rooms instance and detaches the room from Ably. It does not unsubscribe to any + * events. + * + * After calling this function, the room object is no-longer usable. If you wish to get the room object again, + * you must call ``Rooms/get(roomID:options:)``. + * + * Calling this function will abort any in-progress `get(roomID:options:)` calls for the same room. + * + * - Parameters: + * - roomID: The ID of the room. + */ func release(roomID: String) async + + /** + * Get the client options used to create the chat instance. + * + * - Returns: ``ClientOptions`` object. + */ var clientOptions: ClientOptions { get } } diff --git a/Sources/AblyChat/Typing.swift b/Sources/AblyChat/Typing.swift index 7c3ea589..49b9d432 100644 --- a/Sources/AblyChat/Typing.swift +++ b/Sources/AblyChat/Typing.swift @@ -1,14 +1,59 @@ import Ably +/** + * This interface is used to interact with typing in a chat room including subscribing to typing events and + * fetching the current set of typing clients. + * + * Get an instance via ``Room/typing``. + */ public protocol Typing: AnyObject, Sendable, EmitsDiscontinuities { + /** + * Subscribes a given listener to all typing events from users in the chat room. + * + * - Parameters: + * - bufferingPolicy: The ``BufferingPolicy`` for the created subscription. + * + * - Returns: A subscription `AsyncSequence` that can be used to iterate through ``TypingEvent`` events. + */ func subscribe(bufferingPolicy: BufferingPolicy) async -> Subscription - /// Same as calling ``subscribe(bufferingPolicy:)`` with ``BufferingPolicy.unbounded``. + + /// Same as calling ``subscribe(bufferingPolicy:)`` with ``BufferingPolicy/unbounded``. /// /// The `Typing` protocol provides a default implementation of this method. func subscribe() async -> Subscription + + /** + * Get the current typers, a set of clientIds. + * + * - Returns: A set of clientIds that are currently typing. + */ func get() async throws -> Set + + /** + * Start indicates that the current user is typing. This will emit a ``TypingEvent`` event to inform listening clients and begin a timer, + * once the timer expires, another ``TypingEvent`` event will be emitted. In both cases ``TypingEvent/currentlyTyping`` + * contains a list of userIds who are currently typing. + * + * The timeout is configurable through the ``TypingOptions/timeout`` parameter. + * If the current user is already typing, it will reset the timer and begin counting down again without emitting a new event. + * + * - Throws: An `ARTErrorInfo`. + */ func start() async throws + + /** + * Stop indicates that the current user has stopped typing. This will emit a ``TypingEvent`` event to inform listening clients, + * and immediately clear the typing timeout timer. + * + * - Throws: An `ARTErrorInfo`. + */ func stop() async throws + + /** + * Get the Ably realtime channel underpinning typing events. + * + * - Returns: The Ably realtime channel. + */ var channel: RealtimeChannelProtocol { get } } @@ -18,7 +63,13 @@ public extension Typing { } } +/** + * Represents a typing event. + */ public struct TypingEvent: Sendable { + /** + * Get a set of clientIds that are currently typing. + */ public var currentlyTyping: Set public init(currentlyTyping: Set) { diff --git a/Sources/BuildTool/BuildTool.swift b/Sources/BuildTool/BuildTool.swift index e1a01903..00a9cb1b 100644 --- a/Sources/BuildTool/BuildTool.swift +++ b/Sources/BuildTool/BuildTool.swift @@ -15,6 +15,7 @@ struct BuildTool: AsyncParsableCommand { GenerateMatrices.self, Lint.self, SpecCoverage.self, + BuildDocumentation.self, ] ) } @@ -634,7 +635,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 @@ -668,3 +669,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/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