Connect with us


Chat – A SwiftUI Chat UI framework

Chat – A SwiftUI Chat UI framework with fully customizable message cells and a built-in media picker.


  • Displays your messages with pagination and allows you to create and “send” new messages (sending means calling a closure since user will be the one providing actual API calls)
  • Allows you to pass a custom view builder for messages and input views
  • Has a built-in photo and video library/camera picker for multiple media asset selection
  • Can display a fullscreen menu on long press a message cell (automatically shows scroll for big messages)
  • Supports “reply to message” via message menu. Remove and edit are coming soon
  • Supports voice recording, video/photo and text. More content types are coming soon


Create a chat view like this:

@State var messages: [Message] = []

var body: some View {
    ChatView(messages: messages) { draft in
        yourViewModel.send(draft: draft)

messages – list of messages to display
didSendMessage – a closure which is called when the user presses the send button

Message is a type that Chat uses for the internal implementation. In the code above it expects the user to provide a list of Message structs, and it returns a DraftMessage in the didSendMessage closure. You can map it both ways to your own Message model that your API expects.


You may customize message cells like this:

ChatView(messages: viewModel.messages) { draft in
    viewModel.send(draft: draft)
} messageBuilder: { message, positionInGroup, showAttachmentClosure in
    VStack {
        if !message.attachments.isEmpty {
            ForEach(message.attachments, id: \.id) { at in
                AsyncImage(url: at.thumbnail)

messageBuilder‘s parameters:

  • message – the message containing user info, attachments, etc.
  • positionInGroup – the position of the message in its continuous collection of messages from the same user
  • showAttachmentClosure – you can pass an attachment to this closure to use ChatView’s fullscreen media viewer

You may customize the input view (a text field with buttons at the bottom) like this:

ChatView(messages: viewModel.messages) { draft in
    viewModel.send(draft: draft)
} inputViewBuilder: { textBinding, attachments, state, style, actionClosure, dismissKeyboardClosure in
    Group {
        switch style {
        case .message: // input view on chat screen
            VStack {
                HStack {
                    Button("Send") { actionClosure(.send) }
                    Button("Attach") { actionClosure(.photo) }
                TextField("Write your message", text: textBinding)
        case .signature: // input view on photo selection screen
            VStack {
                HStack {
                    Button("Send") { actionClosure(.send) }
                TextField("Compose a signature for photo", text: textBinding)

inputViewBuilder‘s parameters:

  • textBinding to bind your own TextField
  • attachments is a struct containing photos, videos, recordings and a message you are replying to
  • state – the state of the input view that is controled by the library automatically if possible or through your calls of actionClosure
  • style – .message or .signature (the chat screen or the photo selection screen)
  • actionClosure is called on taps on your custom buttons. For example, call actionClosure(.send) if you want to send your message, then the library will reset the text and attachments and call the didSendMessage sending closure
  • dismissKeyboardClosure – call this to dismiss keyboard

Supported content types

This library allows to send the following content in messages in any combination:

  • Text with/without markdown
  • Photo/video
  • Audio recording

Coming soon:

  • User’s location
  • Documents


If you are not using your own messageBuilder:
avatarSize – the default avatar is a circle, you can specify its diameter here
tapAvatarClosure – closure to call on avatar tap
messageUseMarkdown – whether the default message cell uses markdown
mediaPickerSelectionParameters – a struct holding MediaPicker selection parameters (mediaType, selectionStyle, etc.) assetsPickerLimit – the maximum media count that the user can select in the media picker
enableLoadMore(offset: Int, handler: @escaping ChatPaginationClosure) – when user scrolls to offset-th message from the end, call the handler function, so the user can load more messages
chatNavigation(title: String, status: String? = nil, cover: URL? = nil) – pass the info for the Chat’s navigation bar


There are 2 example projects:

  • One has a simple bot posting random text/media messages every 2 seconds. It has no back end and no local storage. Every new start is clean and fresh.
  • Another has an integration with Firestore data base. It has all the necessary back end support, including storing media and audio messages, unread messages counters, etc. You’ll have to create your own Firestore app and DB. Also replace GoogleService-Info with your own. After that you can test on multiple sims/devices.

Create your firestore app Create firesote database (for light weight text data) Create cloud firestore database (for images and voice recordings)


To try out the Chat examples:

  • Clone the repo git clone
  • Open terminal and run cd <ChatRepo>/Example
  • Wait for SPM to finish downloading packages
  • Run it!


Swift Package Manager

dependencies: [
    .package(url: "")


pod 'ExyteChat'


github "Exyte/Chat"


  • iOS 16+
  • Xcode 14+
Chat on GitHub:
Platform: iOS
⭐️: 334