{"id":24168422,"url":"https://github.com/ably/ably-chat-swift","last_synced_at":"2025-03-02T09:42:56.413Z","repository":{"id":253395044,"uuid":"836259317","full_name":"ably/ably-chat-swift","owner":"ably","description":"Ably Chat SDK for Swift to build chat experiences at scale ","archived":false,"fork":false,"pushed_at":"2025-02-24T17:11:37.000Z","size":1493,"stargazers_count":1,"open_issues_count":46,"forks_count":1,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-02-24T17:42:41.364Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ably.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-31T13:24:50.000Z","updated_at":"2025-02-24T16:33:18.000Z","dependencies_parsed_at":"2024-08-26T20:29:55.035Z","dependency_job_id":"35abc4a6-c088-4f61-aae5-e5cb5757f120","html_url":"https://github.com/ably/ably-chat-swift","commit_stats":{"total_commits":109,"total_committers":3,"mean_commits":"36.333333333333336","dds":0.07339449541284404,"last_synced_commit":"5344d7f09d11062462d6204a3de2df64e0c2d250"},"previous_names":["ably-labs/ably-chat-swift","ably/ably-chat-swift"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ably%2Fably-chat-swift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ably%2Fably-chat-swift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ably%2Fably-chat-swift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ably%2Fably-chat-swift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ably","download_url":"https://codeload.github.com/ably/ably-chat-swift/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241488154,"owners_count":19970826,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-12T22:08:46.939Z","updated_at":"2025-03-02T09:42:56.406Z","avatar_url":"https://github.com/ably.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ably Chat SDK for Swift\n\n\u003cp style=\"text-align: left\"\u003e\n    \u003cimg src=\"https://badgen.net/github/license/3scale/saas-operator\" alt=\"License\" /\u003e\n    \u003cimg alt=\"GitHub Release\" src=\"https://img.shields.io/github/v/release/ably/ably-chat-swift\"\u003e\n\u003c/p\u003e\n\nAbly Chat is a set of purpose-built APIs for a host of chat features enabling you to create 1:1, 1:Many, Many:1 and Many:Many chat rooms for\nany scale. It is designed to meet a wide range of chat use cases, such as livestreams, in-game communication, customer support, or social\ninteractions in SaaS products. Built on [Ably's](https://ably.com/) core service, it abstracts complex details to enable efficient chat\narchitectures.\n\nGet started using the [📚 documentation](https://ably.com/docs/products/chat).\n\n![Ably Chat Header](/images/ably-chat-github-header.png)\n\n## Supported Platforms\n\n- macOS 11 and above\n- iOS 14 and above\n- tvOS 14 and above\n\n## Requirements\n\nXcode 16 or later.\n\n## Installation\n\nThe SDK is distributed as a Swift package and can hence be installed using Xcode (search for `github.com/ably/ably-chat-swift` package) or by adding it as a dependency in your package’s `Package.swift`:\n\n```swift\n.package(url: \"https://github.com/ably/ably-chat-swift\", from: \"0.1.0\")\n```\n\n## Supported chat features\n\nThis project is under development so we will be incrementally adding new features. At this stage, you'll find APIs for the following chat\nfeatures:\n\n- Chat rooms for 1:1, 1:many, many:1 and many:many participation.\n- Sending and receiving chat messages.\n- Online status aka presence of chat participants.\n- Chat room occupancy, i.e total number of connections and presence members.\n- Typing indicators\n- Room-level reactions (ephemeral at this stage)\n\nIf there are other features you'd like us to prioritize, please [let us know](https://forms.gle/mBw9M53NYuCBLFpMA).\n\n## Usage\n\nYou will need the following prerequisites:\n\n- An Ably account\n\n  - You can [sign up](https://ably.com/signup) to the generous free tier.\n\n- An Ably API key\n  - Use the default or create a new API key in an app within\n    your [Ably account dashboard](https://ably.com/dashboard).\n  - Make sure your API key has the\n    following [capabilities](https://ably.com/docs/auth/capabilities): `publish`, `subscribe`, `presence`, `history` and\n    `channel-metadata`.\n\nTo instantiate the Chat SDK, create an [Ably client](https://ably.com/docs/getting-started/setup) and pass it into the Chat constructor:\n\n```swift\nimport Ably\nimport AblyChat\n\nlet realtimeOptions = ARTClientOptions()\nrealtimeOptions.key = \"\u003cAPI_KEY\u003e\"\nrealtimeOptions.clientId = \"\u003cclientId\u003e\"\nlet realtime = ARTRealtime(options: realtimeOptions)\nlet chatClient = DefaultChatClient(realtime: realtime, clientOptions: nil)\n```\n\nYou can use [basic authentication](https://ably.com/docs/auth/basic) i.e. the API Key directly for testing purposes,\nhowever it is strongly recommended that you use [token authentication](https://ably.com/docs/auth/token) in production\nenvironments.\n\nTo use Chat you must also set a [`clientId`](https://ably.com/docs/auth/identified-clients) so that users are\nidentifiable.\n\n## Connections\n\nThe Chat SDK uses a single connection to Ably, which is exposed via the `ChatClient#connection` property. You can use this\nproperty to observe the connection state and take action accordingly.\n\n### Current connection status\n\nYou can view the current connection status at any time:\n\n```swift\nlet status = await chatClient.connection.status\nlet error = await chatClient.connection.error\n```\n\n### Subscribing to connection status changes\n\nTo subscribe to connection status changes, create a subscription with the `onStatusChange` method. You can then iterate over it using its `AsyncSequence` interface:\n\n```swift\nlet subscription = chatClient.connection.onStatusChange()\nfor await statusChange in subscription {\n    print(\"Connection status changed to: \\(statusChange.current)\")\n}\n```\n\n## Chat rooms\n\n### Creating or retrieving a chat room\n\nYou can create or retrieve a chat room with name `\"basketball-stream\"` this way:\n\n```swift\nlet room = try await chatClient.rooms.get(roomID: \"basketball-stream\", options: RoomOptions())\n```\n\nThe second argument to `rooms.get` is a `RoomOptions` argument, which tells the Chat SDK what features you would like your room to use and\nhow they should be configured.\n\nYou can also use `RoomOptions.allFeaturesEnabled` to enable all room features with the default configuration.\n\nFor example, you can set the timeout between keystrokes for typing events as part of the room options. Sensible defaults for each of the\nfeatures are provided for your convenience:\n\n- A typing timeout (time of inactivity before typing stops) of 5 seconds.\n- Entry and subscription to presence.\n\nHere’s an example demonstrating how to specify a custom typing timeout of 3 seconds:\n\n```swift\nlet room = try await chatClient.rooms.get(roomID: \"basketball-stream\",\n                                          options: .init(typing: TypingOptions(timeout: 3.0)))\n```\n\nIn order to use the same room but with different options, you must first `release` the room before requesting an instance with the changed\noptions (see below for more information on releasing rooms).\n\nNote that:\n\n- If a `release` call is currently in progress for the room (see below), then a call to `get` will wait for that to complete before completing\n  itself.\n- If a `get` call is currently in progress for the room and `release` is called, the `get` call will fail.\n\n### Attaching to a room\n\nTo start receiving events on a room, it must first be attached. This can be done using the `attach` method:\n\n```swift\ntry await room.attach()\n```\n\n### Detaching from a room\n\nTo stop receiving events on a room, it must be detached, which can be achieved by using the `detach` method:\n\n```swift\nroom.detach()\n```\n\nNote: This does not remove any event listeners you have registered and they will begin to receive events again in the\nevent that the room is re-attached.\n\n### Releasing a room\n\nDepending on your application, you may have multiple rooms that come and go over time (e.g. if you are running 1:1 support chat). When you\nare completely finished with a room, you may `release` it which allows the underlying resources to be collected:\n\n```swift\n_ = try await rooms.release(roomID: \"basketball-stream\")\n```\n\nOnce `release` is called, the room will become unusable and you will need to get a new instance using `rooms.get`.\n\n\u003e [!NOTE]\n\u003e Releasing a room may be optional for many applications. If release is not called, the server will automatically tidy up\n\u003e connections and other resources associated with the room after a period of time.\n\n### Monitoring room status\n\nMonitoring the status of the room is key to a number of common chat features. For example, you might want to display a warning when the room\nhas become detached.\n\n### Current status of a room\n\nTo get the current status (and error), you can use the `status` property like this:\n\n```swift\nswitch await room.status {\n   case let .failed(error: error):\n      // use error\n   }\n   ...\n}\n```\n\n### Listening to room status updates\n\nYou can also subscribe to changes in the room status and be notified whenever they happen by creating a subscription using the room’s `onStatusChange` method and then iterating over this subscription using its `AsyncSequence` interface:\n\n```swift\nlet statusSubscription = try await room.onStatusChange()\nfor await status in statusSubscription {\n    print(\"Room status: \\(status)\")\n}\n```\n\n## Handling discontinuity\n\nThere may be instances where the connection to Ably is lost for a period of time, for example, when the user enters a tunnel. In many\ncircumstances, the connection will recover and operation will continue with no discontinuity of messages. However, during extended\nperiods of disconnection, continuity cannot be guaranteed and you'll need to take steps to recover messages you might have missed.\n\nEach feature of the Chat SDK provides an `onDiscontinuity` method. Here you can create a subscription that will emit a discontinuity event on its `AsyncSequence` interface whenever a\ndiscontinuity in that feature has been observed.\n\nTaking messages as an example, you can listen for discontinuities like so:\n\n```swift\nlet subscription = room.messages.onDiscontinuity()\nfor await discontinuityEvent in subscription {\n    print(\"Recovering from the error: \\(discontinuityEvent.error)\")\n}\n```\n\n## Chat messages\n\n### Subscribing to incoming messages\n\nTo subscribe to incoming messages you create a subscription for the room `messages` object:\n\n```swift\nlet messagesSubscription = try await room.messages.subscribe()\nfor await message in messagesSubscription {\n    print(\"Message received: \\(message)\")\n}\n```\n\n### Sending messages\n\nTo send a message, simply call `send` on the room `messages` property, with the message you want to send:\n\n```swift\nlet message = try await room.messages.send(params: .init(text: \"hello\"))\n```\n\n### Retrieving message history\n\nThe messages object also exposes the `get` method which can be used to request historical messages in the chat room according\nto the given criteria. It returns a paginated response that can be used to request more messages:\n\n```swift\nlet paginatedResult = try await room.messages.get(options: .init(orderBy: .newestFirst))\nprint(paginatedResult.items)\n\nif paginatedResult.hasNext {\n    let next = try await paginatedResult.next!\n    print(next.items)\n} else {\n    print(\"End of messages\")\n}\n```\n\n### Retrieving message history for a subscribed listener\n\nThe return value from `messages.subscribe` includes the `getPreviousMessages`\nmethod, which can be used to request historical messages in the chat room that were sent up to the point that a particular listener was subscribed. It returns a\npaginated response that can be used to request for more messages:\n\n```swift\nlet messagesSubscription = try await room.messages.subscribe()\nlet paginatedResult = try await messagesSubscription.getPreviousMessages(params: .init(limit: 50)) // `orderBy` here is ignored and always `newestFirst`\nprint(paginatedResult.items)\n\nif paginatedResult.hasNext {\n    let next = try await paginatedResult.next!\n    print(next.items)\n} else {\n    print(\"End of messages\")\n}\n```\n\n## Online status\n\n### Retrieving online members\n\nYou can get the complete list of currently online or present members, their state and data, by calling the `presence.get()` method which returns\na list of the presence messages, where each message contains the most recent data for a member:\n\n```swift\n// Retrieve all users entered into presence as an array:\nlet presentMembers = try await room.presence.get()\n\n// Retrieve the status of specific users by their clientId:\nlet presentMember = try await room.presence.get(params: .init(clientID: \"clemons123\"))\n\n// To check whether the user is online or not:\nlet isPresent = try await room.presence.isUserPresent(clientID: \"clemons123\")\n```\n\n### Entering the presence set\n\nTo appear online for other users, you can enter the presence set of a chat room. While entering presence, you can provide optional data that\nwill be associated with the presence message (can be a nested dictionary):\n\n```swift\ntry await room.presence.enter(data: [\"status\": \"Online\"])\n```\n\n### Updating the presence data\n\nUpdates allow you to make changes to the custom data associated with a present user. Common use-cases include updating the user's status:\n\n```swift\ntry await room.presence.update(data: [\"status\": \"Busy\"])\n```\n\n### Leaving the presence set\n\nAbly automatically triggers a presence leave if a client goes offline. But you can also manually leave the presence set as a result of a UI\naction. While leaving presence, you can provide optional data that will be associated with the presence message:\n\n```swift\ntry await room.presence.leave(data: [\"status\": \"Bye!\"])\n```\n\n### Subscribing to presence updates\n\nYou can create a single subscription for all presence event types:\n\n```swift\nlet presenceSubscription = try await room.presence.subscribe(events: [.enter, .leave, .update])\nfor await event in presenceSubscription {\n    print(\"Presence event `\\(event.action)` from `\\(event.clientId)` with data `\\(event.data)`\")\n}\n```\n\n## Typing indicators\n\n\u003e [!NOTE]\n\u003e You should be attached to the room to enable this functionality.\n\nTyping events allow you to inform others that a client is typing and also subscribe to others' typing status.\n\n### Retrieving the set of current typers\n\nYou can get the complete set of the current typing `clientId`s, by calling the `typing.get` method.\n\n```swift\n// Retrieve the entire list of currently typing clients\nlet currentlyTypingClientIds = try await room.typing.get()\n```\n\n### Start typing\n\nTo inform other users that you are typing, you can call the start method. This will begin a timer that will automatically stop typing after\na set amount of time.\n\n```swift\ntry await room.typing.start()\n```\n\nRepeated calls to start will reset the timer, so the clients typing status will remain active.\n\n### Stop typing\n\nYou can immediately stop typing without waiting for the timer to expire.\n\n```swift\ntry await room.typing.start()\n// Some short delay - timer not yet expired\n\ntry await room.typing.stop()\n// Timer cleared and stopped typing event emitted and listeners are notified\n```\n\n### Subscribing to typing updates\n\nTo subscribe to typing events, create a subscription with the `subscribe` method. You can then iterate over it using its `AsyncSequence` interface:\n\n```swift\nlet typingSubscription = try await room.typing.subscribe()\nfor await typing in typingSubscription {\n    typingInfo = typing.currentlyTyping.isEmpty ? \"\" : \"Typing: \\(typing.currentlyTyping.joined(separator: \", \"))...\"\n}\n```\n\n## Occupancy of a chat room\n\nOccupancy tells you how many users are connected to the chat room.\n\n### Subscribing to occupancy updates\n\nTo subscribe to occupancy updates, create a subscription by calling the `subscribe` method on the chat room’s `occupancy` member. You can then iterate over it using its `AsyncSequence` interface:\n\n```swift\nlet occupancySubscription = try await room.occupancy.subscribe()\nfor await event in occupancySubscription {\n    occupancyInfo = \"Connections: \\(event.presenceMembers) (\\(event.connections))\"\n}\n```\n\nOccupancy updates are delivered in near-real-time, with updates in quick succession batched together for performance.\n\n### Retrieving the occupancy of a chat room\n\nYou can request the current occupancy of a chat room using the `occupancy.get` method:\n\n```swift\nlet occupancy = try await room.occupancy.get()\n```\n\n## Room-level reactions\n\nYou can subscribe to and send ephemeral room-level reactions by using the room `reactions` object.\nTo send room-level reactions, you must be [attached](#attaching-to-a-room) to the room.\n\n### Sending a reaction\n\nTo send a reaction such as `like`:\n\n```swift\ntry await room.reactions.send(params: .init(type: \"like\"))\n```\n\nYou can also add any metadata and headers to reactions:\n\n```swift\ntry await room.reactions.send(params: .init(type: \"🎉\", metadata: [\"effect\": \"fireworks\"]))\n```\n\n### Subscribing to room reactions\n\nSubscribe to receive room-level reactions:\n\n```swift\nlet reactionSubscription = try await room.reactions.subscribe()\nfor await reaction in reactionSubscription {\n    print(\"Received a reaction of type \\(reaction.type), and metadata \\(reaction.metadata)\")\n}\n```\n\n## Example app\n\nThis repository contains an example app, written using SwiftUI, which demonstrates how to use the SDK. The code for this app is in the [`Example`](Example) directory.\n\nIn order to allow the app to use modern SwiftUI features, it supports the following OS versions:\n\n- macOS 14 and above\n- iOS 17 and above\n- tvOS 17 and above\n\nTo run the app, open the `AblyChat.xcworkspace` workspace in Xcode and run the `AblyChatExample` target. If you wish to run it on an iOS or tvOS device, you’ll need to set up code signing.\n\n## In-depth\n\n### Channels Behind Chat Features\n\nIt might be useful to know that each feature is backed by an underlying Pub/Sub channel. You can use this information to enable\ninteroperability with other platforms by subscribing to the channels directly using\nthe [Ably Pub/Sub SDKs](https://ably.com/docs/products/channels) for those platforms.\n\nThe channel for each feature can be obtained via the `channel` property\non that feature.\n\n```swift\nlet messagesChannel = room.messages.channel\n```\n\n**Warning**: You should not attempt to change the state of a channel directly. Doing so may cause unintended side-effects in the Chat SDK.\n\n### Channels Used\n\nFor a given chat room, the channels used for features are as follows:\n\n| Feature   | Channel                              |\n| --------- | ------------------------------------ |\n| Messages  | `\u003croomId\u003e::$chat::$chatMessages`     |\n| Presence  | `\u003croomId\u003e::$chat::$chatMessages`     |\n| Occupancy | `\u003croomId\u003e::$chat::$chatMessages`     |\n| Reactions | `\u003croomId\u003e::$chat::$reactions`        |\n| Typing    | `\u003croomId\u003e::$chat::$typingIndicators` |\n\n---\n\n## Contributing\n\nFor guidance on how to contribute to this project, see the [contributing guidelines](CONTRIBUTING.md).\n\n## Support, feedback and troubleshooting\n\nPlease visit http://support.ably.com/ for access to our knowledge base and to ask for any assistance. You can also view\nthe community reported [Github issues](https://github.com/ably/ably-chat-swift/issues) or raise one yourself.\n\nTo see what has changed in recent versions, see the [changelog](CHANGELOG.md).\n\n[Share feedback or request](https://forms.gle/mBw9M53NYuCBLFpMA) a new feature.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fably%2Fably-chat-swift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fably%2Fably-chat-swift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fably%2Fably-chat-swift/lists"}