# Message Moderation

Stream Chat can moderate messages before they are delivered (bounce) and lets users report messages to moderators (flag). This page shows how to customize the UIKit components for moderated messages. For how moderated messages are represented in the SDK, see the [Moderation](https://getstream.io/chat/docs/sdk/ios/v4/guides/moderation/) guide.

## Bounced Messages

A bounced message is displayed with an error indicator next to it. When the user long presses the message or taps the indicator, `ChatMessageListVC` calls `showActions(forDebouncedMessage:)`, which presents an alert with the **Send Anyway**, **Edit Message** and **Delete Message** actions.

To change the actions, or to replace the alert with your own UI, subclass `ChatMessageListVC` and override `messageActions(forDebouncedMessage:)` or `showActions(forDebouncedMessage:)`:

```swift
class CustomChatMessageListVC: ChatMessageListVC {
    // Change the list of actions, e.g. do not allow sending a bounced message anyway.
    override func messageActions(forDebouncedMessage message: ChatMessage) -> [ChatMessageActionItem] {
        super.messageActions(forDebouncedMessage: message)
            .filter { !($0 is ResendActionItem) }
    }

    // Or replace the default alert completely.
    override func showActions(forDebouncedMessage message: ChatMessage) {
        let actions = messageActions(forDebouncedMessage: message)
        let alert = UIAlertController(
            title: "Message not sent",
            message: "This message goes against our community guidelines.",
            preferredStyle: .alert
        )
        actions.forEach { item in
            alert.addAction(UIAlertAction(
                title: item.title,
                style: item.isDestructive ? .destructive : .default,
                handler: { _ in item.action(item) }
            ))
        }
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
        present(alert, animated: true)
    }
}

Components.default.messageListVC = CustomChatMessageListVC.self
```

The error indicator is a `ChatMessageErrorIndicator`. You can change its icon with `Appearance.default.images.messageListErrorIndicator`, or provide your own subclass:

```swift
class CustomErrorIndicator: ChatMessageErrorIndicator {
    override func setUpAppearance() {
        super.setUpAppearance()
        setImage(UIImage(systemName: "exclamationmark.shield.fill"), for: .normal)
        tintColor = .systemOrange
    }
}

Components.default.messageErrorIndicator = CustomErrorIndicator.self
```

>
> **Note:** The error indicator is also used for messages that failed to be sent for other reasons, for example because of a network error.
>

## Flag Action

The flag action is created by `ChatMessageActionsVC.flagActionItem()`. It shows a confirmation alert and then calls `flag()` on the message controller. To change the title, the behaviour, or to flag the message with a reason, subclass `ChatMessageActionsVC` and override `flagActionItem()`:

```swift
class CustomChatMessageActionsVC: ChatMessageActionsVC {
    override func flagActionItem() -> ChatMessageActionItem {
        FlagActionItem(
            title: "Report",
            action: { [weak self] _ in
                guard let self else { return }
                self.alertsRouter.showMessageFlagConfirmationAlert { confirmed in
                    guard confirmed else { return }
                    self.messageController.flag(reason: "inappropriate") { _ in
                        self.delegate?.chatMessageActionsVCDidFinish(self)
                    }
                }
            },
            appearance: appearance
        )
    }
}

Components.default.messageActionsVC = CustomChatMessageActionsVC.self
```

The icon of the default flag action can be changed with `Appearance.default.images.messageActionFlag`.

To hide the flag action, override `messageActions` and filter it out:

```swift
class CustomChatMessageActionsVC: ChatMessageActionsVC {
    override var messageActions: [ChatMessageActionItem] {
        super.messageActions.filter { !($0 is FlagActionItem) }
    }
}
```

To customize the confirmation alert, subclass `AlertsRouter` and override `showMessageFlagConfirmationAlert(confirmed:)`:

```swift
class CustomAlertsRouter: AlertsRouter {
    override func showMessageFlagConfirmationAlert(confirmed: @escaping (Bool) -> Void) {
        let alert = UIAlertController(
            title: "Report message?",
            message: "Our moderators will review this message.",
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in confirmed(false) })
        alert.addAction(UIAlertAction(title: "Report", style: .destructive) { _ in confirmed(true) })
        rootViewController.present(alert, animated: true)
    }
}

Components.default.alertsRouter = CustomAlertsRouter.self
```

For more details about replacing components, see [Custom Components](https://getstream.io/chat/docs/sdk/ios/v4/uikit/custom-components/) and [Customize Message Actions](https://getstream.io/chat/docs/sdk/ios/v4/uikit/guides/customize-message-actions/).

## Changing the Texts

If you only need to change the texts, override the following keys in your app's `Localizable.strings`. By default, the SDK reads strings only from its own bundle, so you also need to set `localizationProvider`. The following example reads from your app's bundle first and falls back to the SDK strings for keys you didn't override:

```swift
let sdkLocalizationProvider = Appearance.default.localizationProvider
Appearance.default.localizationProvider = { key, table in
    let value = Bundle.main.localizedString(forKey: key, value: key, table: table)
    return value == key ? sdkLocalizationProvider(key, table) : value
}
```

The keys used by the moderation components are:

| Key                                         | Default value                      |
| ------------------------------------------- | ---------------------------------- |
| `message.moderation.title`                  | Are you sure?                      |
| `message.moderation.message`                | Consider how your comment might... |
| `message.moderation.resend`                 | Send Anyway                        |
| `message.moderation.edit`                   | Edit Message                       |
| `message.moderation.delete`                 | Delete Message                     |
| `message.actions.flag`                      | Flag Message                       |
| `message.actions.flag.confirmation-title`   | Flag Message                       |
| `message.actions.flag.confirmation-message` | Do you want to send a copy of...   |

See the [localization guide](https://getstream.io/chat/docs/sdk/ios/v4/uikit/localization/) for more details.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/ios/v4/uikit/guides/message-moderation/](https://getstream.io/chat/docs/sdk/ios/v4/uikit/guides/message-moderation/).