CI Status Built and maintained by Quintschaf MIT License
A beautiful data-driven chat view for iOS and macOS.
| QSChatView Screenshot 1 | QSChatView Screenshot 2 |
https://github.com/quintschaf/qschatview.git
- Fully interactive, data-driven chat view
- Various customization options
- Supports light and dark mode
- Works on iOS, macOS and Catalyst
A very basic example with a single chat participant and message.
import SwiftUI import QSChatView struct MyChatView: View { // Instantiate a `ChatController` with some initial messages @StateObject var controller = ChatController(messages: [ ChatMessage(from: .me, content: .text("Hello world")), ]) var body: some View { // Render the chat view with our controller QSChatView(controller).padding() } }
A more advanced example with a modified config, multiple participants and a custom chat backend.
import SwiftUI import QSChatView class MyChatData: ObservableObject { @Published var controller: ChatController @Published var chatPartner: ChatParticipant init() { // Build chat config. // You can also use `ChatConfig.default` here. let config = ChatConfigBuilder() .showTextField() .showTimestamps(false) .withScrollingBehavior(.adaptive) .build() // In this example we specify a fixed set of initial messages. // // In a real-world application, you would probably load messages // from a server or database, or restore them from a local cache. let messages: [ChatMessage] = [ ChatMessage(from: .me, content: .text("Hey John, are you there?")) ] // Create a chat controller with our messages and config self.controller = ChatController(messages: messages, config: config) // Create a chat partner self.chatPartner = ChatParticipantBuilder().withName("John Doe").build() } // This is meant as an example on how to interact with the chat controller. // It also serves as an introduction to the `ChatMessagePromise` API. func startMessageTimer() { // Run this block every 5 seconds Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { _ in // Add a temporary message with a typing indicator. // In this case, John Doe is shown as typing. let messagePromise = self.controller.sendPromise(from: self.chatPartner) // Wait 2 seconds before resolving the promise DispatchQueue.main.asyncAfter(deadline: .now() + 2) { // Resolve the promise with the actual message messagePromise.fulfill(withContent: .text("Hello from John!")) // In the case that John stops typing without sending // a message, we can use `messagePromise.reject()` instead. } } } } struct MyChatView: View { @StateObject var chat = MyChatData() // Called when the user sends a message through the chat view func handleMessageSent(_ message: String) { /* * Messages are added to the controller before this callback is called. * * We can do additional processing here, such as sending the message * to a server, logging it, updating statistics, and so on. * * In this example, we just print the message to the console. */ print("User sent message: \(message)") } var body: some View { // Render the chat view with our controller and callback QSChatView(chat.controller, onMessageSent: handleMessageSent) .padding() .onAppear { chat.startMessageTimer() } } }