# Messaging Flow > **Related spec:** [spec/api.md](../../spec/api.md) | [spec/client/chat-view.md](../../spec/client/chat-view.md) | [spec/client/compose.md](../../spec/client/compose.md) ## Overview Complete message lifecycle in SimpleX Chat iOS: composing, sending, receiving, editing, deleting, reacting to, replying to, and forwarding messages. All messages are end-to-end encrypted via the SMP protocol. The Haskell core handles encryption, routing, and persistence; the Swift UI layer drives composition and display. ## Prerequisites - User profile created and chat engine running (`startChat()` completed) - At least one established contact or group conversation - `ChatModel.shared` populated with chat list data ## Step-by-Step Processes ### 1. Send Text Message 1. User navigates to a conversation (direct or group) via `ChatListView` -> `ChatView`. 2. User types text into `ComposeView`'s `SendMessageView` text editor. 3. Link previews are detected and fetched asynchronously (`ComposeLinkView`). 4. User taps the send button. 5. `ComposeView` builds a `ComposedMessage`: ```swift ComposedMessage( fileSource: nil, quotedItemId: nil, msgContent: .text("Hello"), mentions: [:] ) ``` 6. Calls `apiSendMessages(type:id:scope:live:ttl:composedMessages:sendAsGroup:)` (where `sendAsGroup` defaults to `false`; set to `true` when a channel owner sends as the channel identity). 7. Internally dispatches `ChatCommand.apiSendMessages(...)` to the Haskell core. 8. Core encrypts, queues via SMP, and returns `ChatResponse1.newChatItems(user, aChatItems)`. 9. `processSendMessageCmd` extracts `[ChatItem]` from response. 10. For direct chats, a background task tracks delivery via `chatModel.messageDelivery`. 11. `ChatModel` updates, UI refreshes to show the new message. ### 2. Send Media (Image/Video/File) 1. User taps the attachment button in `ComposeView`. 2. **Image**: Picked via `PhotosPicker` or camera. Compressed to <=255KB. Sent inline with `.image(text, base64Image)` content type. 3. **Video**: Picked from library. Thumbnail generated. Video file sent via XFTP for files >255KB. Content type: `.video(text, thumbnail, duration)`. 4. **File**: Picked via document picker. If <=255KB, sent inline. If >255KB, uploaded via XFTP (up to 1GB). Content type: `.file(text)`. 5. `ComposedMessage` includes `fileSource: CryptoFile(filePath:)`. 6. `apiSendMessages(...)` called with the composed message array. 7. Core handles XFTP upload for large files (chunked, encrypted upload to XFTP servers). 8. Recipient receives file reference and can download. ### 3. Receive Message 1. `ChatReceiver.shared` runs `receiveMsgLoop()` continuously calling `chatRecvMsg()`. 2. Core delivers events via `APIResult`. 3. On `ChatEvent.newChatItems(user, chatItems)`: - `processReceivedMsg` is called. - For the active user, `ChatModel` is updated with new items. - If the chat is currently open, `ItemsModel` appends to `reversedChatItems`. - `NtfManager` posts a local notification if the app is in the background. 4. Small files/images attached to incoming messages are auto-received if within size thresholds. ### 4. Edit Message 1. User long-presses a sent message -> selects "Edit" from context menu. 2. `ComposeView` enters edit mode with the original text pre-filled. 3. User modifies text and taps send. 4. Calls `apiUpdateChatItem(type:id:scope:itemId:updatedMessage:live:)`. 5. Dispatches `ChatCommand.apiUpdateChatItem(...)`. 6. Core returns `ChatResponse1.chatItemUpdated(user, aChatItem)` or `.chatItemNotChanged(user, aChatItem)`. 7. `ChatModel` updates the item in place. Edit timestamp is shown in the UI. ### 5. Delete Message 1. User long-presses a message -> selects "Delete". 2. Presented with options: - **Delete for me** (`CIDeleteMode.cidmInternal`) -- removes locally only. - **Delete for everyone** (`CIDeleteMode.cidmBroadcast`) -- sends deletion to recipient(s). 3. Calls `apiDeleteChatItems(type:id:scope:itemIds:mode:)`. 4. Dispatches `ChatCommand.apiDeleteChatItem(type:id:scope:itemIds:mode:)`. 5. Core returns `ChatResponse1.chatItemsDeleted(user, items, _)` containing `[ChatItemDeletion]`. 6. For group messages from other members, admin/owner can call `apiDeleteMemberChatItems(groupId:itemIds:)`. 7. `ChatModel` removes or replaces items with "deleted" placeholders. ### 6. React to Message 1. User long-presses a message -> selects "React" -> picks an emoji. 2. Calls `apiChatItemReaction(type:id:scope:itemId:add:reaction:)`. 3. `reaction` is `MsgReaction` (e.g., `.emoji(.heart)`). 4. `add: true` to add, `add: false` to remove. 5. Core returns `ChatResponse1.chatItemReaction(user, _, reaction)`. 6. The reaction is displayed below the message bubble. ### 7. Reply to Message 1. User long-presses a message -> selects "Reply". 2. `ComposeView` enters reply mode, showing quoted message in `ContextItemView`. 3. User types reply text and taps send. 4. `ComposedMessage` is created with `quotedItemId: originalItem.id`. 5. `apiSendMessages(...)` sends with the quote reference. 6. Recipient sees the reply with the quoted context rendered above. ### 8. Forward Message 1. User long-presses a message -> selects "Forward". 2. `ChatItemForwardingView` is presented for destination chat selection. 3. `apiPlanForwardChatItems(type:id:scope:itemIds:)` validates what can be forwarded, returns `([Int64], ForwardConfirmation?)`. 4. User confirms and selects destination chat. 5. Calls `apiForwardChatItems(toChatType:toChatId:toScope:fromChatType:fromChatId:fromScope:itemIds:ttl:sendAsGroup:)` (where `sendAsGroup` defaults to `false`). 6. Core returns `ChatResponse1.newChatItems(...)` with the forwarded items in the destination chat. ### 9. Voice Message 1. User taps and holds the microphone button in `ComposeView`. 2. `AudioRecPlay` starts recording to a temporary file. 3. On release, recording stops. Duration is calculated (max 5 minutes / 300 seconds). 4. `ComposedMessage` created with: - `fileSource: CryptoFile` pointing to the audio file - `msgContent: .voice(text: "", duration: seconds)` 5. `apiSendMessages(...)` sends the voice message. 6. Voice messages <=510KB sent inline; larger via XFTP. 7. Recipient sees `CIVoiceView` with waveform and playback controls. ### 10. Delivery Tracking 1. On send, message status starts as `CIStatus.sndNew`. 2. After SMP delivery: `CIStatus.sndSent(sndProgress)`. 3. When delivered to recipient's agent: status updates to delivered. 4. If delivery receipts are enabled by both parties, read status is reported. 5. Failed delivery results in `CIStatus.sndError*` or `CIStatus.sndWarning*`. 6. Status is displayed via `CIMetaView` (checkmarks/indicators). ## Data Structures | Type | Location | Description | |------|----------|-------------| | `ComposedMessage` | `SimpleXChat/APITypes.swift` | Outgoing message: fileSource, quotedItemId, msgContent, mentions | | `MsgContent` | `SimpleXChat/ChatTypes.swift` | Enum: `.text`, `.link`, `.image`, `.video`, `.voice`, `.file` | | `CIContent` | `SimpleXChat/ChatTypes.swift` | Chat item content wrapper with sent/received variants | | `CIStatus` | `SimpleXChat/ChatTypes.swift` | Delivery status: sndNew, sndSent, sndError, rcvNew, rcvRead | | `CIDirection` | `SimpleXChat/ChatTypes.swift` | `.directSnd`, `.directRcv`, `.groupSnd`, `.groupRcv(groupMember)`, `.channelRcv` | | `ChatItem` | `SimpleXChat/ChatTypes.swift` | Full message model: content, meta, status, direction, quotedItem | | `ChatItemDeletion` | `SimpleXChat/ChatTypes.swift` | Deleted item info with old/new item pairs | | `CIDeleteMode` | `SimpleXChat/ChatTypes.swift` | `.cidmInternal` (local) or `.cidmBroadcast` (for everyone) | | `MsgReaction` | `SimpleXChat/ChatTypes.swift` | Reaction type (emoji-based) | | `UpdatedMessage` | `SimpleXChat/APITypes.swift` | Edited message content for update API | ## Error Cases | Error | Cause | Handling | |-------|-------|----------| | `ChatError.errorAgent(.SMP(_, .AUTH))` | Recipient queue issue | Show "Connection link removed" alert | | `ChatError.errorAgent(.BROKER(_, .TIMEOUT))` | Server timeout | Retryable: show retry dialog via `chatApiSendCmdWithRetry` | | `ChatError.errorAgent(.BROKER(_, .NETWORK))` | Network failure | Retryable: show retry dialog | | Send message error | Core processing failure | `sendMessageErrorAlert` shown to user | | `chatItemNotChanged` | Edit with identical content | No error, item returned unchanged | | File too large (>1GB) | XFTP limit exceeded | Prevented in UI file picker | | `fileNotApproved` | Unknown XFTP relay servers | Show "Unknown servers!" alert with approve option | ## Key Files | File | Purpose | |------|---------| | `Shared/Views/Chat/ComposeMessage/ComposeView.swift` | Message composition UI and send logic | | `Shared/Views/Chat/ComposeMessage/SendMessageView.swift` | Text input and send button | | `Shared/Views/Chat/ComposeMessage/ContextItemView.swift` | Reply/edit context display | | `Shared/Views/Chat/ChatItemView.swift` | Per-message rendering dispatcher | | `Shared/Views/Chat/ChatItem/MsgContentView.swift` | Text message content with markdown | | `Shared/Views/Chat/ChatItem/CIMetaView.swift` | Delivery status indicators | | `Shared/Views/Chat/ChatItemForwardingView.swift` | Forward destination picker | | `Shared/Views/Chat/ChatItemInfoView.swift` | Message info (delivery details, timestamps) | | `Shared/Model/SimpleXAPI.swift` | API functions: `apiSendMessages`, `apiUpdateChatItem`, `apiDeleteChatItems`, `apiChatItemReaction`, `apiForwardChatItems` | | `SimpleXChat/APITypes.swift` | `ComposedMessage`, `ChatCommand` enum, response types | | `SimpleXChat/ChatTypes.swift` | `MsgContent`, `CIContent`, `CIStatus`, `CIDirection`, `ChatItem` | | `Shared/Model/AudioRecPlay.swift` | Voice message recording/playback engine | ## Related Specifications - `apps/ios/product/views/chat.md` -- Chat view UI specification - `apps/ios/product/README.md` -- Product overview and capability map