A high-performance Markdown rendering solution for iOS, featuring STXMarkdownView — a UITextView-based component optimized for chat-style UI with streaming support.
MarkdownView/
├── STXMarkdownView/ # Swift Package - Core Markdown rendering library
│ ├── Sources/
│ │ └── STXMarkdownView/
│ │ ├── Core/ # Parser, Renderer, MarkdownView
│ │ ├── Views/ # CodeBlock, Table, Quote, Image views
│ │ ├── Theme/ # Customizable theming system
│ │ ├── Image/ # Image loading with caching
│ │ └── Utils/ # Logging, helpers
│ └── Tests/
├── MarkdownDemo/ # iOS demo application
├── MarkdownDemoSnapshotTests/ # Snapshot tests
└── MarkdownDemo.xcodeproj # Xcode project
| Feature | Description |
|---|---|
| Rich Attachments | Tables, code blocks, images, block quotes, horizontal rules as embedded views |
| Streaming Mode | Throttled rendering for real-time chat/AI assistant UI |
| Syntax Highlighting | Optional Highlightr integration for code blocks |
| Adaptive Tables | Auto-switch between compact and scrollable layout |
| Nested Block Quotes | Recursive rendering with full markdown support inside quotes |
| Task Lists | Checkbox-style task lists (- [ ] / - [x]) |
| Dark Mode | Full dark mode support with theme-aware rendering |
| Image Caching | Memory + disk cache with async loading and downsampling |
| Theme System | Fully customizable colors, fonts, and spacing |
| Performance | Attachment reuse pool, incremental rendering, minimal re-layouts |
- iOS 15.0+
- Swift 5.9+
- Xcode 15.0+
Swift Package Manager
In Xcode: File → Add Package Dependencies → Enter the repository URL:
https://github.com/SteinX/MarkdownView
Or add to your Package.swift:
dependencies: [
.package(url: "https://github.com/SteinX/MarkdownView", from: "1.0.0")
]
// Then add to target dependencies:
.target(
name: "YourApp",
dependencies: [
.product(name: "STXMarkdownView", package: "MarkdownView")
]
)CocoaPods
Add to your Podfile:
pod 'STXMarkdownView', :git => 'https://github.com/SteinX/MarkdownView.git'
# Or specify a tag/branch
pod 'STXMarkdownView', :git => 'https://github.com/SteinX/MarkdownView.git', :tag => '1.0.0'Then run:
pod installNote: CocoaPods distribution uses a pre-built XCFramework (vendored framework).
import STXMarkdownView
let markdownView = MarkdownView()
markdownView.markdown = """
# Hello Markdown
This is **bold** and `inline code`.
```swift
print("Hello, World!")
```
"""
view.addSubview(markdownView)Important: When using inside
UITableViewCellor any width-constrained container, always setpreferredMaxLayoutWidthbefore settingmarkdown. This ensures proper layout calculation.
// In UITableViewCell
override func layoutSubviews() {
super.layoutSubviews()
markdownView.preferredMaxLayoutWidth = contentView.bounds.width - padding
markdownView.markdown = text
}Perfect for AI chat applications with real-time text generation:
// Enable streaming mode for throttled rendering
markdownView.isStreaming = true
markdownView.throttleInterval = 0.1 // 100ms throttle
// Update content incrementally
markdownView.markdown = partialText
// When streaming ends, trigger final render
markdownView.isStreaming = falsevar theme = MarkdownTheme.default
theme = MarkdownTheme(
baseFont: .systemFont(ofSize: 16),
colors: theme.colors,
headings: theme.headings,
code: theme.code,
quote: theme.quote,
lists: theme.lists,
tables: theme.tables,
images: theme.images
)
markdownView.theme = theme// Enable verbose logging (view in Console.app, filter: subsystem:com.stx.markdown)
MarkdownView.logLevel = .verbose
// Other levels: .info, .error, .off- Open
MarkdownDemo.xcodeprojin Xcode - Select a simulator or device
- Build and run (Cmd+R)
- Tap "Start Stream" to see streaming mode in action
MarkdownView (UIView)
└── MarkdownTextView (UITextView)
├── NSAttributedString (text content)
└── Attachments (embedded views)
├── CodeBlockView
├── MarkdownTableView
├── QuoteView
├── MarkdownImageView
└── HorizontalRuleView
Key Components:
- MarkdownParser: Converts Markdown → NSAttributedString + attachment placeholders
- MarkdownRenderer: Coordinates parsing and rendering pipeline
- AttachmentPool: Manages attachment view lifecycle with content-keyed reuse
- MarkdownTextView: Custom UITextView (TextKit1) with glyph-based attachment positioning
| Package | Purpose |
|---|---|
| swift-markdown | Markdown parsing |
| swift-snapshot-testing | Snapshot tests |
| Highlightr | Syntax highlighting (optional) |
This project uses Git LFS for XCFramework binaries and snapshot baseline images. Install Git LFS before cloning:
brew install git-lfs
git lfs install
git clone https://github.com/SteinX/MarkdownView.gitMIT. See LICENSE.