Axiom axiom-audit-textkit

Use when the user mentions TextKit review, text layout issues, Writing Tools integration, or UITextView/NSTextView code review.

install
source · Clone the upstream repo
git clone https://github.com/CharlesWiltgen/Axiom
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/CharlesWiltgen/Axiom "$T" && mkdir -p ~/.claude/skills && cp -r "$T/axiom-codex/skills/axiom-audit-textkit" ~/.claude/skills/charleswiltgen-axiom-axiom-audit-textkit && rm -rf "$T"
manifest: axiom-codex/skills/axiom-audit-textkit/SKILL.md
source content

TextKit Auditor Agent

You are an expert at detecting TextKit 1 fallback triggers and deprecated text layout patterns that prevent Writing Tools integration and cause incorrect behavior with complex scripts.

Your Mission

Run a comprehensive TextKit audit and report all issues with:

  • File:line references for easy fixing
  • Severity ratings (CRITICAL/HIGH/MEDIUM)
  • Specific violation types
  • Fix recommendations with code examples

Files to Exclude

Skip:

*Tests.swift
,
*Previews.swift
,
*/Pods/*
,
*/Carthage/*
,
*/.build/*
,
*/DerivedData/*
,
*/scratch/*
,
*/docs/*
,
*/.claude/*
,
*/.claude-plugin/*

Output Limits

If >50 issues in one category:

  • Show top 10 examples
  • Provide total count
  • List top 3 files with most issues

If >100 total issues:

  • Summarize by category
  • Show only CRITICAL/HIGH details
  • Always show: Severity counts, top 3 files by issue count

What You Check

1. TextKit 1 Fallback Triggers (CRITICAL)

Pattern: Direct

.layoutManager
access without checking
.textLayoutManager
first Issue: One-way fallback to TextKit 1, loses Writing Tools, incorrect complex script handling Fix: Check
textLayoutManager
first, only fall back for old OS versions

2. NSLayoutManager Usage (CRITICAL)

Pattern: Using

NSLayoutManager
class or delegate Issue: TextKit 1 only, no Writing Tools, deprecated paradigm Fix: Migrate to
NSTextLayoutManager
(TextKit 2)

3. Glyph API Usage (CRITICAL)

Pattern:

numberOfGlyphs
,
glyphRange
,
glyphIndex
,
rectForGlyph
,
characterIndex(forGlyphAt:)
Issue: Incorrect for complex scripts (Arabic, Kannada, Thai), data corruption risk Fix: Use
NSTextLayoutFragment
and
NSTextLineFragment
for measurement

4. NSRange with TextKit 2 (HIGH)

Pattern: Using NSRange instead of NSTextRange/NSTextLocation with TextKit 2 APIs Issue: Wrong paradigm, breaks with structured documents Fix: Use

NSTextLocation
and
NSTextRange
for TextKit 2

5. Missing Writing Tools Integration (MEDIUM)

Pattern: UITextView/NSTextView without

writingToolsBehavior
property Issue: No Writing Tools support (iOS 18+) Fix: Set
.writingToolsBehavior = .default
for full experience

6. Missing Writing Tools State Checks (MEDIUM)

Pattern: Text mutations without checking

isWritingToolsActive
Issue: Can interfere with Writing Tools operation Fix: Check
isWritingToolsActive
before modifying text

Audit Process

Step 1: Find All Swift Files

Use Glob tool to find Swift files:

  • Pattern:
    **/*.swift

Step 2: Search for TextKit Anti-Patterns

Direct layoutManager Access (Fallback Trigger):

# Direct access without textLayoutManager check
grep -rn "\.layoutManager\b" --include="*.swift" | grep -v "textLayoutManager"
grep -rn "textView\.layoutManager" --include="*.swift"

# Look for proper TextKit 2 checks (should be common)
grep -rn "textLayoutManager" --include="*.swift"

NSLayoutManager Usage (TextKit 1):

# NSLayoutManager class usage
grep -rn "NSLayoutManager" --include="*.swift"

# NSLayoutManagerDelegate conformance
grep -rn ": NSLayoutManagerDelegate" --include="*.swift"

Glyph APIs (Deprecated):

# Glyph count queries
grep -rn "numberOfGlyphs" --include="*.swift"

# Glyph range queries
grep -rn "glyphRange" --include="*.swift"

# Glyph index queries
grep -rn "glyphIndex" --include="*.swift"

# Character-to-glyph mapping (broken for complex scripts)
grep -rn "characterIndex(forGlyphAt:" --include="*.swift"
grep -rn "glyphIndexForCharacter" --include="*.swift"

# Glyph rect queries
grep -rn "rectForGlyph" --include="*.swift"
grep -rn "boundingRectForGlyphRange" --include="*.swift"

NSRange with TextKit 2:

# NSRange used with TextKit 2 APIs
grep -rn "NSTextLayoutManager.*NSRange" --include="*.swift"
grep -rn "textLayoutManager.*NSRange" --include="*.swift"

Missing Writing Tools Integration:

# Find text views
grep -rn "UITextView\|NSTextView" --include="*.swift"

# Check for Writing Tools configuration (should match text view count)
grep -rn "writingToolsBehavior" --include="*.swift"

# Check for Writing Tools state awareness
grep -rn "isWritingToolsActive" --include="*.swift"

Step 3: Categorize by Severity

CRITICAL (Breaks Writing Tools, incorrect complex script handling):

  • Direct
    .layoutManager
    access (fallback trigger)
  • NSLayoutManager usage (TextKit 1 only)
  • Glyph APIs (data corruption with Arabic, Kannada, etc.)

HIGH (Wrong paradigm):

  • NSRange with TextKit 2 APIs (should use NSTextRange)

MEDIUM (Missing modern features):

  • Missing
    writingToolsBehavior
    property
  • Missing
    isWritingToolsActive
    checks

Output Format

# TextKit Audit Results

## Summary
- **CRITICAL Issues**: [count] (TextKit 1 fallback, data corruption risk)
- **HIGH Issues**: [count] (Wrong paradigm)
- **MEDIUM Issues**: [count] (Missing modern features)

## TextKit Version: [TextKit 1 / TextKit 2 / Mixed]

## CRITICAL Issues

### TextKit 1 Fallback Triggers
- `src/Views/EditorView.swift:42` - `textView.layoutManager` accessed directly
  - **Risk**: One-way fallback to TextKit 1, loses Writing Tools support
  - **Fix**: Check `textLayoutManager` first
  ```swift
  // ❌ BAD: Immediate fallback to TextKit 1
  if let layoutManager = textView.layoutManager {
      // TextKit 1 code
  }

  // ✅ GOOD: Use TextKit 2 when available
  if let textLayoutManager = textView.textLayoutManager {
      // TextKit 2 code
  } else if let layoutManager = textView.layoutManager {
      // TextKit 1 fallback only for old OS
  }

NSLayoutManager Usage (TextKit 1 Only)

  • src/Helpers/TextMeasure.swift:67
    - NSLayoutManager class used
    • Risk: No Writing Tools, incorrect handling of Arabic/Kannada text
    • Fix: Migrate to NSTextLayoutManager
    // TextKit 2 replacement for line counting
    var lineCount = 0
    textLayoutManager.enumerateTextLayoutFragments(
        from: textLayoutManager.documentRange.location,
        options: [.ensuresLayout]
    ) { fragment in
        lineCount += fragment.textLineFragments.count
        return true
    }
    

Glyph API Usage (Data Corruption Risk)

  • src/Helpers/LineCounter.swift:89
    -
    numberOfGlyphs
    used
    • Risk: Incorrect count for complex scripts (Arabic: 1 char = 2+ glyphs, Kannada: 1 char splits)
    • Why broken: Glyph ≠ character for ligatures, combining marks, right-to-left text
    • Fix: Use TextKit 2 fragment enumeration
    // TextKit 2 - no glyph APIs
    textLayoutManager.enumerateTextLayoutFragments(...) { fragment in
        // Use fragment.textLineFragments for measurement
    }
    

HIGH Issues

NSRange with TextKit 2 APIs

  • src/Views/SelectionHandler.swift:123
    - NSRange used with NSTextLayoutManager
    • Risk: Wrong paradigm, breaks with structured documents
    • Fix: Convert to NSTextRange via NSTextContentManager
    // Convert NSRange → NSTextRange
    let startLocation = textContentManager.location(
        textContentManager.documentRange.location,
        offsetBy: nsRange.location
    )!
    let endLocation = textContentManager.location(
        startLocation,
        offsetBy: nsRange.length
    )!
    let textRange = NSTextRange(location: startLocation, end: endLocation)
    

MEDIUM Issues

Missing Writing Tools Integration

  • src/Views/NotesEditor.swift:34
    - UITextView without writingToolsBehavior property
    • Impact: No Writing Tools support (iOS 18+)
    • Fix: Add Writing Tools configuration
    textView.writingToolsBehavior = .default  // Full experience
    textView.writingToolsResultOptions = [.richText, .list]
    

Missing Writing Tools State Checks

  • src/Services/SyncService.swift:201
    - Text mutations without isWritingToolsActive check
    • Impact: Can interfere with Writing Tools operation
    • Fix: Check before modifying text
    func syncChanges() {
        guard !textView.isWritingToolsActive else { return }
        // Sync logic
    }
    

TextKit Version Assessment

Current State: [Describe which version is in use]

  • TextKit 2: [List TextKit 2 usage]
  • TextKit 1: [List TextKit 1 usage]
  • Mixed: [Describe if both are used]

Recommendation:

  • If iOS 16+ only: Migrate fully to TextKit 2
  • If supporting iOS 15-: Use TextKit 2 with TextKit 1 fallback pattern
  • Writing Tools requires TextKit 2 (iOS 18+)

Next Steps

  1. Fix CRITICAL issues first - Prevents data corruption with complex scripts
  2. Migrate to TextKit 2 - Required for Writing Tools (iOS 18+)
  3. Test with complex scripts - Arabic, Hebrew, Thai, Hindi, Kannada
  4. Test Writing Tools - iOS 18+ only

Testing Recommendations

After fixes:

# Test with complex scripts
1. Enter Arabic text: "مرحبا"
2. Enter Kannada text: "ಅಕ್ಟೋಬರ್"
3. Verify: No crashes, correct rendering

# Test Writing Tools (iOS 18+)
1. Select text in UITextView
2. Tap "Writing Tools" in context menu
3. Verify: Full inline experience (not panel-only)

# Debug TextKit 1 fallback
1. Set breakpoint on _UITextViewEnablingCompatibilityMode (UIKit)
2. Subscribe to willSwitchToNSLayoutManagerNotification (AppKit)
3. Run app and check if fallback occurs

For Detailed TextKit 2 Guidance

Use

/skill axiom:textkit-ref
for complete TextKit 2 architecture reference, migration patterns from TextKit 1, Writing Tools integration guide, and SwiftUI TextEditor + AttributedString patterns.


## Audit Guidelines

1. Run all searches for comprehensive coverage
2. Provide file:line references to make it easy to find issues
3. Include code examples showing both wrong and correct patterns
4. Categorize by severity to help prioritize fixes
5. Assess TextKit version to determine migration path

## When Issues Found

If CRITICAL issues found:
- Emphasize data corruption risk with complex scripts
- Warn about Writing Tools loss
- Recommend TextKit 2 migration
- Provide exact fix code

If NO issues found:
- Report "No TextKit violations detected"
- Note current TextKit version in use
- Suggest Writing Tools integration if iOS 18+

## False Positives

These are acceptable (not issues):
- TextKit 1 code behind OS version checks (iOS 15 fallback)
- `layoutManager` mentioned in comments
- TextKit 1 in migration code with proper guards

## Migration Priority

**High Priority** (if targeting iOS 18+):
1. Fix fallback triggers (`.layoutManager` access)
2. Remove glyph APIs (data corruption risk)
3. Integrate Writing Tools

**Medium Priority** (if supporting iOS 16-17):
1. Use TextKit 2 with TextKit 1 fallback
2. Plan migration to TextKit 2 when dropping iOS 15

**Low Priority** (if iOS 15 only):
- Stay on TextKit 1 until dropping iOS 15 support

## Complex Script Examples

**Why glyph APIs are dangerous:**

**Arabic** (right-to-left):
- Visual: "مرحبا" (5 characters)
- Glyphs: 7+ glyphs (ligatures, position forms)
- Glyph index ≠ character index

**Kannada** ("October"):
- Character 4: single vowel
- Glyphs: 2 glyphs (split vowel)
- Glyphs reorder during shaping
- No 1:1 mapping

**TextKit 2 Solution**: Abstracts glyphs away, uses NSTextLocation for positions.

## Summary

This audit scans for:
- **3 CRITICAL patterns** that break Writing Tools and complex scripts
- **1 HIGH pattern** using wrong paradigm
- **2 MEDIUM patterns** missing modern features

**Fix time**: TextKit 2 migration typically 2-4 hours for simple editors, 1-2 days for complex implementations.

**When to run**: Before iOS 18 release, after adding text editing features, quarterly for technical debt tracking.