Native iOS and macOS rendering of LaTeX math formulas via Swift and CoreGraphics. No WebView, no JavaScript, no DOM.
LaTeX string
↓ ratex_parse_and_layout() [C ABI, static lib]
JSON DisplayList
↓ RaTeXEngine.parse() [Swift JSON decode]
DisplayList
↓ RaTeXRenderer.draw() [CoreGraphics]
UIView / NSView / SwiftUI View
- Add the dependency — use Swift Package Manager for the complete native UI package, or CocoaPods for the core-only renderer (see Installation).
- Choose the API that matches the dependency:
-
Swift Package Manager: use
RaTeXVieworRaTeXFormula.// SwiftUI RaTeXFormula(latex: #"\frac{-b \pm \sqrt{b^2-4ac}}{2a}"#, fontSize: 24)
-
RaTeXCore CocoaPod: use
RaTeXEngineandRaTeXRendererfrom your customUIView,NSView, orCGContextdrawing code.let displayList = try RaTeXEngine.shared.parse(#"\frac{1}{2}"#) let renderer = RaTeXRenderer(displayList: displayList, fontSize: 20) renderer.draw(in: context)
-
Fonts load automatically on the first RaTeXRenderer.draw(in:) call. To preload
them at startup, use RaTeXFontLoader.loadFromPackageBundle() with Swift Package
Manager or RaTeXFontLoader.loadFromCocoaPodsBundle() with RaTeXCore.
| Tool | Version |
|---|---|
| Xcode | 15+ |
| Rust | 1.75+ (rustup) |
| iOS target | 14+ |
| macOS target | 14+ |
Install Rust Apple targets once:
rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios
rustup target add aarch64-apple-darwin x86_64-apple-darwinFrom the repo root:
bash scripts/build-apple-xcframework.shThis produces platforms/ios/RaTeX.xcframework with iOS and macOS slices.
For an iOS-only local artifact, use the compatibility wrapper:
bash platforms/ios/build-ios.shIn Xcode: File → Add Package Dependencies, enter https://github.com/erweixin/RaTeX and select the RaTeX product.
Local development — Run bash platforms/ios/build-ios.sh first for iOS-only work, or bash scripts/build-apple-xcframework.sh for macOS SPM consumers. Then point Xcode to the repo root via File → Add Package Dependencies → Add Local….
For a runnable macOS SPM integration check, see demo/spm-macos.
- Drag
platforms/ios/RaTeX.xcframeworkinto your Xcode project. - In Build Phases → Link Binary With Libraries, ensure it is listed.
- Copy the
platforms/ios/Sources/Ratex/*.swiftfiles into your project, includingPlatformCompat.swift. - Add the
Fontsfolder fromplatforms/ios/Sources/Ratex/Fonts/to your target’s Copy Bundle Resources; fonts load automatically on first render, or callRaTeXFontLoader.loadFromBundle()at startup.
Add the repository pod to your Podfile:
pod "RaTeXCore", :git => "https://github.com/erweixin/RaTeX.git", :branch => "main"For reproducible builds, replace :branch => "main" with a released RaTeX
tag after the first release containing RaTeXCore.podspec.
import RaTeXCore
let displayList = try RaTeXEngine.shared.parse(#"\frac{1}{2}"#)
let renderer = RaTeXRenderer(displayList: displayList, fontSize: 20)
// In your UIView.draw(_:) or CGContext block:
renderer.draw(in: context)RaTeXCore is a static, UI-free target. It contains the FFI XCFramework,
display-list types, engine, renderer, font loader, and KaTeX font bundle, but
does not depend on React, React Native, Fabric, Codegen, or JavaScript.
import RaTeX
let mathView = RaTeXView()
mathView.latex = #"\frac{-b \pm \sqrt{b^2-4ac}}{2a}"#
mathView.fontSize = 28
mathView.displayMode = true // true = display/block (default); false = inline/text
mathView.color = .systemBlue
mathView.onError = { print("RaTeX error:", $0) }
// Auto-sizing
mathView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(mathView)
NSLayoutConstraint.activate([
mathView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
mathView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])import RaTeX
struct ContentView: View {
var body: some View {
RaTeXFormula(
latex: #"\int_0^\infty e^{-x^2}\,dx = \frac{\sqrt{\pi}}{2}"#,
fontSize: 24,
color: .blue
// displayMode: true ← default; pass false for inline/text style
)
.ratexColor(.primary)
.padding()
}
}Use .ratexColor(...) to set a default color for descendant formulas, and pass color: to override an individual RaTeXFormula.
For inline rendering, use a custom FlowLayout (a SwiftUI Layout) that places Text and RaTeXFormula children side-by-side with automatic line wrapping. Baseline alignment uses the library-provided RaTeXFormulaAscentKey layout value, which RaTeXFormula exposes from the first frame — no two-pass measurement needed.
import RaTeX
struct InlineExample: View {
private let fs: CGFloat = 17
var body: some View {
FlowLayout(horizontalSpacing: 3, lineSpacing: 6) {
Text("由勾股定理")
RaTeXFormula(latex: #"a^2 + b^2 = c^2"#, fontSize: fs, onError: { _ in })
Text("可直接求得斜边长度。")
}
}
}
// FlowLayout: wrap children horizontally, align baselines.
// Reads RaTeXFormulaAscentKey for formula baseline; falls back to
// firstTextBaseline for Text views.
struct FlowLayout: Layout {
var horizontalSpacing: CGFloat = 4
var lineSpacing: CGFloat = 6
// ... see demo/ios for the full implementation
}RaTeXFormulaAscentKey is a LayoutValueKey<CGFloat> built into the library. It carries the formula's ascent (distance from baseline to top) so that FlowLayout can align mixed children without manual offset calculation.
Baseline alignment is cross-platform.
RaTeXFormulareports its math baseline through.alignmentGuide(.firstTextBaseline), soHStack(alignment: .firstTextBaseline)andTextbaseline alignment line up correctly on both iOS and macOS, on every supported OS version — no customLayoutrequired for the simple case:HStack(alignment: .firstTextBaseline) { Text("Euler's identity:") RaTeXFormula(latex: #"e^{i\pi}+1=0"#, fontSize: 17, displayMode: false) }The
RaTeXFormulaAscentKey+ customLayoutapproach above (iOS 16+ / macOS 13+) is only needed when you also want automatic line wrapping; seedemo/spm-macosfor a runnable macOS example. When embeddingRaTeXViewdirectly in raw UIKit/AppKit Auto Layout, itsfirstBaselineAnchor/lastBaselineAnchoralready resolve to the formula's math baseline on both platforms — UIKit viaforFirstBaselineLayout, AppKit viafirstBaselineOffsetFromTop/lastBaselineOffsetFromBottom— so a baseline constraint against anNSTextField/UILabellines up without any extra work.
import RaTeX
// display mode (default)
let displayList = try RaTeXEngine.shared.parse(#"\sum_{n=1}^\infty \frac{1}{n^2}"#)
// inline mode
let displayList = try RaTeXEngine.shared.parse(#"\frac{1}{2}"#, displayMode: false)
// custom default color
let blueDisplayList = try RaTeXEngine.shared.parse(#"x + y"#, color: .systemBlue)
let renderer = RaTeXRenderer(displayList: displayList, fontSize: 20)
// In your UIView.draw(_:) or CGContext block:
renderer.draw(in: UIGraphicsGetCurrentContext()!)Explicit LaTeX colors such as \color{...} still override the default color for their subtree.
All DisplayList coordinates are in em units. RaTeXRenderer multiplies them
by fontSize (pt) to produce screen coordinates.
- X increases rightward from the left edge.
- Y increases downward from the top edge.
- Baseline is at Y =
height × fontSize.
| File | Purpose |
|---|---|
build-ios.sh |
iOS-only build entry (delegates to unified Apple build script) |
Package.swift |
Swift Package manifest |
Sources/Ratex/DisplayList.swift |
Codable Swift mirror of Rust types |
Sources/Ratex/PlatformCompat.swift |
UIKit/AppKit compatibility aliases |
Sources/Ratex/RaTeXEngine.swift |
Calls C ABI, decodes JSON |
Sources/Ratex/RaTeXRenderer.swift |
CoreGraphics drawing loop |
Sources/Ratex/RaTeXView.swift |
UIKit/AppKit view + SwiftUI View |