Swift Markdown Kit · DOCS
Streaming & Chat
Streaming & Chat
Streaming
Drive the view controller directly from an LLM token stream:
renderer.startStreaming()
renderer.appendChunk("Streaming **Markdown")
renderer.appendChunk("** with math $x^2$", isFinal: true)
The renderer coalesces chunks via requestAnimationFrame and runs an
incremental scanner that caches HTML up to the last safe block boundary, so
total render cost stays linear in the size of the final answer rather than
re-parsing everything on each update.
You can also bind a growing string to MarkdownRenderView: when the new value
is a prefix extension of the old one, the view appends only the delta.
Chat mode
A whole conversation reuses the same rendering core through one WKWebView —
not one WebView per message. During streaming only the current message updates.
let messages = [
MarkdownChatMessage(id: "u1", role: .user, markdown: "Explain streaming."),
MarkdownChatMessage(id: "a1", role: .assistant, markdown: "", status: .streaming)
]
let options = MarkdownChatRenderOptions(
streamingPresentation: .smooth,
messageActions: MarkdownChatMessageActionConfiguration(isEnabled: true)
)
MarkdownChatRenderView(messages: messages, options: options) { event in
if case .messageAction(let action) = event {
print(action.messageID, action.action)
}
}
.smooth queues uneven network chunks for frame-by-frame display; use
.immediate to show raw chunks as they arrive. For a native scroll-to-bottom
control, hold a MarkdownChatRenderProxy and call scrollToBottom(animated:).
UIKit can drive the chat renderer directly:
let chat = MarkdownChatRenderViewController()
chat.setMessages(messages)
chat.appendChunk(messageID: "a1", chunk: "Streaming **Markdown")
chat.appendChunk(messageID: "a1", chunk: "** with math $x^2$", isFinal: true)