Skip to content

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 .layoutManager causes 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 NSTextViewportLayoutControllerDelegate hooks directly inside a UITextView subclass
  • registerTextAttachmentViewProviderReusePolicy(_:forTextAttachmentViewProviderType:) – recycle inline attachment views instead of rebuilding them
  • Collapsible content via shouldEnumerate — exclude collapsed paragraphs from layout using the NSTextContentStorageDelegate

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:

swift
// ✅ 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

swift
// iOS 16+ / macOS 13+
let textView = UITextView(usingTextLayoutManager: true)

Writing Tools Integration

swift
// 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 .layoutManager permanently switches to TextKit 1
  • No glyph APIs – Use NSTextLocation and layout fragments instead
  • NSTextTable unsupported – Use NSTextList or custom layouts

Released under the MIT License