A native Swift port of the Pretext text layout engine. It measures and lays out multiline text without touching the view hierarchy, so repeated layout stays pure arithmetic over cached Core Text measurements.
On the current release benchmark table, Pretext is about 5.8x faster than Core Text and 17.9x faster than SwiftUI for Batch Prepare + Layout (500 texts).
| Test | Pretext | Core Text | SwiftUI | Vs CT | Vs SwiftUI |
|---|---|---|---|---|---|
| Batch Prepare + Layout (500 texts) | 4.9ms | 28.5ms | 87.8ms | 5.8x | 17.9x |
| Reflow 100 Widths (50k calls) | 7.0ms | 1895ms | 11703ms | 272x | 1680x |
| Variable-Width Line-by-Line | 0.08ms | 1.6ms | — | 19.1x | — |
| Interleaved Measure-Mutate (500x) | 4.8ms | 28.8ms | 91.4ms | 6.0x | 19.0x |
| Masonry Heights (1904 texts) | 8.4ms | 25.9ms | 307ms | 3.1x | 36.7x |
The hot layout() path is pure arithmetic. Measurement happens once in prepare(...); repeated layout reuses cached widths.
EditorialEngine.mp4
Editorial Engine — dark multi-column editorial layout with animated orb obstacles
ChikaDance.mp4
Chika Dance — animated text reflow around live video silhouettes
Fluid.mp4
Fluid — particle-style text field reflow
The demo app also includes:
Situational Awareness: light editorial layout with obstacle-aware text flowMasonry: waterfall card layout driven by cached text measurementIllustrated Manuscript: decorated long-form layout with physics-driven page compositionLive Camera Silhouette: camera-driven text routing around a live subject silhouetteBenchmark: in-app performance comparison against Core Text and SwiftUI
On Apple Watch, the catalog is intentionally narrower and currently includes:
Situational AwarenessEditorial EngineMasonryIllustrated ManuscriptFluidBenchmark
PretextsupportsiOS 18+,macOS 15+, andwatchOS 11+PretextUIprovides the optionalSwiftUI.Fontbridge on those same platformsDemosupportsiPhone,iPad,macOS, and a curated Apple Watch catalog in this repository- the standalone
Benchmarkexecutable remains macOS-first, while the shared benchmark presentation is also used inside the demo app
The package ships four targets:
Pretext: core layout enginePretextUI: optional SwiftUI bridge forFontDescriptorDemo: interactive sample appBenchmark: standalone benchmark app backed by shared benchmark support code
# Launch the macOS demo app
rake demo
# Run the full SwiftPM test suite
rake test
# Run the CLI benchmark
rake benchRequires Xcode with iOS 18 / macOS 15 / watchOS 11 SDK support and Swift 6.0+.
import CoreText
import Pretext
let font = CTFontCreateWithName("Helvetica Neue" as CFString, 16, nil)
let prepared = prepare("AGI 春天到了. بدأت الرحلة 🚀", font: font)
let result = layout(prepared, maxWidth: 320, lineHeight: 20)
print(result.lineCount)
print(result.height)If you want textarea-style preserved spaces, tabs, and hard breaks, use whiteSpace: .preWrap during prepare(...).
import CoreText
import Pretext
let font = CTFontCreateWithName("Helvetica Neue" as CFString, 18, nil)
let prepared = prepareWithSegments("AGI 春天到了. بدأت الرحلة 🚀", font: font)
let (result, lines) = layoutWithLines(prepared, maxWidth: 320, lineHeight: 26)
let naturalWidth = measureNaturalWidth(prepared)
walkLineRanges(prepared, maxWidth: 320) { width, start, end in
print(width, start, end)
}
var cursor = LayoutCursor.start
while let line = layoutNextLine(prepared, start: cursor, maxWidth: 320) {
print(line.text)
cursor = line.end
}Useful helpers:
prepareForWidth(...): lazily resolves breakable grapheme widths for a specific widthmeasureNaturalWidth(...): returns the widest forced line when width itself is not causing wrapssetLocale(...): retargets text analysis for futureprepare(...)callsclearCache(): clears shared analysis and measurement caches
import PretextUI
let descriptor = FontDescriptor(familyName: "Helvetica Neue", size: 16)
let displayFont = descriptor.makeDisplayFont()prepare(text, font)orprepareWithSegments(text, font)segments text, measures runs with Core Text, and caches widths.layout(prepared, maxWidth, lineHeight)computes multiline layout from cached widths.layoutWithLines(...),walkLineRanges(...), andlayoutNextLine(...)expose the richer manual-layout path.measureNaturalWidth(prepared)returns the widest forced line for rich/manual layout work.
The key idea is separating measurement from layout so resize and animated obstacle reflow stay cheap. Measurement happens once up front; repeated layout is pure arithmetic over cached widths.
Based on Pretext by Cheng Lou. The Swift port reimplements the text analysis pipeline, line-breaking engine, and editorial demos using Core Text for measurement and SwiftUI Canvas for rendering.
Demo-specific credits:
Chika Danceuses a bundled sample clip sourced via Matteflow bysummerKK; Matteflow credits the underlyingChika Dance Green Screenfootage to Conics.Illustrated Manuscriptadapts theDragondemo from Pretext Playground by Builderz.Fluidadapts fluid.felixmartinez.dev by Felix Martinez.