### English Localizable.strings Example Source: https://github.com/exyte/chat/blob/main/_autodocs/configuration.md Example content for an English Localizable.strings file, mapping keys to their English string values. ```strings "Type a message..." = "Type a message..."; "Add signature..." = "Add signature..."; "Cancel" = "Cancel"; "Recents" = "Recents"; "Waiting for network" = "Waiting for network"; "Recording..." = "Recording..."; "Reply to" = "Reply to"; ``` -------------------------------- ### Complete ChatView Example Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/chatview.md A comprehensive example demonstrating various ChatView customizations, including message building, avatar display, message time, infinite scrolling, and theme settings. ```swift import SwiftUI import ExyteChat struct ContentView: View { @State var messages: [Message] = [] @State var updateTransaction: TableUpdateTransaction? var body: some View { ChatView(messages: messages) { draft in Task { let message = await Message.makeMessage( id: UUID().uuidString, user: currentUser, draft: draft ) messages.append(message) } } messageBuilder: { params in if params.message.user.isCurrentUser { HStack { Spacer() VStack(alignment: .trailing) { Text(params.message.attributedText) .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) } } .padding(.horizontal) } else { params.defaultMessageView() } } .showAvatar(true) .showMessageTimeView(true) .enableLoadMoreOlderMessages( triggerType: .pixels(200), hasMoreToLoad: true ) { await viewModel.loadOlderMessages() } .updateTransaction($updateTransaction) .chatTheme(.init(colors: .init( mainBG: Color(UIColor.systemBackground), messageMyBG: Color.blue ))) } } ``` -------------------------------- ### Spanish Localizable.strings Example Source: https://github.com/exyte/chat/blob/main/_autodocs/configuration.md Example content for a Spanish Localizable.strings file (es), mapping keys to their Spanish string values. ```strings "Type a message..." = "Escribe un mensaje..."; "Add signature..." = "Agregar firma..."; "Cancel" = "Cancelar"; "Recents" = "Recientes"; "Waiting for network" = "Esperando conexión"; "Recording..." = "Grabando..."; "Reply to" = "Responder a"; ``` -------------------------------- ### Complete Upload Example with Progress Tracking Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/attachments.md A comprehensive example demonstrating how to initiate, upload, track progress, and handle completion or errors for attachments within a chat interface. It includes background uploading and UI updates. ```swift @State var messages: [Message] = [] @State var uploadProgress: [String: Int] = [:] // attachmentId -> percentage var body: some View { ChatView(messages: messages) { draft in Task { // Create message with attachments var message = await Message.makeMessage( id: UUID().uuidString, user: currentUser, draft: draft ) // Start uploads var updatedAttachments: [Attachment] = [] for attachment in message.attachments { let uploadingAttachment = attachment.copy( fullUploadStatus: .inProgress(0) ) updatedAttachments.append(uploadingAttachment) // Upload in background uploadAttachment(uploadingAttachment, for: message.id) } message.attachments = updatedAttachments messages.append(message) } } didUpdateAttachmentStatus: { update in if update.updateAction == .cancel { cancelAttachmentUpload(update.attachmentId) } } } private func uploadAttachment(_ attachment: Attachment, for messageId: String) { Task { do { // Simulate upload with progress for percent in 0...99 { try await Task.sleep(nanoseconds: 100_000_000) // Simulate delay let updatedAttachment = attachment.copy( fullUploadStatus: .inProgress(percent) ) if let messageIndex = messages.firstIndex(where: { $0.id == messageId }), let attachmentIndex = messages[messageIndex].attachments.firstIndex(where: { $0.id == attachment.id }) { messages[messageIndex].attachments[attachmentIndex] = updatedAttachment } uploadProgress[attachment.id] = percent } // Complete let completedAttachment = attachment.copy( fullUploadStatus: .complete ) if let messageIndex = messages.firstIndex(where: { $0.id == messageId }), let attachmentIndex = messages[messageIndex].attachments.firstIndex(where: { $0.id == attachment.id }) { messages[messageIndex].attachments[attachmentIndex] = completedAttachment } uploadProgress.removeValue(forKey: attachment.id) } catch { let errorAttachment = attachment.copy( fullUploadStatus: .error ) if let messageIndex = messages.firstIndex(where: { $0.id == messageId }), let attachmentIndex = messages[messageIndex].attachments.firstIndex(where: { $0.id == attachment.id }) { messages[messageIndex].attachments[attachmentIndex] = errorAttachment } } } } private func cancelAttachmentUpload(_ attachmentId: String) { // Handle cancellation uploadProgress.removeValue(forKey: attachmentId) } ``` -------------------------------- ### Complete Swipe Actions Example Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/swipe-actions.md A comprehensive example demonstrating multiple swipe actions on leading and trailing edges, including reply, share, delete, and favorite functionalities. ```swift struct ContentView: View { @State var messages: [Message] = [] var body: some View { ChatView(messages: messages) { draft in Task { let message = await Message.makeMessage( id: UUID().uuidString, user: currentUser, draft: draft ) messages.append(message) try? await apiService.sendMessage(draft) } } .swipeActions( edge: .trailing, performsFirstActionWithFullSwipe: false, items: [ // Reply action SwipeAction( action: { message, defaultHandler in defaultHandler(message, .reply) }, background: .blue ) { Label("Reply", systemImage: "arrowshape.turn.up.left") }, // Share action SwipeAction( action: { message, _ in shareMessage(message) }, background: .green ) { Label("Share", systemImage: "square.and.arrow.up") }, // Delete action (own messages only) SwipeAction( action: { message, _ in Task { await apiService.deleteMessage(message.id) messages.removeAll { $0.id == message.id } } }, activeFor: { $0.user.isCurrentUser }, background: .red ) { Label("Delete", systemImage: "trash") } ] ) .swipeActions( edge: .leading, items: [ // Favorite action SwipeAction( action: { message, _ in Task { await viewModel.toggleFavorite(message) } }, background: .yellow ) { Image(systemName: "star.fill") } ] ) } private func shareMessage(_ message: Message) { let activity = UIActivityViewController( activityItems: [message.text], applicationActivities: nil ) UIApplication.shared.windows.first?.rootViewController?.present(activity, animated: true) } } ``` -------------------------------- ### Example: Customizing ChatTheme Background Images Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/chattheme.md Demonstrates how to create a ChatTheme instance with custom background images for various light/dark and portrait/landscape configurations. ```swift let theme = ChatTheme( colors: ChatTheme.Colors(), images: ChatTheme.Images( background: ChatTheme.Images.Background( portraitBackgroundLight: Image("chatBg"), portraitBackgroundDark: Image("chatBgDark"), landscapeBackgroundLight: Image("chatBgLandscape"), landscapeBackgroundDark: Image("chatBgLandscapeDark") ) ) ) ``` -------------------------------- ### Pagination Migration Example Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/pagination.md Compares the old method of enabling message loading with the new recommended approach using TriggerType. ```swift // Old .enableLoadMore(offset: 5, loadMoreHandler) // New .enableLoadMoreOlderMessages(triggerType: .cellIndex(5)) { await loadMoreHandler() } ``` -------------------------------- ### Complete Chat View Customization Example Source: https://github.com/exyte/chat/blob/main/_autodocs/configuration.md An example demonstrating the application of various message view customization options, including timestamps, usernames, avatars, fonts, link previews, and tap actions. ```swift ChatView(messages: messages) { draft in viewModel.send(draft: draft) } .showMessageTimeView(true) .showUsername(true) .showAvatar(true) .avatarSize(40) .setMessageFont(UIFont.systemFont(ofSize: 16, weight: .regular)) .linkPreviewsEnabled(true) .messageLinkPreviewLimit(2) .shouldShowPreviewForLink { url in // Don't preview ads !url.absoluteString.contains("ads") } .tapAvatarClosure { user, messageId in viewModel.showUserProfile(user) } ``` -------------------------------- ### ChatView with Custom Available Inputs Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/input-view.md Example of initializing a ChatView and setting specific available input types, disabling Giphy in this case. ```swift ChatView(messages: messages) { viewModel.send(draft: draft) } .setAvailableInputs([.text, .media, .audio]) // Giphy disabled ``` -------------------------------- ### Swift Package Manager Installation Source: https://github.com/exyte/chat/blob/main/README.md Add the Exyte Chat library to your project's dependencies using Swift Package Manager. ```swift dependencies: [ .package(url: "https://github.com/exyte/Chat.git") ] ``` -------------------------------- ### Customized ChatView Implementation Source: https://github.com/exyte/chat/blob/main/_autodocs/README.md This example demonstrates a customized ChatView with custom message views, enabled link previews, specific theme colors, available inputs, and message reaction handling. ```swift ChatView(messages: messages) { draft in viewModel.send(draft: draft) } messageBuilder: { params in if params.message.user.isCurrentUser { HStack { Spacer() VStack(alignment: .trailing) { Text(params.message.attributedText) .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(12) } } } else { params.defaultMessageView() } } .showAvatar(true) .showMessageTimeView(true) .linkPreviewsEnabled(true) .chatTheme(ChatTheme(colors: .init( messageMyBG: .blue, messageFriendBG: .gray ))) .setAvailableInputs([.text, .media, .audio, .giphy]) .onMessageReaction( didReactTo: { message, reaction in apiService.sendReaction(message, reaction) } ) ``` -------------------------------- ### Format Swift Code with swift-format Source: https://github.com/exyte/chat/blob/main/CONTRIBUTING.md Use this command to format your Swift code according to the project's style guidelines. Ensure swift-format is installed and the configuration file is present. ```bash swift-format format -i --configuration .swift-format Sources/ExyteChat/ChatView/ChatView.swift ``` -------------------------------- ### Complete Chat View with Pagination Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/pagination.md An example demonstrating how to implement both older and newer message loading using the current pagination API with various trigger types. ```swift @State var messages: [Message] = [] @State var isLoadingOlder = false @State var isLoadingNewer = false @State var canLoadOlder = true @State var canLoadNewer = false @State var pageSize = 20 var body: some View { ChatView(messages: messages) { draft in Task { let message = await Message.makeMessage( id: UUID().uuidString, user: currentUser, draft: draft ) messages.append(message) try? await apiService.sendMessage(draft) } } .enableLoadMoreOlderMessages( triggerType: .pixels(300), hasMoreToLoad: canLoadOlder ) { isLoadingOlder = true defer { isLoadingOlder = false } do { let batch = try await apiService.loadMessages( before: messages.first?.id, limit: pageSize ) if !batch.isEmpty { messages.insert(contentsOf: batch, at: 0) } canLoadOlder = batch.count == pageSize } catch { showAlert("Failed to load older messages", error.localizedDescription) } } .enableLoadMoreNewerMessages( triggerType: .pixels(100), hasMoreToLoad: canLoadNewer ) { isLoadingNewer = true defer { isLoadingNewer = false } do { let batch = try await apiService.loadMessages( after: messages.last?.id, limit: pageSize ) if !batch.isEmpty { messages.append(contentsOf: batch) } canLoadNewer = batch.count == pageSize } catch { showAlert("Failed to load newer messages", error.localizedDescription) } } .onAppear { Task { // Load initial batch let initial = try await apiService.loadRecentMessages(limit: pageSize) messages = initial canLoadOlder = true canLoadNewer = false } } } private func showAlert(_ title: String, _ message: String) { // Show error UI } ``` -------------------------------- ### Customize Media Picker Limits and Parameters Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/input-view.md Configure the media picker to control the number of assets that can be selected and the types of media allowed. This example limits selection to 5 items of any media type. ```swift ChatView(messages: messages) { draft in viewModel.send(draft: draft) } .assetsPickerLimit(assetsPickerLimit: 5) // Max 5 items .setMediaPickerSelectionParameters( MediaPickerSelectionParameters( selectionLimit: 5, mediaType: .all // Photos and videos ) ) ``` -------------------------------- ### Applying Swipe Actions to ChatView Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/swipe-actions.md Demonstrates how to apply swipe actions to the `ChatView` using the `.swipeActions` modifier. This example shows a basic reply action. ```APIDOC ## Basic Example ```swift ChatView(messages: messages) { draft in viewModel.send(draft: draft) } .swipeActions(edge: .trailing, items: [ SwipeAction( action: { message, defaultHandler in defaultHandler(message, .reply) }, background: .blue ) { Label("Reply", systemImage: "arrowshape.turn.up.left") } ]) ``` ``` -------------------------------- ### Configure Message Reactions Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/chatview.md Configure message reactions using either a custom delegate or closures. The closure-based approach is demonstrated in the example. ```swift func messageReactionDelegate(_ reactionDelegate: ReactionDelegate) -> ChatView func onMessageReaction( didReactTo: @escaping (Message, DraftReaction) -> Void, canReactTo: ((Message) -> Bool)? = nil, availableReactionsFor: ((Message) -> [ReactionType]?)? = nil, allowEmojiSearchFor: ((Message) -> Bool)? = nil, shouldShowOverviewFor: ((Message) -> Bool)? = nil ) -> ChatView ``` ```swift ChatView(messages: messages) { draft in viewModel.send(draft: draft) } .onMessageReaction( didReactTo: { message, reaction in viewModel.saveReaction(message, reaction) }, canReactTo: { message in !message.user.isCurrentUser } ) ``` -------------------------------- ### Custom ReactionDelegate Implementation Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/reactions.md An example implementation of the ReactionDelegate protocol. This snippet shows how to customize reaction logic, such as sending reactions to a backend, restricting reactions to recent messages, and providing custom emoji sets. ```swift class CustomReactionDelegate: ReactionDelegate { func didReact(to message: Message, reaction: DraftReaction) { // Send reaction to backend Task { await apiService.sendReaction( messageID: message.id, reaction: reaction ) } } func canReact(to message: Message) -> Bool { // Only allow reactions on recent messages Date.now.timeIntervalSince(message.createdAt) < 86400 } func reactions(for message: Message) -> [ReactionType]? { // Custom emoji set for important messages if message.customData["important"] as? Bool == true { return [ .emoji("👍"), .emoji("❤️"), .emoji("🎉"), .emoji("🔥"), ] } return nil // Use defaults } func allowEmojiSearch(for message: Message) -> Bool { true // Always allow search } func shouldShowOverview(for message: Message) -> Bool { !message.reactions.isEmpty } } ChatView(messages: messages) { draft in viewModel.send(draft: draft) } .messageReactionDelegate(CustomReactionDelegate()) ``` -------------------------------- ### Apply Custom Audio Recording Settings Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/input-view.md Apply custom recorder settings to the ChatView to modify audio recording behavior. This example shows how to set a higher sample rate. ```swift let settings = RecorderSettings( audioFormatID: kAudioFormatMPEG4AAC, sampleRate: 16000, // Higher quality numberOfChannels: 1 ) ChatView(messages: messages) { draft in viewModel.send(draft: draft) } .setRecorderSettings(settings) ``` -------------------------------- ### Custom Message Menu Action Implementation Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/message-menu.md An example implementation of the MessageMenuAction protocol for custom menu actions like share and report. It defines titles, icons, and logic for which items to display based on the message sender. ```swift enum CustomMenuAction: MessageMenuAction { case copy case reply case share case report case quote func title() -> String { switch self { case .copy: "Copy" case .reply: "Reply" case .share: "Share" case .report: "Report" case .quote: "Quote" } } func icon() -> Image { switch self { case .copy: Image(systemName: "doc.on.doc") case .reply: Image(systemName: "arrowshape.turn.up.left") case .share: Image(systemName: "square.and.arrow.up") case .report: Image(systemName: "exclamationmark.triangle") case .quote: Image(systemName: "text.quote") } } static func menuItems(for message: Message) -> [CustomMenuAction] { if message.user.isCurrentUser { return [.copy, .reply, .quote] } else { return [.copy, .reply, .share, .report] } } // Required by CaseIterable static let allCases: [CustomMenuAction] = [ .copy, .reply, .share, .report, .quote ] } ``` -------------------------------- ### Customizing Chat Theme with Colors and Images Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/chattheme.md Apply a custom theme to the ChatView by providing a ChatTheme object with specific color and image configurations. This example demonstrates setting background colors, text colors, message bubble colors, input field styles, and custom background images for different orientations and light/dark modes. ```swift import SwiftUI import ExyteChat struct ContentView: View { @State var messages: [Message] = [] var body: some View { ChatView(messages: messages) { draft in // Send message } .chatTheme( ChatTheme( colors: ChatTheme.Colors( mainBG: Color(red: 0.1, green: 0.1, blue: 0.15), mainTint: Color.cyan, mainText: .white, messageMyBG: Color.cyan, messageMyText: Color.black, messageFriendBG: Color(red: 0.2, green: 0.2, blue: 0.25), messageFriendText: .white, inputBG: Color(red: 0.2, green: 0.2, blue: 0.25), inputText: .white, inputPlaceholderText: Color.gray, sendButtonBackground: Color.cyan ), images: ChatTheme.Images( background: ChatTheme.Images.Background( portraitBackgroundLight: Image("chat_bg_light"), portraitBackgroundDark: Image("chat_bg_dark"), landscapeBackgroundLight: Image("chat_bg_landscape_light"), landscapeBackgroundDark: Image("chat_bg_landscape_dark") ) ) ) ) } } ``` -------------------------------- ### User Initializers Source: https://github.com/exyte/chat/blob/main/_autodocs/types.md Provides methods to create User instances, allowing for different configurations based on whether the user is the current participant. ```swift public init(id: String, name: String, avatarURL: URL?, avatarCacheKey: String? = nil, isCurrentUser: Bool) public init(id: String, name: String, avatarURL: URL?, avatarCacheKey: String? = nil, type: UserType) ``` -------------------------------- ### Configure Media Picker Selection Parameters Source: https://github.com/exyte/chat/blob/main/_autodocs/configuration.md Set detailed selection parameters including limit and media type for the media picker. ```swift .setMediaPickerSelectionParameters( MediaPickerSelectionParameters( selectionLimit: 5, mediaType: .all // Photos and videos // ... other options ) ) ``` -------------------------------- ### Multiple Swipe Actions Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/swipe-actions.md Add multiple swipe actions to the trailing edge of messages. This example includes 'Reply', 'Copy', and 'Report' actions. ```swift ChatView(messages: messages) { draft in viewModel.send(draft: draft) } .swipeActions(edge: .trailing, items: [ SwipeAction( action: { message, defaultHandler in defaultHandler(message, .reply) }, background: .blue ) { Image(systemName: "arrowshape.turn.up.left") }, SwipeAction( action: { message, defaultHandler in defaultHandler(message, .copy) }, background: .gray ) { Image(systemName: "doc.on.doc") }, SwipeAction( action: { message, _ in // Custom action viewModel.reportMessage(message) }, background: .red ) { Image(systemName: "flag") } ]) ``` -------------------------------- ### Add Custom Swipe Actions to Message Cells Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/chatview.md Add custom swipe actions to message cells. The example shows how to implement a delete action. ```swift func swipeActions(edge: HorizontalEdge = .trailing, performsFirstActionWithFullSwipe: Bool = true, items: [SwipeAction]) -> ChatView ``` ```swift ChatView(messages: messages) { draft in viewModel.send(draft: draft) } .swipeActions(items: [ SwipeAction( action: { message, defaultAction in viewModel.deleteMessage(message) }, activeFor: { $0.user.isCurrentUser }, background: .red ) { Label("Delete", systemImage: "trash") } ]) ``` -------------------------------- ### Simple URL Constructor Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/attachments.md Initializes an Attachment using a single URL for both thumbnail and full image. This is the simplest way to create an attachment when no separate thumbnail is needed or available. ```APIDOC ## Simple URL Constructor ### Description Initializes an Attachment using a single URL for both thumbnail and full image. This is the simplest way to create an attachment when no separate thumbnail is needed or available. ### Method Signature ```swift public init(id: String, url: URL, type: AttachmentType, cacheKey: String? = nil) ``` ### Parameters - **id** (string) - Required - A unique identifier for the attachment. - **url** (URL) - Required - The URL for the attachment (used for both thumbnail and full image). - **type** (AttachmentType) - Required - The type of the attachment (e.g., .image, .video). - **cacheKey** (string?) - Optional - A key for caching the attachment. ### Example ```swift let attachment = Attachment( id: UUID().uuidString, url: imageURL, type: .image, cacheKey: "user_photo_123" ) ``` ``` -------------------------------- ### Configure Sticker Keyboard with Giphy Source: https://github.com/exyte/chat/blob/main/README.md Include the sticker keyboard and configure Giphy integration by providing a client ID and specifying media types. Ensure to set `showAttributionMark` to true for Giphy's production key requirements. ```swift .setAvailableInputs([.text, .giphy]) .giphyConfig( GiphyConfiguration( giphyKey: "client id", mediaTypeConfig: [.recents, .gifs, .stickers, .clips], showAttributionMark: true ) ) ``` -------------------------------- ### Conditional Swipe Action Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/swipe-actions.md Show a swipe action only for specific messages based on a condition. This example displays an 'Edit' action only for messages sent by the current user. ```swift SwipeAction( action: { message, defaultHandler in defaultHandler(message, .edit { editedText in // Save edited text }) }, activeFor: { message in // Only show edit for own messages message.user.isCurrentUser }, background: .orange ) { Label("Edit", systemImage: "pencil") } ``` -------------------------------- ### Full Constructor Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/attachments.md Initializes an Attachment with separate URLs for thumbnail and full content, along with optional cache keys and upload status tracking. This constructor is useful for more complex scenarios involving distinct thumbnail and full-size assets. ```APIDOC ## Full Constructor ### Description Initializes an Attachment with separate URLs for thumbnail and full content, along with optional cache keys and upload status tracking. This constructor is useful for more complex scenarios involving distinct thumbnail and full-size assets. ### Method Signature ```swift public init( id: String, thumbnail: URL, full: URL, type: AttachmentType, thumbnailCacheKey: String? = nil, fullCacheKey: String? = nil, fullUploadStatus: UploadStatus? = nil ) ``` ### Parameters - **id** (string) - Required - A unique identifier for the attachment. - **thumbnail** (URL) - Required - The URL for the thumbnail image. - **full** (URL) - Required - The URL for the full-size content. - **type** (AttachmentType) - Required - The type of the attachment (e.g., .image, .video). - **thumbnailCacheKey** (string?) - Optional - A cache key for the thumbnail. - **fullCacheKey** (string?) - Optional - A cache key for the full content. - **fullUploadStatus** (UploadStatus?) - Optional - The upload status of the full content. ### Example with upload tracking: ```swift let attachment = Attachment( id: UUID().uuidString, thumbnail: thumbURL, full: fullURL, type: .video, thumbnailCacheKey: "video_thumb_456", fullCacheKey: "video_full_456", fullUploadStatus: .inProgress(nil) // Show progress without percentage ) ``` ``` -------------------------------- ### Customize Built-in Message View Appearance Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/chatview.md Customize the appearance of the default message view, including avatar, font, and link previews. The example demonstrates several of these customizations. ```swift func showMessageTimeView(_ show: Bool) -> ChatView func showUsername(_ show: Bool) -> ChatView func messageLinkPreviewLimit(_ limit: Int) -> ChatView func linkPreviewsEnabled(_ enabled: Bool) -> ChatView func shouldShowPreviewForLink(_ shouldShowPreviewForLink: @escaping (URL) -> Bool) -> ChatView func setMessageFont(_ font: UIFont) -> ChatView func showAvatar(_ show: Bool) -> ChatView func avatarSize(avatarSize: CGFloat) -> ChatView func tapAvatarClosure(_ closure: @escaping TapAvatarClosure) -> ChatView func avatarBuilder(_ builder: @escaping (User)->V) -> ChatView ``` ```swift ChatView(messages: messages) { draft in viewModel.send(draft: draft) } .showAvatar(true) .avatarSize(40) .setMessageFont(UIFont.systemFont(ofSize: 16)) .tapAvatarClosure { user, messageId in viewModel.showUserProfile(user) } ``` -------------------------------- ### Attachment Initializers Source: https://github.com/exyte/chat/blob/main/_autodocs/types.md Provides methods to create Attachment instances, supporting both detailed initialization with upload status and a simpler version for existing media. ```swift public init(id: String, thumbnail: URL, full: URL, type: AttachmentType, thumbnailCacheKey: String? = nil, fullCacheKey: String? = nil, fullUploadStatus: UploadStatus? = nil) public init(id: String, url: URL, type: AttachmentType, cacheKey: String? = nil) ``` -------------------------------- ### Pixel Trigger Example Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/pagination.md Use the pixel trigger to load newer messages when the user scrolls within a specified pixel distance from the edge. This is suitable for smooth, continuous loading. ```swift case pixels(_ offset: CGFloat) ``` ```swift .enableLoadMoreNewerMessages( triggerType: .pixels(200), // Load when 200px from bottom hasMoreToLoad: hasMore ) { await viewModel.loadNewerMessages() } ``` -------------------------------- ### Handle Draft Message Sending Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/input-view.md An example handler for processing a draft message. It constructs a full message, optimistically adds it to the UI, uploads attachments, sends the message, and updates its status. ```swift ChatView(messages: messages) { draft in Task { // Build full message let message = await Message.makeMessage( id: UUID().uuidString, user: currentUser, status: .sending, draft: draft ) // Optimistically add to UI messages.append(message) // Upload attachments and send do { let uploadedDraft = try await uploadAttachments(draft) let finalMessage = await apiService.sendMessage(uploadedDraft) // Update status if let index = messages.firstIndex(where: { $0.id == message.id }) { messages[index].status = .sent } } catch { if let index = messages.firstIndex(where: { $0.id == message.id }) { messages[index].status = .error(draft) } } } } ``` -------------------------------- ### ChatTheme.Images.FullscreenMedia Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/chattheme.md Media viewer controls. ```APIDOC ## ChatTheme.Images.FullscreenMedia ### Description Media viewer controls. ### Properties ```swift public struct FullscreenMedia { public var play: Image public var pause: Image public var mute: Image public var unmute: Image } ``` ``` -------------------------------- ### Cell Index Trigger Example Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/pagination.md Use the cell index trigger to load older messages when a specific number of messages from the edge are visible. This method works regardless of scroll speed. ```swift case cellIndex(_ offset: Int) ``` ```swift .enableLoadMoreOlderMessages( triggerType: .cellIndex(5), // Load when 5th-oldest visible hasMoreToLoad: hasMore ) { await viewModel.loadOlderMessages() } ``` -------------------------------- ### Attachment Initializer with URL Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/attachments.md Use this initializer when the thumbnail and full image share the same URL. It's the simplest way to create an attachment. ```swift public init(id: String, url: URL, type: AttachmentType, cacheKey: String? = nil) ``` ```swift let attachment = Attachment( id: UUID().uuidString, url: imageURL, type: .image, cacheKey: "user_photo_123" ) ``` -------------------------------- ### Upload Progress Workflow - Option 1: No Status Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/attachments.md This workflow describes the simplest approach where the upload is completed before the attachment is created and the message is sent. The receiver gets the completed attachment immediately. ```APIDOC ## Upload Progress Workflow - Option 1: No Status ### Description This workflow describes the simplest approach where the upload is completed before the attachment is created and the message is sent. The receiver gets the completed attachment immediately. ### Flow 1. User selects image 2. Upload to server (100% complete) 3. Create attachment with permanent URL 4. Send message 5. Receiver gets completed attachment immediately ### Example ```swift // Complete upload before creating attachment let uploadedURL = try await uploadToServer(imageData) let attachment = Attachment( id: UUID().uuidString, url: uploadedURL, type: .image // No fullUploadStatus ) ``` ``` -------------------------------- ### ChatTheme.Images.MediaPicker Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/chattheme.md Media selection UI icons. ```APIDOC ## ChatTheme.Images.MediaPicker ### Description Media selection UI icons. ### Properties ```swift public struct MediaPicker { public var chevronDown: Image public var chevronRight: Image public var cross: Image } ``` ``` -------------------------------- ### Customize Media Picker Settings Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/chatview.md Configure the appearance and behavior of the built-in media picker, including live camera style, asset selection limits, and parameter handling. ```swift func setMediaPickerLiveCameraStyle(_ style: MediaPickerLiveCameraStyle) -> ChatView func assetsPickerLimit(assetsPickerLimit: Int) -> ChatView func setMediaPickerSelectionParameters(_ params: MediaPickerSelectionParameters) -> ChatView func orientationHandler(orientationHandler: @escaping MediaPickerOrientationHandler) -> ChatView func setMediaPickerParameters(_ params: MediaPickerParameters) -> ChatView ``` -------------------------------- ### Configure Audio Recording Settings Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/input-view.md Define custom settings for audio recording, such as audio format, sample rate, and bit rate. Defaults are provided for common use cases. ```swift public struct RecorderSettings: Codable, Hashable { var audioFormatID: AudioFormatID // Default: kAudioFormatMPEG4AAC var sampleRate: CGFloat // Default: 12000 var numberOfChannels: Int // Default: 1 (mono) var encoderBitRateKey: Int // Default: 0 (automatic) var linearPCMBitDepth: Int // Default: 16 var linearPCMIsFloatKey: Bool // Default: false var linearPCMIsBigEndianKey: Bool // Default: false var linearPCMIsNonInterleaved: Bool // Default: false public init( audioFormatID: AudioFormatID = kAudioFormatMPEG4AAC, sampleRate: CGFloat = 12000, numberOfChannels: Int = 1, encoderBitRateKey: Int = 0, linearPCMBitDepth: Int = 16, linearPCMIsFloatKey: Bool = false, linearPCMIsBigEndianKey: Bool = false, linearPCMIsNonInterleaved: Bool = false ) } ``` -------------------------------- ### Custom Message View with Message Builder Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/message-menu.md Example of integrating custom UI elements and actions within a message using the message builder. This allows for custom long-press menus and action handling. ```swift ChatView(messages: messages) { draft in viewModel.send(draft: draft) } messageBuilder: { params in VStack { Text(params.message.attributedText) // Custom long-press menu button Button(action: params.showContextMenuClosure) { Image(systemName: "ellipsis") } } .onLongPressGesture { params.showContextMenuClosure() } ``` -------------------------------- ### ChatTheme Initializer Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/chattheme.md Initializes a ChatTheme with default or custom color, image, and style settings. ```swift public init( colors: ChatTheme.Colors = .init(), images: ChatTheme.Images = .init(), style: ChatTheme.Style = .init() ) ``` -------------------------------- ### Enable and Configure Giphy in ChatView Source: https://github.com/exyte/chat/blob/main/_autodocs/configuration.md Demonstrates how to enable the Giphy input and configure its settings using the `.giphyConfig` modifier. Ensure to set `showAttributionMark` to true for production API keys. ```swift ChatView(messages: messages) { draft in viewModel.send(draft: draft) } .setAvailableInputs([.text, .media, .audio, .giphy]) .giphyConfig(GiphyConfiguration( giphyKey: "YOUR_API_KEY", mediaTypeConfig: [.gifs, .stickers], showAttributionMark: true // Required for production )) ``` -------------------------------- ### Preserving Scroll Position During Pagination Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/pagination.md Integrate with scroll position to maintain a stable view when loading older messages. This example uses a `TableUpdateTransaction` to insert new messages at the top while keeping the scroll position consistent. ```swift @State var updateTransaction: TableUpdateTransaction? .enableLoadMoreOlderMessages( triggerType: .pixels(200), hasMoreToLoad: canLoadOlder ) { let batch = try await apiService.loadOlderMessages(before: messages.first?.id) // Update while keeping scroll position stable await updateTransaction?(animationMode: .keepStable) { messages.insert(contentsOf: batch, at: 0) } } .updateTransaction($updateTransaction) ``` -------------------------------- ### Minimal ChatView Implementation Source: https://github.com/exyte/chat/blob/main/_autodocs/README.md This snippet shows the most basic implementation of ChatView. It initializes the chat with an empty message list and handles sending new messages. ```swift import SwiftUI import ExyteChat struct ContentView: View { @State var messages: [Message] = [] var body: some View { ChatView(messages: messages) { draft in Task { let message = await Message.makeMessage( id: UUID().uuidString, user: User(id: "1", name: "Me", avatarURL: nil, isCurrentUser: true), draft: draft ) messages.append(message) } } } } ``` -------------------------------- ### Chat Theme Customization System Source: https://github.com/exyte/chat/blob/main/_autodocs/MANIFEST.md Details the theme customization system for the chat library, including the ChatTheme structure, environment integration, color properties, image configurations, dark mode support, and a complete theming example. ```APIDOC ## Chat Theme Customization System ### Description Explains the theme customization system for the chat library. Covers the ChatTheme structure, initializer, environment integration, extensive color properties, image configurations for various UI elements, background image settings, dark mode support, and provides a complete theming example. ### Coverage - ChatTheme structure and initializer - Environment integration - Colors (40+ customizable color properties) - Images (Background, AttachMenu, InputView, FullscreenMedia, MediaPicker, Message, Reply) - Background image configuration - Color category reference table - Dark mode support - Complete theming example ``` -------------------------------- ### ChatTheme Initializer for Theme Variations Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/chattheme.md This initializer is useful for creating variations of a theme, such as light and dark modes. It copies all colors from an existing theme except for the main background color. ```swift public init(copy: Colors, mainBG: Color) ``` -------------------------------- ### Handle Device Orientation in Media Picker Source: https://github.com/exyte/chat/blob/main/_autodocs/configuration.md Implement a callback to manage device orientation changes during the media picking process. ```swift .orientationHandler { newOrientation in // Handle device rotation during media picking } ``` -------------------------------- ### Basic Swipe Action for Reply Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/swipe-actions.md Demonstrates how to attach swipe actions to messages, specifically adding a 'Reply' action that triggers the default reply handler. ```swift ChatView(messages: messages) { viewModel.send(draft: draft) } .swipeActions(edge: .trailing, items: [ SwipeAction( action: { message, defaultHandler in defaultHandler(message, .reply) }, background: .blue ) { Label("Reply", systemImage: "arrowshape.turn.up.left") } ]) ``` -------------------------------- ### ChatView Component and Builders Source: https://github.com/exyte/chat/blob/main/_autodocs/MANIFEST.md Documentation for the ChatView component, its initializers, view builders, customization options, scrolling behavior, update transactions, pagination, localization, message reactions, swipe actions, and built-in message/input view customizations. ```APIDOC ## ChatView Component and Builders ### Description Provides documentation for the ChatView component, its signature, initializers, and various view builders. Covers customization of chat layout, display, content insets, scrolling, update transactions, pagination, localization, message reactions, swipe actions, and built-in message and input view customizations. ### Coverage - ChatView signature and initializer - View builders (mainHeaderBuilder, dateHeaderBuilder, betweenListAndInputViewBuilder) - Chat customizations (layout, display, content insets) - Scrolling and offsets - Update transactions - Pagination (enableLoadMore, loadOlderMessages, loadNewerMessages) - Localization - Message reactions - Swipe actions - Built-in message view customizations - Built-in input view customizations - Media picker customizations - Complete usage examples ``` -------------------------------- ### Complete ChatView with Input View Customization Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/input-view.md This snippet shows a full implementation of ChatView, including a custom input view builder. It demonstrates how to handle message sending, attachments like replies, and various input actions such as photo and Giphy selection. Use this for a comprehensive chat interface. ```swift @State var messages: [Message] = [] @State var draftText = "" var body: some View { ChatView(messages: messages) { draft in Task { let message = await Message.makeMessage( id: UUID().uuidString, user: currentUser, draft: draft ) messages.append(message) try? await apiService.sendMessage(draft) } } inputViewBuilder: { params in VStack(spacing: 8) { if let reply = params.attachments.replyMessage { ReplyIndicator(reply: reply) { params.inputViewActionClosure(.cancelEdit) // Clear } } HStack { if params.inputViewStyle == .message { HStack(spacing: 8) { Button(action: { params.inputViewActionClosure(.photo) }) { Image(systemName: "paperclip") } if isGiphyAvailable { Button(action: { params.inputViewActionClosure(.giphy) }) { Image(systemName: "photo.fill") } } } } TextField("Message", text: params.text) .textFieldStyle(.roundedBorder) if params.inputViewState.canSend { Button(action: { params.inputViewActionClosure(.send) }) { Image(systemName: "arrow.up.circle.fill") } } } .padding() } } .inputViewText($draftText) .setAvailableInputs([.text, .media, .audio, .giphy]) .setRecorderSettings(RecorderSettings(sampleRate: 16000)) .giphyConfig(GiphyConfiguration(giphyKey: "YOUR_KEY")) } ``` -------------------------------- ### ChatView Constructor with Message Menu Action Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/message-menu.md Illustrates the `ChatView` constructor, highlighting the `messageMenuAction` parameter which accepts a closure for handling menu interactions. The default implementation delegates to built-in handlers. ```swift public init( messages: [Message], chatType: ChatType = .conversation, replyMode: ReplyMode = .quote, didSendMessage: @escaping (DraftMessage) -> Void, messageBuilder: ..., inputViewBuilder: ..., messageMenuAction: @escaping ( _ selectedMenuAction: MenuAction, _ defaultActionClosure: @escaping (Message, DefaultMessageMenuAction) -> Void, _ message: Message ) -> Void = { (selectedMenuAction: DefaultMessageMenuAction, defaultActionClosure, message) in defaultActionClosure(message, selectedMenuAction) }, didUpdateAttachmentStatus: ... ) ``` -------------------------------- ### Preset: Standard Quality Recorder Settings Source: https://github.com/exyte/chat/blob/main/_autodocs/configuration.md Balanced configuration for audio recording, offering a good compromise between quality and file size. ```swift RecorderSettings( audioFormatID: kAudioFormatMPEG4AAC, sampleRate: 16000, // Balance quality/size numberOfChannels: 1 // Mono ) ``` -------------------------------- ### Chat Theme Initializer Source: https://github.com/exyte/chat/blob/main/_autodocs/api-reference/chattheme.md Initializes a ChatTheme with a comprehensive set of customizable colors for various chat UI elements. Default values are provided, often referencing predefined color assets. ```APIDOC ## Initializer ### Description Initializes a ChatTheme with a comprehensive set of customizable colors for various chat UI elements. Default values are provided, often referencing predefined color assets. ### Parameters #### Initializer 1 - **mainBG** (Color) - Optional - Background color for the main chat interface. - **mainTint** (Color) - Optional - Tint color for main UI elements. - **mainText** (Color) - Optional - Color for main text. - **mainCaptionText** (Color) - Optional - Color for caption text. - **messageMyBG** (Color) - Optional - Background color for messages sent by the current user. - **messageReadStatus** (Color) - Optional - Color for message read status indicators. - **messageMyText** (Color) - Optional - Text color for messages sent by the current user. - **messageMyTimeText** (Color) - Optional - Time text color for messages sent by the current user. - **messageFriendBG** (Color) - Optional - Background color for messages from other users. - **messageFriendText** (Color) - Optional - Text color for messages from other users. - **messageFriendTimeText** (Color) - Optional - Time text color for messages from other users. - **messageSystemBG** (Color) - Optional - Background color for system messages. - **messageSystemText** (Color) - Optional - Text color for system messages. - **messageSystemTimeText** (Color) - Optional - Time text color for system messages. - **inputBG** (Color) - Optional - Background color for the input area. - **inputText** (Color) - Optional - Text color for the input area. - **inputPlaceholderText** (Color) - Optional - Placeholder text color for the input area. - **inputSignatureBG** (Color) - Optional - Background color for the input signature area. - **inputSignatureText** (Color) - Optional - Text color for the input signature area. - **inputSignaturePlaceholderText** (Color) - Optional - Placeholder text color for the input signature area. - **menuBG** (Color) - Optional - Background color for menus. - **menuText** (Color) - Optional - Text color for menu items. - **menuTextDelete** (Color) - Optional - Text color for delete actions in menus. - **statusError** (Color) - Optional - Color for error status indicators. - **statusGray** (Color) - Optional - Color for gray status indicators. - **sendButtonBackground** (Color) - Optional - Background color for the send button. - **recordDot** (Color) - Optional - Color for the record dot indicator. #### Initializer 2 - **copy** (Colors) - Required - An existing Colors object to copy properties from. - **mainBG** (Color) - Required - The main background color to set, overriding the copied value. ```