TextKit 2 Reference
This reference helps you work with Apple's TextKit 2 framework for custom text rendering, rich text editing, and Writing Tools integration. It covers architecture, migration from TextKit 1, and iOS 26–27 features.
When to Use This Reference
Use this reference when you're:
- Building custom text views or rich text editors
- Migrating from TextKit 1 to TextKit 2
- Integrating Writing Tools (iOS 18+)
- Working with SwiftUI TextEditor and AttributedString (iOS 26+)
- Debugging text layout issues or unexpected fallbacks to TextKit 1
- Customizing rendering surfaces or attachment view recycling from inside a UITextView subclass (iOS 27+)
Not sure if you need TextKit 2? If you're using standard Text, TextField, or UILabel, you probably don't. TextKit 2 is for custom text rendering, syntax highlighting, or rich text editing beyond what built-in controls provide.
Example Prompts
Questions you can ask Claude that will draw from this reference:
- "How do I migrate my text view from TextKit 1 to TextKit 2?"
- "Why is my UITextView falling back to TextKit 1?"
- "How do I count lines in TextKit 2 without glyph APIs?"
- "How do I integrate Writing Tools with my custom text editor?"
- "How do I use AttributedString with SwiftUI TextEditor in iOS 26?"
- "What's the difference between NSTextRange and NSRange?"
- "How do I customize text fragment rendering inside a UITextView without losing input or accessibility?"
- "How do I recycle inline attachment views in my text editor?"
What's Covered
Core Architecture
- Three-layer MVC pattern (Model, Controller, View)
- NSTextContentManager, NSTextLayoutManager, NSTextViewportLayoutController
- Object-based ranges (NSTextLocation, NSTextRange) vs integer indices
Migration from TextKit 1
- Paradigm shift from glyphs to elements
- NSRange ↔ NSTextRange conversion patterns
- Fallback triggers to avoid (accessing
.layoutManagercauses one-way fallback) - Why glyph APIs are dangerous for international text
Writing Tools (iOS 18+)
- TextKit 2 requirement for full experience
- Lifecycle delegate methods
- Protected ranges for code blocks and quotes
- Writing Tools Coordinator for custom text engines (iOS 26+)
SwiftUI TextEditor (iOS 26+)
- AttributedString binding
- Custom formatting definitions
- Value constraints
- Selection handling
- AttributedString text alignment and line height control
- Programmatic selection replacement
- DiscontiguousAttributedSubstring for non-contiguous selections
Viewport Rendering Surfaces & Attachment Reuse (iOS 27)
NSTextViewportRenderingSurface– render text fragments from a custom view without building a full custom text engine- Subclassable viewport delegate on framework text views — override
NSTextViewportLayoutControllerDelegatehooks directly inside aUITextViewsubclass registerTextAttachmentViewProviderReusePolicy(_:forTextAttachmentViewProviderType:)– recycle inline attachment views instead of rebuilding them- Collapsible content via
shouldEnumerate— exclude collapsed paragraphs from layout using theNSTextContentStorageDelegate
Documentation Scope
This page documents the axiom-uikit skill — a comprehensive reference Claude uses when answering TextKit 2 questions. The skill contains detailed API documentation, code examples, and migration patterns.
For automated scanning: Use the textkit-auditor agent to scan your codebase for TextKit 1 fallback triggers and migration opportunities.
For typography and Dynamic Type: See typography-ref for font handling, text styles, and accessibility considerations.
Key Patterns
Checking for TextKit 2 (Critical)
Always check TextKit 2 first to avoid triggering fallback:
// ✅ GOOD: Check TextKit 2 first
if let textLayoutManager = textView.textLayoutManager {
// TextKit 2 code
} else if let layoutManager = textView.layoutManager {
// TextKit 1 fallback only for old OS
}
// ❌ BAD: Accessing .layoutManager triggers one-way fallback
if let layoutManager = textView.layoutManager {
// You're now stuck in TextKit 1!
}Creating a TextKit 2 Text View
// iOS 16+ / macOS 13+
let textView = UITextView(usingTextLayoutManager: true)Writing Tools Integration
// Lifecycle awareness
func textViewWritingToolsWillBegin(_ textView: UITextView) {
isSyncing = false // Pause syncing during Writing Tools
}
func textViewWritingToolsDidEnd(_ textView: UITextView) {
isSyncing = true // Resume syncing
}Known Limitations
- One-way fallback – Accessing
.layoutManagerpermanently switches to TextKit 1 - No glyph APIs – Use NSTextLocation and layout fragments instead
- NSTextTable unsupported – Use NSTextList or custom layouts
Related Resources
- textkit-auditor – Autonomous agent that scans for TextKit 1 fallback triggers
- typography-ref – Typography, Dynamic Type, and font handling reference
- WWDC 2021-10061 – Meet TextKit 2
- WWDC 2024-10168 – Get started with Writing Tools
- WWDC 2025-280 – Cook up a rich text experience in SwiftUI
- WWDC 2026-370 – Viewport rendering surfaces and attachment reuse in TextKit 2