@ViewBuilder
func makeCustomAttachmentViewType(options: CustomAttachmentViewTypeOptions) -> some View {
let message = options.message
let isGenerating = message.extraData["generating"]?.boolValue == true
StreamingMessageView(
content: message.text,
isGenerating: isGenerating
)
.padding()
}SwiftUI Integration
The AI components work seamlessly with the StreamChatSwiftUI SDK.
Streaming Message View
You can easily integrate the message streaming view into the StreamChatSwiftUI SDK.
One option is to add it as a custom attachment:
Then, you need to create your own message resolver that will tell the SDK to treat the messages with "ai_generated" custom data as custom attachments:
class CustomMessageResolver: MessageTypeResolving {
func hasCustomAttachment(message: ChatMessage) -> Bool {
message.extraData["ai_generated"] == true
}
}Finally, you need to inject this resolver when initializing the SDK:
let utils = Utils(messageTypeResolver: CustomMessageResolver())
let streamChat = StreamChat(chatClient: chatClient, utils: utils)You can also add thinking indicators, for example at the bottom of the message list, using the makeMessageListContainerModifier from the Styles protocol:
class CustomStyles: RegularStyles {
let typingIndicatorHandler: TypingIndicatorHandler
init(typingIndicatorHandler: TypingIndicatorHandler) {
self.typingIndicatorHandler = typingIndicatorHandler
}
func makeMessageListContainerModifier(options: MessageListContainerModifierOptions) -> some ViewModifier {
CustomMessageListContainerModifier(typingIndicatorHandler: typingIndicatorHandler)
}
}Assign it to your ViewFactory's styles property:
class CustomViewFactory: ViewFactory {
@Injected(\.chatClient) public var chatClient
lazy var styles = CustomStyles(typingIndicatorHandler: typingIndicatorHandler)
// ...
}Where the CustomMessageListContainerModifier would look something like this:
struct CustomMessageListContainerModifier: ViewModifier {
@ObservedObject var typingIndicatorHandler: TypingIndicatorHandler
func body(content: Content) -> some View {
content.overlay {
AIAgentOverlayView(typingIndicatorHandler: typingIndicatorHandler)
}
}
}
struct AIAgentOverlayView: View {
@ObservedObject var typingIndicatorHandler: TypingIndicatorHandler
var body: some View {
VStack {
Spacer()
if typingIndicatorHandler.typingIndicatorShown {
HStack {
AITypingIndicatorView(text: typingIndicatorHandler.state)
Spacer()
}
.padding()
.frame(height: 60)
.background(Color(UIColor.secondarySystemBackground))
}
}
}
}The TypingIndicatorHandler can be an observable object that listens to the different events for the thinking state. For a reference implementation, please check our demo app.