### Generate Documentation Archive Source: https://github.com/getstream/stream-chat-swiftui/blob/develop/README.md Run this command to build the documentation archive for the SwiftUI StreamChat SDK and open it in Xcode. Ensure you have Xcode and xcpretty installed. ```bash xcodebuild docbuild -skipMacroValidation -skipPackagePluginValidation -derivedDataPath .derivedData -scheme StreamChatSwiftUI -destination generic/platform=iOS | xcpretty open .derivedData/Build/Products/Debug-iphoneos/StreamChatSwiftUI.doccarchive ``` -------------------------------- ### Define Custom Channel Actions with ChannelAction Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Create custom swipe or long-press actions for channels using `ChannelAction`. This example defines a 'Pin'/'Unpin' action that can be added to the list of supported actions. ```swift import StreamChat import StreamChatSwiftUI // Build a custom "pin" action @MainActor func pinChannelAction(for channel: ChatChannel, onDismiss: @escaping @MainActor () -> Void) -> ChannelAction { ChannelAction( title: channel.isPinned ? "Unpin" : "Pin", iconName: channel.isPinned ? "pin.slash" : "pin", action: { let ctrl = InjectedValues[".chatClient"].channelController(for: channel.cid) if channel.isPinned { ctrl.unpin { _ in onDismiss() } } else { ctrl.pin { _ in onDismiss() } } }, confirmationPopup: nil, isDestructive: false ) } // In your ViewFactory func supportedMoreChannelActions(options: SupportedMoreChannelActionsOptions) -> [ChannelAction] { var actions = ChannelAction.defaultActions(for: options) actions.insert(pinChannelAction(for: options.channel, onDismiss: options.onDismiss), at: 1) return actions } ``` -------------------------------- ### Configure Message Composer Behavior with ComposerConfig Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Configure message composer behavior, including text-input dimensions, attachment limits, and media picker types. This example sets up voice recording and image/video support. ```swift import StreamChatSwiftUI let config = ComposerConfig( isVoiceRecordingEnabled: true, isVoiceRecordingAutoSendEnabled: false, // review before sending inputViewMinHeight: 40, inputViewMaxHeight: 120, inputViewCornerRadius: 20, gallerySupportedTypes: .imagesAndVideo, maxGalleryAssetsCount: 10, adjustMessageOnSend: { text in // Strip leading/trailing whitespace before each send text.trimmingCharacters(in: .whitespacesAndNewlines) }, adjustMessageOnRead: { text in text }, maxAttachmentSize: 100 * 1024 * 1024 // 100 MB default ) ``` -------------------------------- ### Implement Custom Styles with the Styles Protocol Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Implement the `Styles` protocol to globally customize the look and feel of UI components like the composer, message bubbles, and channel list. This example shows custom input field and message bubble styles. ```swift import StreamChatSwiftUI import SwiftUI struct MyStyles: Styles { var composerPlacement: ComposerPlacement = .floating // Round input field with a translucent background func makeComposerInputViewModifier( options: ComposerInputModifierOptions ) -> some ViewModifier { ComposerInputRoundedModifier(cornerRadius: 24) } // Flat message bubble (no shadow) func makeMessageViewModifier( for info: MessageModifierInfo ) -> some ViewModifier { FlatMessageBubbleModifier( message: info.message, isFirst: info.isFirst ) } // Glass-effect scroll-to-bottom button on iOS 26+ func makeScrollToBottomButtonModifier( options: ScrollToBottomButtonModifierOptions ) -> some ViewModifier { LiquidGlassModifier(shape: .roundedRect(20)) } } // Attach to a ViewFactory class MyViewFactory: ViewFactory { @Injected(".chatClient") public var chatClient public static let shared = MyViewFactory() private init() {} public var styles: MyStyles = MyStyles() } ``` -------------------------------- ### Configure Channel List Behavior with ChannelListConfig Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Use `ChannelListConfig` to customize the channel list's navigation bar, dividers, muted channel indicators, and swipe/long-press actions. This example adds a custom 'Archive' action. ```swift import StreamChat import StreamChatSwiftUI let config = ChannelListConfig( channelItemMutedStyle: .bottomRightCorner, navigationBarDisplayMode: .inline, showChannelListDividerOnLastItem: true, supportedMoreChannelActions: { options in // Start from defaults, add an archive action var actions = ChannelAction.defaultActions(for: options) actions.append(ChannelAction( title: "Archive", iconName: "archivebox", action: { let ctrl = InjectedValues[".chatClient"].channelController(for: options.channel.cid) ctrl.archive { _ in options.onDismiss() } }, confirmationPopup: nil, isDestructive: false )) return actions } ) ``` -------------------------------- ### Customize Message Preview Formatting Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Subclass `MessagePreviewFormatter` to alter how chat messages are displayed as previews in the channel list or thread list. This example customizes the display for deleted messages and voice recordings. ```swift import StreamChat import StreamChatSwiftUI class BrandedMessagePreviewFormatter: MessagePreviewFormatter { override func format( _ previewMessage: ChatMessage, in channel: ChatChannel ) -> String { // Redact deleted messages differently if previewMessage.isDeleted { return "🚫 Message removed" } // Show emoji for voice recordings if previewMessage.voiceRecordingAttachments.first != nil { return "🎀 Voice message" } // Fall back to the default return super.format(previewMessage, in: channel) } } // Register via Utils let utils = Utils(messagePreviewFormatter: BrandedMessagePreviewFormatter()) ``` -------------------------------- ### Run Tests on Active Booted Device Source: https://github.com/getstream/stream-chat-swiftui/blob/develop/AGENTS.md This command runs tests on an active, booted iOS Simulator if a specific device is not available. It dynamically selects a booted simulator. ```bash xcodebuild \ -scheme StreamChatSwiftUI \ -destination "platform=iOS Simulator,OS=any,name=$(xcrun simctl list devices booted | grep '(Booted)' | head -1 | sed 's/ (.*)//')" \ -configuration Debug test ``` -------------------------------- ### ChannelListConfig Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt `ChannelListConfig` allows customization of the channel list's navigation bar, dividers, muted channel indicators, and contextual actions. ```APIDOC ## `ChannelListConfig` β€” Channel list behaviour `ChannelListConfig` configures the channel list's navigation bar style, divider, muted-channel indicator layout, and the set of contextual actions exposed on swipe/long-press. ```swift import StreamChat import StreamChatSwiftUI let config = ChannelListConfig( channelItemMutedStyle: .bottomRightCorner, navigationBarDisplayMode: .inline, showChannelListDividerOnLastItem: true, supportedMoreChannelActions: { options in // Start from defaults, add an archive action var actions = ChannelAction.defaultActions(for: options) actions.append(ChannelAction( title: "Archive", iconName: "archivebox", action: { let ctrl = InjectedValues[\"chatClient\"].channelController(for: options.channel.cid) ctrl.archive { _ in options.onDismiss() } }, confirmationPopup: nil, isDestructive: false )) return actions } ) ``` ``` -------------------------------- ### Individual Channel View with Floating Composer Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Render a single channel's message list and composer using ChatChannelView. The composer can be placed either floating or docked. ```swift import StreamChat import StreamChatSwiftUI import SwiftUI struct ChannelScreen: View { let channelController: ChatChannelController var body: some View { // composerPlacement can be .floating (default) or .docked ChatChannelView( viewFactory: MyViewFactory.shared, channelController: channelController, composerPlacement: .floating ) } } ``` -------------------------------- ### Implement Custom ViewFactory to Replace UI Components Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Conform to the ViewFactory protocol and override make* methods to customize specific UI components. DefaultViewFactory provides sensible defaults for un-overridden methods. Ensure necessary imports are included. ```swift import StreamChat import StreamChatSwiftUI import SwiftUI // Custom factory that replaces the channel list item and the message bubble class MyViewFactory: ViewFactory { @Injected(\.chatClient) public var chatClient public static let shared = MyViewFactory() private init() {} public var styles = DefaultStyles() // Replace the channel list row with a compact layout func makeChannelListItem( options: ChannelListItemOptions ) -> some View { CompactChannelRow( channel: options.channel, channelName: options.channelName, onTap: options.onItemTap ) } // Inject a custom message text view func makeMessageTextView( options: MessageTextViewOptions ) -> some View { BrandedMessageTextView(message: options.message) } // Replace the empty state for the channel list func makeEmptyChannelsView(options: EmptyChannelsViewOptions) -> some View { VStack(spacing: 16) { Image(systemName: "bubble.left.and.bubble.right") .font(.system(size: 60)) Text("No conversations yet") .font(.headline) } } } // Usage ChatChannelListView(viewFactory: MyViewFactory.shared) ``` -------------------------------- ### Build StreamChatSwiftUI (Debug) Source: https://github.com/getstream/stream-chat-swiftui/blob/develop/AGENTS.md Use this command to build the project in Debug configuration for iOS Simulator. Ensure Xcode is used for day-to-day work. ```bash xcodebuild \ -scheme StreamChatSwiftUI \ -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \ -configuration Debug build ``` -------------------------------- ### Advanced ChatChannelListView Configuration Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Configure ChatChannelListView with custom queries, deep-link support, and a custom view factory. Handles tab bar visibility and search by message text. ```swift import StreamChat import StreamChatSwiftUI import SwiftUI // Advanced usage – custom query, deep-link support, custom factory struct AdvancedContentView: View { @Injected(\.chatClient) var chatClient var body: some View { let query = ChannelListQuery( filter: .containMembers(userIds: [chatClient.currentUserId ?? ""]), sort: [Sorting(key: .lastMessageAt)] ) let controller = chatClient.channelListController(query: query) ChatChannelListView( viewFactory: MyViewFactory.shared, channelListController: controller, title: "Messages", selectedChannelId: "channel-to-open-on-launch", // deep-link handleTabBarVisibility: true, embedInNavigationView: true, searchType: .messages // search by message text ) } } ``` -------------------------------- ### Channel Info Navigation Link Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Create a navigation link to the ChatChannelInfoView for displaying channel details and settings. This view is automatically navigated to from the default viewInfo channel action. ```swift import StreamChat import StreamChatSwiftUI import SwiftUI struct ChannelInfoButton: View { let channel: ChatChannel var body: some View { NavigationLink { ChatChannelInfoView( factory: MyViewFactory.shared, channel: channel, shownFromMessageList: true ) } label: { Label("Info", systemImage: "info.circle") } } } ``` -------------------------------- ### Minimal ChatChannelListView Usage Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Use ChatChannelListView with default settings for a basic channel list. It manages loading, empty states, search, and navigation automatically. ```swift import StreamChat import StreamChatSwiftUI import SwiftUI // Minimal usage – uses DefaultViewFactory and a default channel query struct ContentView: View { var body: some View { // Embeds its own NavigationStack; shows "Stream Chat" in the nav bar ChatChannelListView() } } ``` -------------------------------- ### Channel View for Deep-Linked Channel Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Display a specific channel using ChatChannelView, initialized for deep-linking. Requires a ChannelId and automatically fetches the channel controller. ```swift import StreamChat import StreamChatSwiftUI import SwiftUI struct DeepLinkChannelScreen: View { let channelId: ChannelId @Injected(\.chatClient) var chatClient var body: some View { let controller = chatClient.channelController( for: channelId, messageOrdering: .topToBottom ) ChatChannelView( channelController: controller, scrollToMessage: nil ) } } ``` -------------------------------- ### Check Swift Code Formatting with SwiftFormat Source: https://github.com/getstream/stream-chat-swiftui/blob/develop/AGENTS.md Performs a lint check on Swift code using SwiftFormat without making any edits. Adhere to the .swiftformat configuration. ```bash swiftformat --config .swiftformat --lint . ``` -------------------------------- ### Run StreamChatSwiftUI Tests (Debug) Source: https://github.com/getstream/stream-chat-swiftui/blob/develop/AGENTS.md Executes unit and snapshot tests for the project in Debug configuration on an iOS Simulator. For CI parity and automation, the CLI is preferred over Xcode. ```bash xcodebuild \ -scheme StreamChatSwiftUI \ -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \ -configuration Debug test ``` -------------------------------- ### ChannelAction Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt `ChannelAction` defines contextual actions for channels, such as swipe or long-press actions. It can be extended from default actions. ```APIDOC ## `ChannelAction` β€” Contextual channel actions `ChannelAction` models the swipe/long-press actions shown per channel. Use `ChannelAction.defaultActions(for:)` as a starting point, extend with your own, and supply them via `ChannelListConfig.supportedMoreChannelActions`. ```swift import StreamChat import StreamChatSwiftUI // Build a custom "pin" action @MainActor func pinChannelAction(for channel: ChatChannel, onDismiss: @escaping @MainActor () -> Void) -> ChannelAction { ChannelAction( title: channel.isPinned ? "Unpin" : "Pin", iconName: channel.isPinned ? "pin.slash" : "pin", action: { let ctrl = InjectedValues[\"chatClient\"].channelController(for: channel.cid) if channel.isPinned { ctrl.unpin { _ in onDismiss() } } else { ctrl.pin { _ in onDismiss() } } }, confirmationPopup: nil, isDestructive: false ) } // In your ViewFactory func supportedMoreChannelActions(options: SupportedMoreChannelActionsOptions) -> [ChannelAction] { var actions = ChannelAction.defaultActions(for: options) actions.insert(pinChannelAction(for: options.channel, onDismiss: options.onDismiss), at: 1) return actions } ``` ``` -------------------------------- ### Override Formatters and Configs with Custom Utils Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Override formatters and configurations before connecting to StreamChat. This involves creating a custom Utils object and registering it. ```swift import StreamChat import StreamChatSwiftUI // Override formatters and configs before connecting let customUtils = Utils( markdownFormatter: MyMarkdownFormatter(), dateFormatter: .makeDefault(), channelListConfig: ChannelListConfig( channelItemMutedStyle: .afterChannelName, navigationBarDisplayMode: .large, showChannelListDividerOnLastItem: false ), messageListConfig: MessageListConfig( messagePopoverEnabled: true, becomesFirstResponderOnOpen: true, groupMessages: true ), composerConfig: ComposerConfig( isVoiceRecordingEnabled: true, isVoiceRecordingAutoSendEnabled: false, inputViewMinHeight: 44, inputViewMaxHeight: 150, gallerySupportedTypes: .imagesAndVideo, maxAttachmentSize: 50 * 1024 * 1024 // 50 MB ) ) // Inject before ChatClient setup StreamChatSwiftUIAppearance.registerSharedDependencies(utils: customUtils) ``` -------------------------------- ### Auto-fix Swift Code Formatting with SwiftFormat Source: https://github.com/getstream/stream-chat-swiftui/blob/develop/AGENTS.md Automatically formats Swift code according to the rules defined in .swiftformat. This command modifies files in place. ```bash swiftformat --config .swiftformat . ``` -------------------------------- ### Check Message Composer Capabilities Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Check the capabilities of the MessageComposerViewModel, such as whether a message can be sent, if it has content, and if voice recording is enabled. ```swift import StreamChat import StreamChatSwiftUI // Check composer capabilities @MainActor func printComposerState(_ vm: MessageComposerViewModel) { print("Can send: \(vm.canSendMessage)") print("Has content: \(vm.hasContent)") print("Voice recording enabled: \(vm.isVoiceRecordingEnabled)") print("Picker state: \(vm.pickerState)") // .photos | .files | .camera } ``` -------------------------------- ### Lint Swift Code with SwiftLint Source: https://github.com/getstream/stream-chat-swiftui/blob/develop/AGENTS.md Validates Swift code against defined rules using SwiftLint in strict mode. Ensure to respect the .swiftlint.yml configuration. ```bash swiftlint lint --config .swiftlint.yml --strict ``` -------------------------------- ### Styles Protocol Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt The `Styles` protocol enables visual and layout customization for various Stream Chat SwiftUI components like the composer, message bubbles, and channel list. ```APIDOC ## `Styles` protocol β€” Visual and layout customisation `Styles` governs ComposerPlacement, and exposes typed `ViewModifier` factory methods for the composer, message bubble, channel list, message list, scroll-to-bottom button, search bar, and toolbar. Implement it to change the entire look of the SDK without touching individual views. ```swift import StreamChatSwiftUI import SwiftUI struct MyStyles: Styles { var composerPlacement: ComposerPlacement = .floating // Round input field with a translucent background func makeComposerInputViewModifier( options: ComposerInputModifierOptions ) -> some ViewModifier { ComposerInputRoundedModifier(cornerRadius: 24) } // Flat message bubble (no shadow) func makeMessageViewModifier( for info: MessageModifierInfo ) -> some ViewModifier { FlatMessageBubbleModifier( message: info.message, isFirst: info.isFirst ) } // Glass-effect scroll-to-bottom button on iOS 26+ func makeScrollToBottomButtonModifier( options: ScrollToBottomButtonModifierOptions ) -> some ViewModifier { LiquidGlassModifier(shape: .roundedRect(20)) } } // Attach to a ViewFactory class MyViewFactory: ViewFactory { @Injected(\ .chatClient) public var chatClient public static let shared = MyViewFactory() private init() {} public var styles: MyStyles = MyStyles() } ``` ``` -------------------------------- ### MessagePreviewFormatter Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt `MessagePreviewFormatter` is used to format the text preview of messages displayed in the channel list and thread list. It can be subclassed for custom formatting logic. ```APIDOC ## `MessagePreviewFormatter` β€” Channel list message preview text `MessagePreviewFormatter` converts a `ChatMessage` into the one-line preview shown in the channel list row and thread list. Subclass it to override the logic for any message type. ```swift import StreamChat import StreamChatSwiftUI class BrandedMessagePreviewFormatter: MessagePreviewFormatter { override func format( _ previewMessage: ChatMessage, in channel: ChatChannel ) -> String { // Redact deleted messages differently if previewMessage.isDeleted { return "🚫 Message removed" } // Show emoji for voice recordings if previewMessage.voiceRecordingAttachments.first != nil { return "🎀 Voice message" } // Fall back to the default return super.format(previewMessage, in: channel) } } // Register via Utils let utils = Utils(messagePreviewFormatter: BrandedMessagePreviewFormatter()) ``` ``` -------------------------------- ### Poll Creation Sheet Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Present CreatePollView as a sheet for users to create polls within a channel. This view handles the poll question, options, and settings. It can be triggered from an attachment picker. ```swift import StreamChat import StreamChatSwiftUI import SwiftUI struct PollCreationSheet: View { let channelController: ChatChannelController @Binding var isPresented: Bool var body: some View { CreatePollView( factory: DefaultViewFactory.shared, chatController: channelController, messageController: nil ) } } // Present from a button struct ComposerExtraButton: View { let channelController: ChatChannelController @State private var showPollCreator = false var body: some View { Button { showPollCreator = true } label: { Image(systemName: "chart.bar.doc.horizontal") } .sheet(isPresented: $showPollCreator) { PollCreationSheet( channelController: channelController, isPresented: $showPollCreator ) } } } ``` -------------------------------- ### Observe Message List State with ChatChannelViewModel Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Observe message list state externally using ChatChannelViewModel. This view displays message count, typing indicators, thread status, and the first unread message. ```swift import StreamChat import StreamChatSwiftUI import SwiftUI // Observe message list state externally struct ChannelDebugOverlay: View { @ObservedObject var viewModel: ChatChannelViewModel var body: some View { VStack(alignment: .leading) { Text("Messages: \(viewModel.messages.count)") Text("Typing: \(viewModel.shouldShowInlineTypingIndicator ? \"yes\" : \"no\")") Text("Is thread: \(viewModel.isMessageThread ? \"yes\" : \"no\")") if let unread = viewModel.firstUnreadMessageId { Text("First unread: \(unread)") } } .font(.caption) .padding(6) .background(.thinMaterial) .cornerRadius(8) } } ``` -------------------------------- ### Extend ChatChannelListViewModel for Custom Logic Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Subclass ChatChannelListViewModel to add custom logic or state management. Use @Published properties for observable changes and implement methods like reloadWithCurrentFilter to update the channel list. ```swift import StreamChat import StreamChatSwiftUI import SwiftUI // Custom subclass adding an "archived" filter toggle class AppChannelListViewModel: ChatChannelListViewModel { @Published var showingArchived = false { didSet { reloadWithCurrentFilter() } } private func reloadWithCurrentFilter() { // Swap the channel list controller and reload } } // Accessing published state struct ChannelListStatus: View { @ObservedObject var viewModel: ChatChannelListViewModel var body: some View { Group { if viewModel.loading { ProgressView("Loading channels…") } else if viewModel.channels.isEmpty { Text("No channels") } else { Text("\(viewModel.channels.count) channels") } } .onChange(of: viewModel.searchText) { newText in print("Searching: \(newText), isSearching: \(viewModel.isSearching)") } } } ``` -------------------------------- ### Use ViewModelsFactory to Create View Models Programmatically Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Utilize ViewModelsFactory for programmatic access to view models outside of SwiftUI initializers. Ensure StreamChat and StreamChatSwiftUI are imported. ```swift import StreamChat import StreamChatSwiftUI // Channel list view model with a custom controller let query = ChannelListQuery(filter: .containMembers(userIds: ["user-42"])) let listController = chatClient.channelListController(query: query) let channelListVM = ViewModelsFactory.makeChannelListViewModel( channelListController: listController, selectedChannelId: nil, searchType: .channels ) // Channel view model (e.g. for programmatic injection) let channelController = chatClient.channelController(for: .init("messaging", "room-1")) let channelVM = ViewModelsFactory.makeChannelViewModel( with: channelController, messageController: nil, scrollToMessage: nil ) // Composer view model let composerVM = ViewModelsFactory.makeMessageComposerViewModel( with: channelController, messageController: nil, quotedMessage: .constant(nil), editedMessage: .constant(nil), willSendMessage: nil ) // Thread list view model let threadListVM = ViewModelsFactory.makeThreadListViewModel() ``` -------------------------------- ### Custom Markdown Formatting Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Subclass DefaultMarkdownFormatter to customize Markdown rendering, such as applying a background color to inline code. Ensure the formatter is registered with Utils. ```swift import StreamChatSwiftUI import SwiftUI @MainActor class BrandedMarkdownFormatter: DefaultMarkdownFormatter { @available(iOS 15, *) override func format( _ string: String, attributes: AttributeContainer, layoutDirection: LayoutDirection ) -> AttributedString { // Apply branding tint to inline code var branded = super.format(string, attributes: attributes, layoutDirection: layoutDirection) branded.runs.forEach { run in if run.inlinePresentationIntent?.contains(.code) == true { branded[run.range].backgroundColor = .init(.systemTeal.withAlphaComponent(0.2)) } } return branded } } // Register let utils = Utils(markdownFormatter: BrandedMarkdownFormatter()) ``` -------------------------------- ### Send a Message Programmatically with MessageComposerViewModel Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Send a message programmatically using MessageComposerViewModel. This function sets the text and calls the sendMessage method. ```swift import StreamChat import StreamChatSwiftUI // Sending a message programmatically @MainActor func sendGreeting(in channelController: ChatChannelController) { let vm = ViewModelsFactory.makeMessageComposerViewModel( with: channelController, messageController: nil, quotedMessage: .constant(nil), editedMessage: .constant(nil), willSendMessage: nil ) vm.text = "Hello from the SDK! πŸ‘‹" vm.sendMessage( quotedMessage: nil, editedMessage: nil, completion: { print("Message sent") } ) } ``` -------------------------------- ### Custom Message Reminders Formatter in Swift Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Implement a custom `MessageRemindersFormatter` to display reminder dates in a shorter, abbreviated format. This custom formatter is then registered with the `Utils` class. ```swift import StreamChatSwiftUI import Foundation // Custom formatter using a shorter style class ShortMessageRemindersFormatter: MessageRemindersFormatter { private let formatter: RelativeDateTimeFormatter = { let f = RelativeDateTimeFormatter() f.unitsStyle = .abbreviated // "in 2 hr." instead of "in 2 hours" return f }() func format(_ remindAt: Date) -> String? { guard remindAt.timeIntervalSinceNow > 0 else { return nil } return formatter.localizedString(for: remindAt, relativeTo: Date()) } } // Register let utils = Utils(messageRemindersFormatter: ShortMessageRemindersFormatter()) ``` -------------------------------- ### Pinned Messages Browse View Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Present PinnedMessagesView within a NavigationView to allow users to browse all pinned messages in a channel. Tapping a message navigates back to it in the channel view. ```swift import StreamChat import StreamChatSwiftUI import SwiftUI struct PinnedMessagesSheet: View { let channel: ChatChannel var body: some View { NavigationView { PinnedMessagesView( factory: DefaultViewFactory.shared, channel: channel ) .navigationTitle("Pinned Messages") .navigationBarTitleDisplayMode(.inline) } } } ``` -------------------------------- ### Programmatically Scroll to Message with ChannelInteractor Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Scroll programmatically to a specific message ID using ChannelInteractor. Ensure the view model is updated on the main thread. ```swift import StreamChat import StreamChatSwiftUI import SwiftUI // Scroll programmatically to a message id class ChannelInteractor { let viewModel: ChatChannelViewModel init(vm: ChatChannelViewModel) { viewModel = vm } @MainActor func jumpToMessage(id: String) { viewModel.scrolledId = id } } ``` -------------------------------- ### Glass Effect Card Modifier Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Apply a borderless glass effect overlay to content using LiquidGlassBorderlessModifier. This modifier is suitable for cards and includes a fallback for older OS versions. ```swift import StreamChatSwiftUI import SwiftUI // Borderless glass overlay card struct GlassCard: View { let content: () -> Content var body: some View { content() .padding() .modifier( LiquidGlassBorderlessModifier( shape: RoundedRectangle(cornerRadius: 20) ) ) } } ``` -------------------------------- ### Glass Effect Button Modifier Source: https://context7.com/getstream/stream-chat-swiftui/llms.txt Apply a native iOS glass effect to a button using LiquidGlassModifier. This modifier provides interactive feedback on press and includes a fallback for older OS versions. ```swift import StreamChatSwiftUI import SwiftUI // Bordered glass button (interactive feedback on press) struct GlassIconButton: View { let action: () -> Void var body: some View { Button(action: action) { Image(systemName: "paperplane.fill") .padding(12) } .modifier( LiquidGlassModifier( shape: RoundedRectangle(cornerRadius: 14), isInteractive: true ) ) } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.