This is beta documentation for Stream Chat IOS SDK v5. For the latest stable version, see the latest version (v4) .

Message Reactions

Reactions Overview

The SwiftUI chat SDK provides a default view that's displayed as an overlay of the message. When you long press on a message, the message reactions overlay is shown. By default, it shows a blurred background and a possibility to react to a message or remove a reaction. Additionally, the reactions overlay has a "message actions" slot, which allows you to perform actions on the message. Both the displayed reactions on a message and the reactions overlay can be replaced by your own views.

When reactions are added to a message, a view displaying the added reactions is shown above the message. When this view is tapped or long-pressed, a new overlay view displaying the list of people who reacted to a message is presented. That view can also be swapped with your own implementation.

Customizing the Message Reactions View

The simplest way to customize the message reactions view is to replace its reaction icons. Those are available under the availableReactions property in the Images class, which is part of the Appearance class in the StreamChat object. The availableReactions property is a dictionary, which contains mappings between MessageReactionType and its corresponding ChatMessageReactionAppearanceType, which consists of small and large icon for a reaction. If you change these properties, make sure to inject the updated Images class in the StreamChat object.

let customReactions: [MessageReactionType: ChatMessageReactionAppearance] = [
    .init(rawValue: "custom"): .init(smallIcon: smallIcon, largeIcon: largeIcon)
]
var images = Appearance.Images()
images.availableReactions = customReactions
var appearance = Appearance()
appearance.images = images

let streamChat = StreamChat(chatClient: chatClient, appearance: appearance)

You can also change the tint color of the reactions, by changing reactionCurrentUserColor for the current user's reactions, or reactionOtherUserColor for other users' reactions. Additionally, you can set a background for a selected reaction, with selectedReactionBackgroundColor. These colors are optional, so if you don't want to tint the reactions, but want to use the original icon color, you can just pass nil as a value. Here's an example how to change these values:

var colors = Appearance.ColorPalette()
colors.reactionCurrentUserColor = UIColor.blue
colors.reactionOtherUserColor = UIColor.red
colors.selectedReactionBackgroundColor = UIColor.gray

var appearance = Appearance()
appearance.colorPalette = colors

let streamChat = StreamChat(chatClient: chatClient, appearance: appearance)

By default, the reactions are sorted by their raw value in an alphabetical order. You can change this logic by adding your own implementation of the sortReactions closure in the Utils class.

Here's an example how to achieve this:

let customReactionSort: (MessageReactionType, MessageReactionType) -> Bool = { lhs, rhs in
    // Your custom sorting logic here
    lhs.rawValue < rhs.rawValue
}
let utils = Utils(sortReactions: customReactionSort)
streamChat = StreamChat(chatClient: chatClient, utils: utils)

Changing the Message Reactions View

Alternatively, you can completely swap the ReactionsContainer view with your own implementation. In order to do that, you need to implement the makeMessageReactionView method from the ViewFactory:

public func makeMessageReactionView(
    options: MessageReactionViewOptions
) -> some View {
    CustomReactionsContainer(message: options.message)
}

The MessageReactionViewOptions provides:

  • message – the ChatMessage whose reactions are displayed.
  • onTapGesture – callback when the reactions view is tapped.
  • onLongPressGesture – callback when the reactions view is long-pressed.

Customizing the Reactions Overlay View

The reactions overlay view (shown on long press of a message) also provides access to the message actions. To replace or extend the default message actions, configure them via messageListConfig:

let utils = Utils(
    messageListConfig: MessageListConfig(
        supportedMessageActions: { options in
            MessageAction.defaultActions(for: options)
        }
    )
)
let streamChat = StreamChat(chatClient: chatClient, utils: utils)

Alternatively, you can swap the whole MessageActionsView with your own implementation, by implementing the makeMessageActionsView method in the ViewFactory:

public func makeMessageActionsView(
    options: MessageActionsViewOptions
) -> some View {
    let messageActions = InjectedValues[\.utils].messageListConfig.supportedMessageActions(
        SupportedMessageActionsOptions(
            message: options.message,
            channel: options.channel,
            onFinish: options.onFinish,
            onError: options.onError
        )
    )
    return MessageActionsView(messageActions: messageActions)
}

The MessageActionsViewOptions provides:

  • message – the message for which actions are shown.
  • channel – the channel where the message lives.
  • onFinish – callback when an action finishes successfully.
  • onError – callback when an action fails.

As mentioned at the beginning, when the reactions are tapped or long-pressed, a view with the list of users who reacted to the message is displayed. In order to change this view with your own implementation, you will need to implement the makeReactionsDetailView in the ViewFactory.

func makeReactionsDetailView(options: ReactionsDetailViewOptions) -> some View {
    ReactionsDetailView(message: options.message)
}

The ReactionsDetailViewOptions provides:

  • message – the message whose reaction authors to display.

The background of the reactions overlay is a blurred snapshot of the current channel view. You can customize it by implementing the makeReactionsBackgroundView method in the ViewFactory. For example, you can remove the blur, change the opacity, or even return an EmptyView. Here's an example implementation:

func makeReactionsBackgroundView(
    options: ReactionsBackgroundOptions
) -> some View {
    Image(uiImage: options.currentSnapshot)
        .overlay(Color.black.opacity(options.popInAnimationInProgress ? 0 : 0.1))
        .blur(radius: options.popInAnimationInProgress ? 0 : 4)
}

The ReactionsBackgroundOptions provides:

  • currentSnapshot – the current snapshot of the whole chat channel view.
  • popInAnimationInProgress – whether the pop-in animation is currently in progress; use this to transition between animation states.

You can customize the snapshot generation logic. In order to do this, you will need to provide your implementation of the SnapshotCreator protocol, which has one method func makeSnapshot(for view: AnyView) -> UIImage. The view parameter is the SwiftUI view which invokes the reactions overlay presentation (the ChatChannelView), while the generated UIImage is used in the makeReactionsBackgroundView method above.

In case you want to implement your own implementation of this protocol, you will need to inject it in our Utils class.

let snapshotCreator = CustomSnapshotCreator()
let utils = Utils(snapshotCreator: snapshotCreator)
let streamChat = StreamChat(chatClient: chatClient, utils: utils)

Finally, you can swap the whole ReactionsOverlayView with your own implementation. In order to do this, you need to implement the makeReactionsOverlayView method in the ViewFactory. The current snapshot of the message list is provided to you, in case you want to blur it or apply any other effects.

public func makeReactionsOverlayView(
    options: ReactionsOverlayViewOptions
) -> some View {
    ReactionsOverlayView(
        factory: self,
        channel: options.channel,
        currentSnapshot: options.currentSnapshot,
        messageDisplayInfo: options.messageDisplayInfo,
        onBackgroundTap: options.onBackgroundTap,
        onActionExecuted: options.onActionExecuted
    )
}

The ReactionsOverlayViewOptions provides:

  • channel – the channel where the message lives.
  • currentSnapshot – the current snapshot of the message list.
  • messageDisplayInfo – display information about the message (position, size, etc.).
  • onBackgroundTap – callback when the background is tapped to dismiss.
  • onActionExecuted – callback when a message action is executed.