From f23127919c5f70fe42ad8f6699e64565eb910739 Mon Sep 17 00:00:00 2001 From: shreeyajoshi-cometchat Date: Thu, 10 Sep 2026 16:38:24 +0530 Subject: [PATCH 1/4] docs(ios): restructure pin/save/threads and drop the fictional enableThreadSubscription MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Core features now match the other platforms: - Pin Message and Save Message merge into one "Pin & Save Messages" section. iOS was the only platform splitting them. - Add "Pin Conversations", which iOS shipped in 5.1.22 undocumented. - Thread Subscription becomes a subsection of Threaded Conversations rather than a top-level section. Every component-level `enableThreadSubscription` the docs described does not exist. The only real gate is on UIKitSettings, and the components expose the `hideThreadSubscription*` half — which is what the other platforms document too. Removed from the message list, both headers, the pinned-messages page (invented wholesale there) and the guide, folding the behaviour each section described into the flag that does exist. `hideThreadSubscriptionOption` is likewise not a CometChatMessageList property; it lives on AdditionalConfiguration, so the sample now sets it there. Co-Authored-By: Claude Opus 5 (1M context) --- ui-kit/ios/core-features.mdx | 61 +++++++++++++----------- ui-kit/ios/guide-thread-subscription.mdx | 20 +++----- ui-kit/ios/message-header.mdx | 38 +++++---------- ui-kit/ios/message-list.mdx | 35 ++++---------- ui-kit/ios/pinned-messages.mdx | 13 ----- ui-kit/ios/threaded-messages-header.mdx | 6 +-- 6 files changed, 64 insertions(+), 109 deletions(-) diff --git a/ui-kit/ios/core-features.mdx b/ui-kit/ios/core-features.mdx index 4f85399c3..449eab7e3 100644 --- a/ui-kit/ios/core-features.mdx +++ b/ui-kit/ios/core-features.mdx @@ -561,7 +561,7 @@ class ThreadedMessagesViewController: UIViewController { **Component Used:** - [ThreadedMessages](/ui-kit/ios/threaded-messages-header) - Displays thread replies -## Thread Subscription +### Thread Subscription *Available since v5.1.22* @@ -579,10 +579,9 @@ class ThreadSubscriptionViewController: UIViewController { let messageListView = CometChatMessageList() - // Off by default — opt in. Adds a "Subscribe to thread" / - // "Unsubscribe from thread" option to the message action sheet, - // on parent messages only. - messageListView.enableThreadSubscription = true + // The "Subscribe to thread" / "Unsubscribe from thread" option is + // added to the message action sheet on parent messages only. Hide it + // on one screen with hideThreadSubscriptionOption. } } @@ -595,9 +594,6 @@ extension ViewController: CometChatThreadEventListener { } ``` -**Component Used:** -- [MessageList](/ui-kit/ios/message-list) - Hosts the subscribe/unsubscribe option - See the [Thread Subscription guide](/ui-kit/ios/guide-thread-subscription) for the full feature. ## Group Chat @@ -712,72 +708,81 @@ class QuotedReplyViewController: UIViewController { } ``` -## Pin Message +## Pin & Save Messages *Available since v5.1.22* -Pin an important message so everyone in the conversation can find it. Pinning is restricted to group owners, admins and moderators; everyone else sees the pin indicator and the pinned list. +Keep important messages in reach. **Pinning** highlights a message for everyone in the conversation and is restricted to group owners, admins and moderators — everyone else sees the pin indicator and the pinned list. **Saving** bookmarks a message privately for the acting user and spans every conversation, so saves are listed on a user-level screen rather than a per-conversation one. ```swift lines import UIKit import CometChatUIKitSwift import CometChatSDK -class PinMessageViewController: UIViewController { +class PinSaveMessagesViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let messageListView = CometChatMessageList() - // Pin is off by default — opt in, and the feature must also be - // enabled for your app (CometChat.isPinMessageEnabled()). + // Both are off by default — opt in, and each feature must also be + // enabled for your app (CometChat.isPinMessageEnabled() and + // CometChat.isSaveMessageEnabled()). messageListView.enablePinMessage = true + messageListView.enableSaveMessage = true - // Open the per-conversation pinned list + // Pinned messages are per-conversation. let pinnedVC = CometChatPinnedMessages(group: group) pinnedVC.set(onMessageClicked: { [weak self] message in self?.navigationController?.popViewController(animated: true) messageListView.goToMessage(withId: message.id) }) navigationController?.pushViewController(pinnedVC, animated: true) + + // Saved messages span every conversation, so open this from app chrome. + let savedVC = CometChatSavedMessages() + savedVC.hideNavigationBar = false + navigationController?.pushViewController(savedVC, animated: true) } } ``` -See the [Pin & Save Messages guide](/ui-kit/ios/guide-pin-save-message) and [Pinned Messages](/ui-kit/ios/pinned-messages). +**Components Used:** +- [MessageList](/ui-kit/ios/message-list) - Adds Pin, Unpin, Save and Unsave to the message options, and shows the bubble indicators +- [Pinned Messages](/ui-kit/ios/pinned-messages) - The messages pinned in one conversation +- [Saved Messages](/ui-kit/ios/saved-messages) - The current user's saved messages, across every conversation +- [Conversations](/ui-kit/ios/conversations) - Lets users pin a whole conversation to the top of their list + +See the [Pin & Save Messages guide](/ui-kit/ios/guide-pin-save-message) for end-to-end wiring. -## Save Message +## Pin Conversations *Available since v5.1.22* -Save a message privately for later. Saves are visible only to the user who made them and span every conversation, so they are listed on a user-level screen rather than a per-conversation one. +Keep the chats that matter at the top. Users pin a conversation from its swipe action; pinned conversations show an indicator and stay above the rest of the list. An admin can also pin a conversation for everyone — those rows are not the user's to unpin, so the affordance is not offered. ```swift lines import UIKit import CometChatUIKitSwift import CometChatSDK -class SaveMessageViewController: UIViewController { +class PinConversationsViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - let messageListView = CometChatMessageList() - - // Save is off by default — opt in, and the feature must also be - // enabled for your app (CometChat.isSaveMessageEnabled()). - messageListView.enableSaveMessage = true + let conversationsView = CometChatConversations() - // Saved messages span every conversation, so open this from app chrome - let savedVC = CometChatSavedMessages() - savedVC.hideNavigationBar = false - navigationController?.pushViewController(savedVC, animated: true) + // Off by default — opt in, and the feature must also be enabled + // for your app. + conversationsView.enablePinConversation = true } } ``` -See the [Pin & Save Messages guide](/ui-kit/ios/guide-pin-save-message) and [Saved Messages](/ui-kit/ios/saved-messages). +**Component Used:** +- [Conversations](/ui-kit/ios/conversations) - Provides the pin/unpin option, the row indicator, and pinned-first ordering ## Search diff --git a/ui-kit/ios/guide-thread-subscription.mdx b/ui-kit/ios/guide-thread-subscription.mdx index 6741a7082..4d5c25f43 100644 --- a/ui-kit/ios/guide-thread-subscription.mdx +++ b/ui-kit/ios/guide-thread-subscription.mdx @@ -19,26 +19,19 @@ The UI Kit ships the toggle as a **Subscribe to thread / Unsubscribe from thread - Threaded messages working in your app — see [Threaded Messages](/ui-kit/ios/guide-threaded-messages). - CometChat UI Kit for iOS v5.1.22 or later, with Chat SDK v4.1.9 or later. -## Enable the Feature +## The Surfaces -Thread subscription is **off by default**. Opt in per [CometChatMessageList](/ui-kit/ios/message-list) instance. When the gate is off, the option never renders and no subscription request is ever made. - -```swift -let messageListView = CometChatMessageList() -messageListView.enableThreadSubscription = true // opt in — default is false -``` - -Set it on **both** your main message list and the message list inside your thread screen, so the option is available in either place. +Thread subscription surfaces in two places: the message action sheet on [CometChatMessageList](/ui-kit/ios/message-list), and a bell on the thread header. Each can be hidden per instance with `hideThreadSubscriptionOption` and `hideThreadSubscriptionButton` respectively. ## Surface 1: The Message Action Sheet Option -With the gate on, `CometChatMessageList` adds a **Subscribe to thread** / **Unsubscribe from thread** option to the long-press action sheet. The label and icon reflect the current state, read synchronously from the SDK when the sheet is built. +`CometChatMessageList` adds a **Subscribe to thread** / **Unsubscribe from thread** option to the long-press action sheet. The label and icon reflect the current state, read synchronously from the SDK when the sheet is built. The option appears only on **parent messages** — never on a reply inside a thread: ```swift // The kit's gate, for reference: -// enableThreadSubscription && parentMessageId == 0 && !hideThreadSubscriptionOption +// parentMessageId == 0 && !hideThreadSubscriptionOption ``` @@ -65,12 +58,11 @@ To replace the kit's behavior with your own, supply an `onItemClick` on a custom [CometChatMessageHeader](/ui-kit/ios/message-header) renders a subscribe/unsubscribe bell in its trailing area. Set `parentMessage` to put the header in **thread mode** — the bell renders only then, so a conversation header is unaffected. As with the action-sheet option, it renders in one-on-one threads as well as groups. -[CometChatThreadedMessageHeader](/ui-kit/ios/threaded-messages-header) can render the same bell on its reply-count bar instead, for a screen with no header bar. Enable it on exactly one of the two — never both. +[CometChatThreadedMessageHeader](/ui-kit/ios/threaded-messages-header) renders the same bell on its reply-count bar, for a screen with no header bar. Hide it on one of the two with `hideThreadSubscriptionButton` so a screen never shows two bells. ```swift let messageHeaderView = CometChatMessageHeader() messageHeaderView.set(parentMessage: parentMessage) // puts the header in thread mode -messageHeaderView.set(enableThreadSubscription: true) // off by default ``` The bell tracks state on its own: it reads the current subscription state from the SDK, flips optimistically on tap, reverts if the request fails, toasts in both directions, and emits `ccThreadSubscriptionChanged` on success. You do not wire any of that up. @@ -83,7 +75,7 @@ messageHeaderView.set(hideThreadSubscriptionButton: true) -The bell needs `parentMessage` and `enableThreadSubscription`, on a parent whose message has been sent. Setting the flag alone on a conversation header renders nothing — that is deliberate, so turning the feature on globally cannot put a thread control on a non-thread screen. +The bell needs `parentMessage`, on a parent whose message has been sent. Without it a conversation header renders nothing — that is deliberate, so a thread control can never appear on a non-thread screen. diff --git a/ui-kit/ios/message-header.mdx b/ui-kit/ios/message-header.mdx index 065d2f874..ff26ef182 100644 --- a/ui-kit/ios/message-header.mdx +++ b/ui-kit/ios/message-header.mdx @@ -51,8 +51,7 @@ The `CometChatMessageHeader` component displays user or group details in the too "disableTyping": { "type": "Bool", "default": false } }, "threads": { - "parentMessage": { "type": "BaseMessage?", "default": "nil", "note": "Set to put the header in thread mode" }, - "enableThreadSubscription": { "type": "Bool", "default": false, "note": "Also requires parentMessage" } + "parentMessage": { "type": "BaseMessage?", "default": "nil", "note": "Set to put the header in thread mode; the subscribe bell renders only in thread mode" } }, "style": { "avatarStyle": { "type": "AvatarStyle", "default": "AvatarStyle()" }, @@ -885,29 +884,6 @@ let messageHeader = CometChatMessageHeader() messageHeader.disableTyping = true ``` -### enableThreadSubscription - -*Available since v5.1.22* - -Enables the subscribe/unsubscribe bell in the header's trailing area, letting users opt in to notifications for a thread's replies. The bell **also** requires `parentMessage` — setting this flag alone on a conversation header renders nothing, so turning the feature on globally cannot put a thread control on a non-thread screen. It renders in one-on-one threads as well as groups. - -The bell manages itself: it reads the current state from the SDK, flips optimistically on tap, reverts on failure, toasts in both directions, and stays in step with a toggle made from the message action sheet. - -| | | -|---|---| -| Type | `Bool` | -| Default | `false` | - -```swift lines -import CometChatUIKitSwift - -let messageHeader = CometChatMessageHeader() -messageHeader.set(parentMessage: parentMessage) -messageHeader.set(enableThreadSubscription: true) -``` - -See the [Thread Subscription guide](/ui-kit/ios/guide-thread-subscription) for the full feature. - ### hideBackButton Hides the back button in the header. @@ -969,6 +945,18 @@ Hides the subscribe/unsubscribe bell while leaving the feature on — for a scre | Type | `Bool` | | Default | `false` | +The bell renders in the header's trailing area only when `parentMessage` is set, so a conversation header never shows a thread control. It appears in one-on-one threads as well as groups, and manages itself: it reads the current state from the SDK, flips optimistically on tap, reverts on failure, toasts in both directions, and stays in step with a toggle made from the message action sheet. + +```swift lines +import CometChatUIKitSwift + +let messageHeader = CometChatMessageHeader() +messageHeader.set(parentMessage: parentMessage) +messageHeader.set(hideThreadSubscriptionButton: true) +``` + +See the [Thread Subscription guide](/ui-kit/ios/guide-thread-subscription) for the full feature. + ### hideVideoCallButton Hides the video call button. diff --git a/ui-kit/ios/message-list.mdx b/ui-kit/ios/message-list.mdx index 07995141c..f8efb3ef2 100644 --- a/ui-kit/ios/message-list.mdx +++ b/ui-kit/ios/message-list.mdx @@ -58,7 +58,6 @@ The `CometChatMessageList` component displays a scrollable list of messages in a "hideMessagePrivatelyOption": { "type": "Bool", "default": false }, "hidePinMessageOption": { "type": "Bool", "default": false }, "hideSaveMessageOption": { "type": "Bool", "default": false }, - "hideThreadSubscriptionOption": { "type": "Bool", "default": false }, "hideGroupActionMessages": { "type": "Bool", "default": false }, "hideNewMessageIndicator": { "type": "Bool", "default": false }, "hideEmptyView": { "type": "Bool", "default": false }, @@ -74,7 +73,6 @@ The `CometChatMessageList` component displays a scrollable list of messages in a "showMarkAsUnreadOption": { "type": "Bool", "default": false }, "enablePinMessage": { "type": "Bool", "default": false, "note": "Also requires CometChat.isPinMessageEnabled()" }, "enableSaveMessage": { "type": "Bool", "default": false, "note": "Also requires CometChat.isSaveMessageEnabled()" }, - "enableThreadSubscription": { "type": "Bool", "default": false, "note": "Offered on parent messages only, never on a reply" }, "messageAlignment": { "type": "MessageAlignment", "default": ".standard" } }, "viewSlots": { @@ -1515,26 +1513,6 @@ let messageList = CometChatMessageList() messageList.enableSaveMessage = true ``` -### enableThreadSubscription - -*Available since v5.1.22* - -Enables the **Subscribe to thread** / **Unsubscribe from thread** option in message actions, letting users opt in to notifications for a thread's replies. The option is offered on **parent messages only** — never on a reply inside a thread, because a subscription is always rooted at the thread's parent. It is not gated on reply count: subscribing to a message that has no replies yet is supported. It renders in one-on-one conversations as well as groups. - -| | | -|---|---| -| Type | `Bool` | -| Default | `false` | - -```swift lines -import CometChatUIKitSwift - -let messageList = CometChatMessageList() -messageList.enableThreadSubscription = true -``` - -See the [Thread Subscription guide](/ui-kit/ios/guide-thread-subscription) for the full feature, including keeping your own subscription control in sync. - ### enableSmartReplies Enables AI-powered smart reply suggestions. @@ -1810,7 +1788,9 @@ messageList.hideSaveMessageOption = true *Available since v5.1.22* -Hides the subscribe/unsubscribe option in message actions. Independent of `enableThreadSubscription` — use this to suppress the option on one screen while the feature stays on elsewhere. Ignored while `enableThreadSubscription` is `false`, since nothing renders in that case anyway. +Hides the subscribe/unsubscribe option in message actions — use this to suppress the option on one screen while it stays available elsewhere. Set it on an `AdditionalConfiguration` and pass that to [CometChatMessageHeader](/ui-kit/ios/message-header). + +The option is offered on **parent messages only** — never on a reply inside a thread, because a subscription is always rooted at the thread's parent. It is not gated on reply count: subscribing to a message that has no replies yet is supported. It renders in one-on-one conversations as well as groups. | | | |---|---| @@ -1820,10 +1800,15 @@ Hides the subscribe/unsubscribe option in message actions. Independent of `enabl ```swift lines import CometChatUIKitSwift -let messageList = CometChatMessageList() -messageList.hideThreadSubscriptionOption = true +let configuration = AdditionalConfiguration() +configuration.hideThreadSubscriptionOption = true + +let messageHeader = CometChatMessageHeader() +messageHeader.additionalConfiguration = configuration ``` +See the [Thread Subscription guide](/ui-kit/ios/guide-thread-subscription) for the full feature, including keeping your own subscription control in sync. + ### hideShareMessageOption Hides the share message option in message actions. diff --git a/ui-kit/ios/pinned-messages.mdx b/ui-kit/ios/pinned-messages.mdx index 06494025c..930cc3fa3 100644 --- a/ui-kit/ios/pinned-messages.mdx +++ b/ui-kit/ios/pinned-messages.mdx @@ -415,19 +415,6 @@ The group whose pinned messages to show. Mutually exclusive with `user`; pass it | Type | `Group?` | | Default | `nil` | -### enableThreadSubscription - -Enables the **Subscribe to thread** / **Unsubscribe from thread** option in the pinned row's message actions, exactly as on [MessageList](/ui-kit/ios/message-list). Off by default and opted into per surface — turning it on for the message list does **not** turn it on here. - -| | | -|---|---| -| Type | `Bool` | -| Default | `false` | - -```swift lines -pinnedVC.enableThreadSubscription = true -``` - ### hideUnpinOption Hides the per-row unpin swipe action. Set this for users who cannot pin in this conversation. diff --git a/ui-kit/ios/threaded-messages-header.mdx b/ui-kit/ios/threaded-messages-header.mdx index e370cbe8d..29462bb58 100644 --- a/ui-kit/ios/threaded-messages-header.mdx +++ b/ui-kit/ios/threaded-messages-header.mdx @@ -191,20 +191,19 @@ Below is a list of customizations along with corresponding code snippets: | setMaxHeight | Sets the maximum height for the threaded message view. | `setMaxHeight(300)` | | setMessageAlignment | Sets the alignment of messages (e.g., left or right). | `setMessageAlignment(.right)` | | setParentMessage | Sets the parent message for the threaded conversation. | `setParentMessage(parentMessage)` | -| enableThreadSubscription | Shows the subscribe/unsubscribe bell on the reply-count bar. | `set(enableThreadSubscription: true)` | | hideThreadSubscriptionButton | Hides the bell while leaving the feature on, for a screen that draws its own control. | `set(hideThreadSubscriptionButton: true)` | ### Thread Subscription *Available since v5.1.22* -With `enableThreadSubscription` on, the header renders a subscribe/unsubscribe bell as a trailing action on its reply-count bar. It appears only on a **sent parent message**, in one-on-one threads as well as groups. +The header renders a subscribe/unsubscribe bell as a trailing action on its reply-count bar. It appears only on a **sent parent message**, in one-on-one threads as well as groups. The bell manages itself: it reads the current state from the SDK, flips optimistically on tap, reverts on failure, toasts in both directions, and stays in step with a toggle made from the message action sheet. -Most thread screens show the bell in their header bar via [CometChatMessageHeader](/ui-kit/ios/message-header) instead — that is what the sample apps do, and it matches the other CometChat platforms. Use this component's bell when your screen has no header bar, and enable it on **exactly one** of the two so a screen never shows two bells. +Most thread screens show the bell in their header bar via [CometChatMessageHeader](/ui-kit/ios/message-header) instead — that is what the sample apps do, and it matches the other CometChat platforms. Use this component's bell when your screen has no header bar, and hide it on **one** of the two with `hideThreadSubscriptionButton` so a screen never shows two bells. @@ -214,7 +213,6 @@ import CometChatUIKitSwift let threadHeaderView = CometChatThreadedMessageHeader() threadHeaderView.set(parentMessage: parentMessage) threadHeaderView.set(controller: self) -threadHeaderView.set(enableThreadSubscription: true) ``` See the [Thread Subscription guide](/ui-kit/ios/guide-thread-subscription) for the full feature. From cd990e9a64a80feaa0d7f694cf6e4f7611792995 Mon Sep 17 00:00:00 2001 From: shreeyajoshi-cometchat Date: Thu, 10 Sep 2026 20:54:12 +0530 Subject: [PATCH 2/4] docs(ios): document the thread-subscription opt-out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The feature ships on by default and has no dashboard flag, so CometChatThreadSubscriptionConfig.setEnabled(false) is the only way to remove it from an app — and nothing in the iOS docs said so. Integrators had no way to find the switch short of reading the kit source. Adds a "Turning the Feature Off" section to the guide covering the call itself, how it ANDs with the per-instance hideThreadSubscription* flags (gate = whole app, hide flags = one screen), and that it can be flipped at any point rather than only before CometChatUIKit.init. A note records that this is the only control that will ever exist, and names the Android and React Native equivalents so a reader arriving from another kit lands in the right place. A warning ported from the RN guide records that closing the gate hides the controls but does NOT stop subscriptions or their notifications — users are still auto-subscribed by replying or being @-mentioned. Also corrects the reference gate snippet, which listed two of its three clauses and omitted the gate itself, and cross-references the new section from the four places a reader meets the feature: core-features, the message list's hideThreadSubscriptionOption, and hideThreadSubscriptionButton on both headers. The two header pages already said "while leaving the feature on" without ever naming what turned it off. Signatures verified against the kit source: setEnabled takes an unlabelled Bool, isEnabled() returns Bool. Documents an API that is committed but not yet built or merged — if the type is renamed in review, these five files need the same rename. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5 (1M context) --- ui-kit/ios/core-features.mdx | 2 ++ ui-kit/ios/guide-thread-subscription.mdx | 31 +++++++++++++++++++++++- ui-kit/ios/message-header.mdx | 2 +- ui-kit/ios/message-list.mdx | 2 ++ ui-kit/ios/threaded-messages-header.mdx | 2 +- 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/ui-kit/ios/core-features.mdx b/ui-kit/ios/core-features.mdx index 449eab7e3..861ca8457 100644 --- a/ui-kit/ios/core-features.mdx +++ b/ui-kit/ios/core-features.mdx @@ -567,6 +567,8 @@ class ThreadedMessagesViewController: UIViewController { Let users subscribe to a thread to be notified about its replies, or unsubscribe to mute it. Users are subscribed automatically when they start a thread, reply in one, or are @-mentioned in one — subscribing explicitly is how they opt in to a thread they haven't participated in yet. +On by default — there is no dashboard flag for this feature, so `CometChatThreadSubscriptionConfig.setEnabled(false)` is the only way to remove it. See [Turning the Feature Off](/ui-kit/ios/guide-thread-subscription#turning-the-feature-off). + ```swift lines import UIKit import CometChatUIKitSwift diff --git a/ui-kit/ios/guide-thread-subscription.mdx b/ui-kit/ios/guide-thread-subscription.mdx index 4d5c25f43..49aff4561 100644 --- a/ui-kit/ios/guide-thread-subscription.mdx +++ b/ui-kit/ios/guide-thread-subscription.mdx @@ -23,6 +23,33 @@ The UI Kit ships the toggle as a **Subscribe to thread / Unsubscribe from thread Thread subscription surfaces in two places: the message action sheet on [CometChatMessageList](/ui-kit/ios/message-list), and a bell on the thread header. Each can be hidden per instance with `hideThreadSubscriptionOption` and `hideThreadSubscriptionButton` respectively. +## Turning the Feature Off + +Thread subscription is **on by default** — there is nothing to switch on. To remove it from your app entirely, close the gate: + +```swift lines +import CometChatUIKitSwift + +// On by default. Call this only to opt OUT. +CometChatThreadSubscriptionConfig.setEnabled(false) +``` + +With the gate closed neither surface renders and no subscription request is ever made, whatever the per-instance `hideThreadSubscription*` flags say. The two are ANDed: the gate decides whether the feature exists in your app at all, and the per-instance flags remove a surface from one screen. + +`CometChatThreadSubscriptionConfig` is a plain process-wide switch, not an init-time setting — you can flip it at any point, before or after `CometChatUIKit.init`, and read it back with `isEnabled()`. + + + +This is the **only** control that will ever exist for the feature. Unlike pin and save, thread subscription has no dashboard flag and no app setting, so nothing on the server can switch it on or off. The other CometChat platforms work the same way: Android uses `CometChatThreadSubscriptionConfig.setEnabled(false)` and React Native `ThreadSubscriptionConfig.setEnabled(false)`. + + + + + +Closing the gate hides the controls but does **not** stop subscriptions. Users are still subscribed automatically by starting a thread, replying, or being @-mentioned, and still receive thread notifications — they simply have no way to change it in your app. Suppressing the notifications themselves is a notification-settings concern, not a UI Kit one. + + + ## Surface 1: The Message Action Sheet Option `CometChatMessageList` adds a **Subscribe to thread** / **Unsubscribe from thread** option to the long-press action sheet. The label and icon reflect the current state, read synchronously from the SDK when the sheet is built. @@ -31,7 +58,9 @@ The option appears only on **parent messages** — never on a reply inside a thr ```swift // The kit's gate, for reference: -// parentMessageId == 0 && !hideThreadSubscriptionOption +// CometChatThreadSubscriptionConfig.isEnabled() +// && !hideThreadSubscriptionOption +// && parentMessageId == 0 ``` diff --git a/ui-kit/ios/message-header.mdx b/ui-kit/ios/message-header.mdx index ff26ef182..697b85c0a 100644 --- a/ui-kit/ios/message-header.mdx +++ b/ui-kit/ios/message-header.mdx @@ -938,7 +938,7 @@ Hides the user status (online/offline/last active). *Available since v5.1.22* -Hides the subscribe/unsubscribe bell while leaving the feature on — for a screen that already draws its own control and would otherwise show two. +Hides the subscribe/unsubscribe bell while leaving the feature on — for a screen that already draws its own control and would otherwise show two. To turn the feature off across your whole app instead, call `CometChatThreadSubscriptionConfig.setEnabled(false)` — see [Turning the Feature Off](/ui-kit/ios/guide-thread-subscription#turning-the-feature-off). | | | |---|---| diff --git a/ui-kit/ios/message-list.mdx b/ui-kit/ios/message-list.mdx index f8efb3ef2..ae4590d3b 100644 --- a/ui-kit/ios/message-list.mdx +++ b/ui-kit/ios/message-list.mdx @@ -1790,6 +1790,8 @@ messageList.hideSaveMessageOption = true Hides the subscribe/unsubscribe option in message actions — use this to suppress the option on one screen while it stays available elsewhere. Set it on an `AdditionalConfiguration` and pass that to [CometChatMessageHeader](/ui-kit/ios/message-header). +ANDed with the feature gate: to remove thread subscription from your app entirely rather than from one screen, call `CometChatThreadSubscriptionConfig.setEnabled(false)` instead — see [Turning the Feature Off](/ui-kit/ios/guide-thread-subscription#turning-the-feature-off). + The option is offered on **parent messages only** — never on a reply inside a thread, because a subscription is always rooted at the thread's parent. It is not gated on reply count: subscribing to a message that has no replies yet is supported. It renders in one-on-one conversations as well as groups. | | | diff --git a/ui-kit/ios/threaded-messages-header.mdx b/ui-kit/ios/threaded-messages-header.mdx index 29462bb58..b2c2ee44c 100644 --- a/ui-kit/ios/threaded-messages-header.mdx +++ b/ui-kit/ios/threaded-messages-header.mdx @@ -197,7 +197,7 @@ Below is a list of customizations along with corresponding code snippets: *Available since v5.1.22* -The header renders a subscribe/unsubscribe bell as a trailing action on its reply-count bar. It appears only on a **sent parent message**, in one-on-one threads as well as groups. +The header renders a subscribe/unsubscribe bell as a trailing action on its reply-count bar. It appears only on a **sent parent message**, in one-on-one threads as well as groups. The feature is on by default; turn it off across your whole app with `CometChatThreadSubscriptionConfig.setEnabled(false)` — see [Turning the Feature Off](/ui-kit/ios/guide-thread-subscription#turning-the-feature-off). The bell manages itself: it reads the current state from the SDK, flips optimistically on tap, reverts on failure, toasts in both directions, and stays in step with a toggle made from the message action sheet. From c599db6d7ced8f1c64cd2904655ae6b490421b85 Mon Sep 17 00:00:00 2001 From: shreeyajoshi-cometchat Date: Fri, 11 Sep 2026 13:15:54 +0530 Subject: [PATCH 3/4] docs(ios): put the thread-subscription bell in the navigation bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verified the bell's on-screen placement against the Android and React kit sources rather than component names. Every CometChat platform renders the subscription bell in the thread screen's top bar: Android's CometChatThreadScreen puts it in CometChatToolbar(actions:) and hides the reply-count bell, and React's CometChatThreadHeaderTopBar mounts SubscriptionToggle inside __top-bar. On iOS CometChatMessageHeader in thread mode is what lands it there. - Document CometChatMessageHeader as the bell to use, with a hosting example pinning it to the safe-area top. - Reframe the CometChatThreadedMessageHeader bell as a fallback for a thread screen with no top bar, matching how Android coordinates the two, rather than as a co-equal choice. - Drop the claim that this matches "where the React and React Native kits place theirs" — true of the placement, false of the component. React's bell is on CometChatThreadHeader and React Native trunk ships no header bell. - core-features: threadSubscription is enabled by default and spans both surfaces (the opt-in gate was removed with UIKitSettings.enableThreadSubscription). Co-Authored-By: Claude Opus 5 (1M context) --- ui-kit/ios/core-features.mdx | 2 +- ui-kit/ios/guide-thread-subscription.mdx | 43 +++++++++++++++++++++--- ui-kit/ios/message-header.mdx | 4 ++- ui-kit/ios/threaded-messages-header.mdx | 27 ++++++++++----- 4 files changed, 61 insertions(+), 15 deletions(-) diff --git a/ui-kit/ios/core-features.mdx b/ui-kit/ios/core-features.mdx index 861ca8457..3ea37743b 100644 --- a/ui-kit/ios/core-features.mdx +++ b/ui-kit/ios/core-features.mdx @@ -16,7 +16,7 @@ description: "Review CometChat iOS UI Kit core features for messaging, media sha {"name": "reactions", "description": "Let users react to messages with emojis", "component": "CometChatMessageList", "enabledByDefault": true}, {"name": "mentions", "description": "Tag users in messages with @mentions", "component": "CometChatMessageComposer", "enabledByDefault": true}, {"name": "threadedConversations", "description": "Reply to specific messages in threads", "component": "CometChatThreadedMessageHeader", "enabledByDefault": true}, - {"name": "threadSubscription", "description": "Subscribe to or unsubscribe from a thread's replies", "component": "CometChatMessageList", "enabledByDefault": false}, + {"name": "threadSubscription", "description": "Subscribe to or unsubscribe from a thread's replies — a bell in the thread screen's top bar, plus an option in the message action sheet", "component": "CometChatMessageHeader, CometChatMessageList", "enabledByDefault": true}, {"name": "groupChat", "description": "Create and manage group conversations", "component": "CometChatGroups", "enabledByDefault": true}, {"name": "search", "description": "Search across conversations and messages", "component": "CometChatSearch", "enabledByDefault": true} ], diff --git a/ui-kit/ios/guide-thread-subscription.mdx b/ui-kit/ios/guide-thread-subscription.mdx index 49aff4561..4c0ccafad 100644 --- a/ui-kit/ios/guide-thread-subscription.mdx +++ b/ui-kit/ios/guide-thread-subscription.mdx @@ -21,7 +21,7 @@ The UI Kit ships the toggle as a **Subscribe to thread / Unsubscribe from thread ## The Surfaces -Thread subscription surfaces in two places: the message action sheet on [CometChatMessageList](/ui-kit/ios/message-list), and a bell on the thread header. Each can be hidden per instance with `hideThreadSubscriptionOption` and `hideThreadSubscriptionButton` respectively. +Thread subscription surfaces in two places: the message action sheet on [CometChatMessageList](/ui-kit/ios/message-list), and a bell in the thread screen's navigation bar, rendered by [CometChatMessageHeader](/ui-kit/ios/message-header). Each can be hidden per instance with `hideThreadSubscriptionOption` and `hideThreadSubscriptionButton` respectively. ## Turning the Feature Off @@ -83,20 +83,55 @@ messageListView.hideThreadSubscriptionOption = true To replace the kit's behavior with your own, supply an `onItemClick` on a custom option with the id `MessageOptionConstants.threadSubscription` — the kit calls your handler instead of its own. -## Surface 2: The Bell in the Message Header +## Surface 2: The Bell in the Navigation Bar [CometChatMessageHeader](/ui-kit/ios/message-header) renders a subscribe/unsubscribe bell in its trailing area. Set `parentMessage` to put the header in **thread mode** — the bell renders only then, so a conversation header is unaffected. As with the action-sheet option, it renders in one-on-one threads as well as groups. -[CometChatThreadedMessageHeader](/ui-kit/ios/threaded-messages-header) renders the same bell on its reply-count bar, for a screen with no header bar. Hide it on one of the two with `hideThreadSubscriptionButton` so a screen never shows two bells. +This is the bell to use. Every CometChat platform places the subscription bell in the thread screen's **top bar**, not in the reply-count row beneath it — on iOS, putting `CometChatMessageHeader` in thread mode is how you get it there. ```swift let messageHeaderView = CometChatMessageHeader() messageHeaderView.set(parentMessage: parentMessage) // puts the header in thread mode ``` +Host it as the thread screen's navigation bar — pin it to the safe-area top and hide the system bar, exactly as a conversation screen does: + +```swift lines +override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationController?.setNavigationBarHidden(true, animated: animated) +} + +func buildUI() { + view.addSubview(messageHeaderView) + view.addSubview(threadHeaderView) // reply-count bar, directly beneath + view.addSubview(messageListView) + + NSLayoutConstraint.activate([ + messageHeaderView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + messageHeaderView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + messageHeaderView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + + threadHeaderView.topAnchor.constraint(equalTo: messageHeaderView.bottomAnchor), + threadHeaderView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + threadHeaderView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + ]) +} +``` + + + +[CometChatThreadedMessageHeader](/ui-kit/ios/threaded-messages-header) carries a **fallback** bell on its reply-count bar, so a thread screen with no top bar still has the control. A screen that renders both components should hide it, leaving the top-bar bell as the only one: + +```swift +threadHeaderView.hideThreadSubscriptionButton = true +``` + + + The bell tracks state on its own: it reads the current subscription state from the SDK, flips optimistically on tap, reverts if the request fails, toasts in both directions, and emits `ccThreadSubscriptionChanged` on success. You do not wire any of that up. -If your screen already draws its own control and you would otherwise show two bells, suppress the kit's: +If your screen draws a subscription control of its own in the navigation bar, suppress the kit's: ```swift messageHeaderView.set(hideThreadSubscriptionButton: true) diff --git a/ui-kit/ios/message-header.mdx b/ui-kit/ios/message-header.mdx index 697b85c0a..3c5c5cd60 100644 --- a/ui-kit/ios/message-header.mdx +++ b/ui-kit/ios/message-header.mdx @@ -938,7 +938,9 @@ Hides the user status (online/offline/last active). *Available since v5.1.22* -Hides the subscribe/unsubscribe bell while leaving the feature on — for a screen that already draws its own control and would otherwise show two. To turn the feature off across your whole app instead, call `CometChatThreadSubscriptionConfig.setEnabled(false)` — see [Turning the Feature Off](/ui-kit/ios/guide-thread-subscription#turning-the-feature-off). +Hides the subscribe/unsubscribe bell while leaving the feature on — for a screen that already draws its own control in the navigation bar. To turn the feature off across your whole app instead, call `CometChatThreadSubscriptionConfig.setEnabled(false)` — see [Turning the Feature Off](/ui-kit/ios/guide-thread-subscription#turning-the-feature-off). + +This is the bell to use on a thread screen: every CometChat platform places the subscription bell in the thread screen's top bar, and on iOS this header in thread mode is what puts it there. [CometChatThreadedMessageHeader](/ui-kit/ios/threaded-messages-header#thread-subscription) carries a fallback bell on its reply-count bar, for a thread screen that has no top bar; when your screen renders both components, hide that one rather than this one. | | | |---|---| diff --git a/ui-kit/ios/threaded-messages-header.mdx b/ui-kit/ios/threaded-messages-header.mdx index b2c2ee44c..f47a34dcb 100644 --- a/ui-kit/ios/threaded-messages-header.mdx +++ b/ui-kit/ios/threaded-messages-header.mdx @@ -191,30 +191,39 @@ Below is a list of customizations along with corresponding code snippets: | setMaxHeight | Sets the maximum height for the threaded message view. | `setMaxHeight(300)` | | setMessageAlignment | Sets the alignment of messages (e.g., left or right). | `setMessageAlignment(.right)` | | setParentMessage | Sets the parent message for the threaded conversation. | `setParentMessage(parentMessage)` | -| hideThreadSubscriptionButton | Hides the bell while leaving the feature on, for a screen that draws its own control. | `set(hideThreadSubscriptionButton: true)` | +| hideThreadSubscriptionButton | Hides this component's own bell. Set it when the screen also renders [CometChatMessageHeader](/ui-kit/ios/message-header#thread-subscription), which carries the navigation-bar bell. | `set(hideThreadSubscriptionButton: true)` | ### Thread Subscription *Available since v5.1.22* -The header renders a subscribe/unsubscribe bell as a trailing action on its reply-count bar. It appears only on a **sent parent message**, in one-on-one threads as well as groups. The feature is on by default; turn it off across your whole app with `CometChatThreadSubscriptionConfig.setEnabled(false)` — see [Turning the Feature Off](/ui-kit/ios/guide-thread-subscription#turning-the-feature-off). +The subscription bell belongs to [CometChatMessageHeader](/ui-kit/ios/message-header#thread-subscription) — put it in the thread screen's navigation bar, which is where every CometChat platform places it. -The bell manages itself: it reads the current state from the SDK, flips optimistically on tap, reverts on failure, toasts in both directions, and stays in step with a toggle made from the message action sheet. - - - -Most thread screens show the bell in their header bar via [CometChatMessageHeader](/ui-kit/ios/message-header) instead — that is what the sample apps do, and it matches the other CometChat platforms. Use this component's bell when your screen has no header bar, and hide it on **one** of the two with `hideThreadSubscriptionButton` so a screen never shows two bells. - - +A thread screen usually renders both components: the message header as the navigation bar, and this component directly beneath it for the parent message and reply count. ```swift lines import CometChatUIKitSwift +// Navigation bar — `parentMessage` puts it in thread mode and renders the bell +let messageHeaderView = CometChatMessageHeader() +messageHeaderView.set(parentMessage: parentMessage) + +// Reply-count row — the parent message and its reply count let threadHeaderView = CometChatThreadedMessageHeader() threadHeaderView.set(parentMessage: parentMessage) threadHeaderView.set(controller: self) ``` + + +This component carries a **fallback** bell of its own on the reply-count bar, so a thread screen with no top bar still has the control. When your screen renders both components, hide it — the top-bar bell is then the only one: + +```swift +threadHeaderView.hideThreadSubscriptionButton = true +``` + + + See the [Thread Subscription guide](/ui-kit/ios/guide-thread-subscription) for the full feature. --- From c2722fdf2565839e9858ac7bbf7e6bb9387693d6 Mon Sep 17 00:00:00 2001 From: shreeyajoshi-cometchat Date: Fri, 11 Sep 2026 14:33:15 +0530 Subject: [PATCH 4/4] docs(ios): document pin conversations on the conversations page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The conversations page had zero pin coverage across all 1,592 lines, while core-features linked to it as the component reference that "provides the pin/unpin option, the row indicator, and pinned-first ordering" — a promise the destination page did not keep. `enablePinConversation` was documented nowhere except one code sample. Every other platform documents this on its own conversations page: React Native, Android, React and Angular all do. Adds, modelled on the React Native section: - `enablePinConversation` and `hidePinConversationOption` prop sections. - A "Pinning Conversations" section covering pinned-first ordering, the private-to-user semantic, and confirm-on-unpin. - The two gates, which is the part that bites: the swipe action needs the `features.ux.conversations.pinned.enabled` app setting mapped AND `enablePinConversation`. The kit reads the app setting via `CometChat.isPinConversationEnabled()`, restored to the guard in uikit-ios 51a047c6c now that the backend has shipped the flag. - Admin pins, with the `pinnedAt` / `pinnedBy` reads an integrator needs to detect one. Both are @objc public on the SDK's Conversation; the kit's own `systemPinner` constant is internal, so the "app_system" sentinel has to be compared directly — noted as a gap against RN's `isSystemPinnedConversation()`. - The `PinConversationErrorCodes` table. Co-Authored-By: Claude Opus 5 (1M context) --- ui-kit/ios/conversations.mdx | 88 ++++++++++++++++++++++++++++++++++++ ui-kit/ios/core-features.mdx | 2 +- 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/ui-kit/ios/conversations.mdx b/ui-kit/ios/conversations.mdx index 91baf9b81..0b8e2428f 100644 --- a/ui-kit/ios/conversations.mdx +++ b/ui-kit/ios/conversations.mdx @@ -398,6 +398,58 @@ class MyViewController: UIViewController, CometChatConversationEventListener { --- +## Pinning Conversations + +*Available since v5.1.22* + +A pinned conversation sits at the top of the list with a pin indicator on the row, and holds that position as new messages arrive in other chats. A personal pin is **private to the logged-in user** — nobody else sees it — and syncs to that user's other devices. + + +**Two gates, both required.** Pin Conversation needs the `features.ux.conversations.pinned.enabled` app setting mapped for your app, *and* your own opt-in via `enablePinConversation`. With either missing the swipe action never appears. The kit reads the app setting through `CometChat.isPinConversationEnabled()`. + + +```swift lines +import CometChatUIKitSwift + +let conversations = CometChatConversations() +conversations.enablePinConversation = true // off by default +``` + +Pinning applies immediately; **unpinning asks for confirmation first**. Hide the action on a single list with [`hidePinConversationOption`](#hidepinconversationoption) while it stays available elsewhere. + +### Admin Pins + +An app can also pin a conversation **for everyone**. A system pin sorts above every personal pin, and is not the user's to remove — the kit does not offer the unpin affordance on those rows at all. + +To reflect the same distinction in a view of your own, read the two properties the SDK exposes on `Conversation`: + +```swift lines +import CometChatSDK + +// `pinnedAt` uses a 0 sentinel, so presence — not `> 0` — is the pinned test. +let isPinned = conversation.pinnedAt != 0 + +// An admin/global pin. A user cannot unpin one, so hide or disable your unpin control. +let isSystemPinned = isPinned && conversation.pinnedBy == "app_system" +``` + + +The UI Kit has no public helper for this yet — unlike the React Native kit's `isSystemPinnedConversation()` — so the `"app_system"` sentinel has to be compared directly for now. + + +### Errors + +A failed pin surfaces as a toast. `PinConversationErrorCodes` names the codes the backend emits, so you can match them in your own handling: + +| Code | Meaning | +|---|---| +| `ERR_PINNED_CONVERSATIONS_LIMIT_EXCEEDED` | The user is at the per-user pinned cap. The real cap rides in `errorParams` — never hard-code it. | +| `ERR_CONVERSATION_NOT_ACCESSIBLE` | No conversation row yet (never messaged), or deleted-for-me. | +| `ERR_PERMISSION_DENIED` | RBAC denial. | +| `ERR_BAD_REQUEST` | Invalid `pinnedBy` filter or malformed body. | + +--- + ## Custom View Slots | Slot | Signature | Replaces | @@ -1099,6 +1151,24 @@ let conversations = CometChatConversations() conversations.disableTyping = true ``` +### enablePinConversation + +*Available since v5.1.22* + +Adds the **Pin / Unpin** swipe action to each row. Off by default — see [Pinning Conversations](#pinning-conversations) for the feature, including the app setting it also requires. + +| | | +|---|---| +| Type | `Bool` | +| Default | `false` | + +```swift lines +import CometChatUIKitSwift + +let conversations = CometChatConversations() +conversations.enablePinConversation = true +``` + ### hideBackButton Hides the back button in the navigation bar. @@ -1135,6 +1205,24 @@ Hides the entire navigation bar. | Type | `Bool` | | Default | `false` | +### hidePinConversationOption + +*Available since v5.1.22* + +Hides the Pin / Unpin swipe action on one list while the feature stays on elsewhere. ANDed with `enablePinConversation` and the app setting — see [Pinning Conversations](#pinning-conversations). + +| | | +|---|---| +| Type | `Bool` | +| Default | `false` | + +```swift lines +import CometChatUIKitSwift + +let conversations = CometChatConversations() +conversations.hidePinConversationOption = true +``` + ### hideReceipts Hides read/delivered receipt indicators. diff --git a/ui-kit/ios/core-features.mdx b/ui-kit/ios/core-features.mdx index 3ea37743b..fdffa318f 100644 --- a/ui-kit/ios/core-features.mdx +++ b/ui-kit/ios/core-features.mdx @@ -784,7 +784,7 @@ class PinConversationsViewController: UIViewController { ``` **Component Used:** -- [Conversations](/ui-kit/ios/conversations) - Provides the pin/unpin option, the row indicator, and pinned-first ordering +- [Conversations](/ui-kit/ios/conversations#pinning-conversations) - Provides the pin/unpin option, the row indicator, and pinned-first ordering ## Search