# Message Threads

## Threads Overview

The SwiftUI SDK supports replying to messages in threads. The default "reply in thread" action opens a new screen, where the message and its replies are shown, in a user interface similar to the message list. Additionally, the composer in this view has a possibility to also send the message in the main channel or group.

## View Customizations

### Changing the Message Replies View

The `MessageRepliesView` is shown below a message, when there are thread replies to it. Tapping on it opens up the thread view.

In order to customize the `MessageRepliesView`, you will need to implement the `makeMessageRepliesView` in the `ViewFactory`. Here's an example how to do that:

```swift
func makeMessageRepliesView(options: MessageRepliesViewOptions) -> some View {
    CustomMessageRepliesView(
        channel: options.channel,
        message: options.message,
        replyCount: options.replyCount
    )
}
```

The `MessageRepliesViewOptions` provides:

- `channel` – the channel the message belongs to.
- `message` – the parent message.
- `replyCount` – the number of thread replies.
- `usesInvertedStyle` – whether the view uses an inverted (outgoing message) style.

Note that this method swaps the whole view, including the navigation link. This gives you a chance to present the thread in different ways (for example push navigation - like the default behaviour, sheet, full screen cover, etc).

### Changing the Thread Header

The default header shows a static text, implying that you are in a thread. You can easily swap this header with your own implementation. To do this, you need to implement the `makeMessageThreadHeaderViewModifier` method in the `ViewFactory`. Here's how the default implementation looks like:

```swift
class CustomViewFactory: ViewFactory {
    @Injected(\.chatClient) public var chatClient
    public var styles = RegularStyles()

    func makeMessageThreadHeaderViewModifier(
        options: MessageThreadHeaderViewModifierOptions
    ) -> some MessageThreadHeaderViewModifier {
        DefaultMessageThreadHeaderModifier()
    }
}

/// The default message thread header.
public struct DefaultMessageThreadHeader: ToolbarContent {
    @Injected(\.fonts) private var fonts
    @Injected(\.colors) private var colors

    public var body: some ToolbarContent {
        ToolbarItem(placement: .principal) {
            VStack {
                Text(L10n.Message.Actions.threadReply)
                    .font(fonts.bodyBold)
                Text(L10n.Message.Threads.subtitle)
                    .font(fonts.footnote)
                    .foregroundColor(Color(colors.navigationBarSubtitle))
            }
        }
    }
}

/// The default message thread header modifier.
public struct DefaultMessageThreadHeaderModifier: MessageThreadHeaderViewModifier {

    public func body(content: Content) -> some View {
        content.toolbarThemed {
            DefaultMessageThreadHeader()
        }
    }
}
```

### Swapping the SendInChannelView

The default `SendInChannelView` has a checkmark and a text describing the view's action. If needed, you can replace this with your own implementation. To do this, implement the `makeSendInChannelView` in the `ViewFactory`:

```swift
class CustomViewFactory: ViewFactory {
    @Injected(\.chatClient) public var chatClient
    public var styles = RegularStyles()

    func makeSendInChannelView(
        options: SendInChannelViewOptions
    ) -> some View {
        CustomSendInChannelView(
            sendInChannel: options.showReplyInChannel
        )
    }
}
```

The `SendInChannelViewOptions` provides:

- `showReplyInChannel` – binding to whether the "send in channel" checkbox is ticked.

### Swapping the Message Threads View

If you don't prefer the message threads to look similarly to the message list, you can completely swap the message thread destination. In order to do this, you would need to implement the `makeMessageThreadDestination` method in the `ViewFactory`. In this method, you will need to return a closure that makes the thread destination. In the closure, the chat channel and message are provided, to identify the correct message thread.

```swift
class CustomViewFactory: ViewFactory {
    @Injected(\.chatClient) public var chatClient
    public var styles = RegularStyles()

    public func makeMessageThreadDestination(
        options: MessageThreadDestinationOptions
    ) -> @MainActor (ChatChannel, ChatMessage) -> ChatChannelView<Self> {
        { [unowned self] channel, message in
            let channelController = chatClient.channelController(
                for: channel.cid,
                messageOrdering: .topToBottom
            )
            let messageController = chatClient.messageController(
                cid: channel.cid,
                messageId: message.id
            )
            return CustomChatChannelView(
                viewFactory: self,
                channelController: channelController,
                messageController: messageController
            )
        }
    }
}
```

### Changing the Thread Replies Divider

When all replies in a thread have been loaded, the SDK shows a divider between the parent message and the first reply. This divider displays the total reply count (e.g. "5 replies") and uses the same design as the unread messages divider.

You can customize this view by implementing `makeThreadRepliesDividerView` in the `ViewFactory`:

```swift
func makeThreadRepliesDividerView(options: ThreadRepliesDividerViewOptions) -> some View {
    CustomThreadRepliesDivider(replyCount: options.replyCount)
}
```

The `ThreadRepliesDividerViewOptions` provides:

- `replyCount` – the number of replies in the thread.

The divider is only shown once all replies have been loaded (i.e. after pagination completes). To hide it entirely, return an `EmptyView`.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/ios/swiftui/message-components/message-threads/](https://getstream.io/chat/docs/sdk/ios/swiftui/message-components/message-threads/).