AppKit vs UIKit Text Capabilities
Use when comparing NSTextView and UITextView capabilities, porting editor behavior between macOS and iOS, or deciding whether an AppKit-only text feature has a UIKit equivalent. Reach for this when the main task is platform capability tradeoffs, not TextKit 1 vs TextKit 2 choice.
Use when comparing NSTextView and UITextView capabilities, porting editor behavior between macOS and iOS, or deciding whether an AppKit-only text feature has a UIKit equivalent. Reach for this when the main task is platform capability tradeoffs, not TextKit 1 vs TextKit 2 choice.
Family: View And Stack Decisions
Use this skill when the main question is which platform text stack can support a capability or editor behavior.
When to Use
Section titled “When to Use”- You are comparing
NSTextViewandUITextViewcapabilities. - You are porting text code between macOS and iOS.
- You need to know whether a feature gap is architectural or just an API difference.
Quick Decision
Section titled “Quick Decision”- Desktop document-editor features, text tables, rulers, or services -> AppKit
- Touch-first editing, modular interactions, or iOS-specific selection UI -> UIKit
- Unsure whether the feature is platform-only or text-view-only -> keep reading
Core Guidance
Section titled “Core Guidance”NSTextView Can Do — UITextView Cannot
Section titled “NSTextView Can Do — UITextView Cannot”Rich Text Editing Panels (AppKit Only)
Section titled “Rich Text Editing Panels (AppKit Only)”NSTextView provides built-in formatting panels with no UIKit equivalent:
| Panel | API | Purpose |
|---|---|---|
| Font Panel | usesFontPanel | System Fonts window syncs with selection |
| Ruler | usesRuler / isRulerVisible | Interactive paragraph formatting (margins, tabs) |
| Link Panel | orderFrontLinkPanel: | Insert/edit hyperlinks |
| List Panel | orderFrontListPanel: | Configure list formatting |
| Table Panel | orderFrontTablePanel: | Insert/manipulate text tables |
| Spacing Panel | orderFrontSpacingPanel: | Paragraph spacing configuration |
| Substitutions Panel | orderFrontSubstitutionsPanel: | Smart quotes, dashes, text replacement config |
Text Tables (NSTextTable / NSTextTableBlock)
Section titled “Text Tables (NSTextTable / NSTextTableBlock)”AppKit-only classes. No UIKit equivalent exists.
let table = NSTextTable()table.numberOfColumns = 3
let cell = NSTextTableBlock(table: table, startingRow: 0, rowSpan: 1, startingColumn: 0, columnSpan: 1)cell.backgroundColor = .lightGraycell.setWidth(1.0, type: .absoluteValueType, for: .border)
let style = NSMutableParagraphStyle()style.textBlocks = [cell]Features: row/column spanning, per-cell borders/background, padding/margin control, automatic or fixed layout. Triggers TextKit 1 fallback.
Grammar Checking
Section titled “Grammar Checking”// AppKittextView.isGrammarCheckingEnabled = truetextView.toggleGrammarChecking(nil)
// UIKit — NO equivalent API// Grammar checking is system-level only, not exposed to developersText Completion System
Section titled “Text Completion System”// AppKit — full completion infrastructuretextView.complete(nil) // Invoke completion popuptextView.isAutomaticTextCompletionEnabled = true
// Override for custom completions:override func completions(forPartialWordRange charRange: NSRange, indexOfSelectedItem index: UnsafeMutablePointer<Int>) -> [String]? { return ["completion1", "completion2"]}
// UIKit — NO built-in completion API// Must build custom using UITextInput + overlay viewsSpell Checking (Granular Control)
Section titled “Spell Checking (Granular Control)”| Feature | AppKit | UIKit |
|---|---|---|
| Toggle continuous spell checking | isContinuousSpellCheckingEnabled | spellCheckingType (.yes/.no) |
| Mark specific range as misspelled | setSpellingState(_:range:) | Not available |
| Document tag management | spellCheckerDocumentTag() | Not available |
| Grammar checking | isGrammarCheckingEnabled | Not available |
Smart Substitutions (Individual Toggle APIs)
Section titled “Smart Substitutions (Individual Toggle APIs)”| Feature | AppKit API | UIKit Equivalent |
|---|---|---|
| Smart quotes | isAutomaticQuoteSubstitutionEnabled | smartQuotesType (similar) |
| Smart dashes | isAutomaticDashSubstitutionEnabled | smartDashesType (similar) |
| Text replacement | isAutomaticTextReplacementEnabled | System-level only |
| Auto-correction | isAutomaticSpellingCorrectionEnabled | autocorrectionType (similar) |
| Link detection | isAutomaticLinkDetectionEnabled | dataDetectorTypes (different API) |
| Data detection | isAutomaticDataDetectionEnabled | dataDetectorTypes (different API) |
AppKit provides individual toggle actions and a Substitutions Panel for user configuration. UIKit has simpler enum properties.
Services Menu Integration
Section titled “Services Menu Integration”NSTextView automatically participates in the macOS Services menu (send selected text to other apps):
// Automatic for NSTextView — implements NSServicesMenuRequestor// Services like "Look Up in Dictionary", "Send via Mail" appear in Services menu
// For custom views:override func validRequestor(forSendType sendType: NSPasteboard.PasteboardType?, returnType: NSPasteboard.PasteboardType?) -> Any? { if sendType == .string { return self } return super.validRequestor(forSendType: sendType, returnType: returnType)}iOS has no Services concept.
Field Editor Architecture
Section titled “Field Editor Architecture”Unique to AppKit. One shared NSTextView per window handles all NSTextField editing:
// Get the field editorlet fieldEditor = window.fieldEditor(true, for: textField) as? NSTextView
// Custom field editorfunc windowWillReturnFieldEditor(_ sender: NSWindow, to client: Any?) -> Any? { if client is MySpecialTextField { return myCustomFieldEditor } return nil}Key implications:
- Memory efficient (one editor for all fields)
- One TK1 fallback in ANY field editor affects ALL fields in the window
- Custom field editors can provide per-field customization
UIKit has no field editor. Each UITextField manages its own editing.
NSText Heritage (Direct RTF/RTFD)
Section titled “NSText Heritage (Direct RTF/RTFD)”NSTextView inherits from NSText, providing:
// Direct RTF/RTFD I/Olet rtfData = textView.rtf(from: range)let rtfdData = textView.rtfd(from: range)textView.replaceCharacters(in: range, withRTF: rtfData)textView.replaceCharacters(in: range, withRTFD: rtfdData)textView.writeRTFD(toFile: path, atomically: true)textView.readRTFD(fromFile: path)
// Font/ruler pasteboardtextView.copyFont(nil) // Copy font attributestextView.pasteFont(nil) // Apply font from pasteboardtextView.copyRuler(nil) // Copy paragraph attributestextView.pasteRuler(nil) // Apply paragraph attributes
// SpeechtextView.startSpeaking(nil)textView.stopSpeaking(nil)UIKit has none of these. You’d need to manually create RTF data, manage font pasteboards, or use AVSpeechSynthesizer.
Print Support
Section titled “Print Support”// AppKit — native print support via NSViewtextView.printView(nil) // Opens print dialog
// UIKit — separate print systemlet formatter = UISimpleTextPrintFormatter(attributedText: textView.attributedText)let controller = UIPrintInteractionController.sharedcontroller.printFormatter = formattercontroller.present(animated: true)macOS Dark Mode gotcha: printView(nil) renders with the current appearance. In Dark Mode, this produces white text on white paper. Fix: create an off-screen text view sharing the same NSTextStorage with .appearance = NSAppearance(named: .aqua), and print from that.
iOS printing tiers: UISimpleTextPrintFormatter handles most cases. For custom headers/footers or multi-section documents, subclass UIPrintPageRenderer and compose formatters per page range.
Find and Replace
Section titled “Find and Replace”| Feature | AppKit | UIKit |
|---|---|---|
| API | NSTextFinder (since OS X 10.7) | UIFindInteraction (since iOS 16) |
| Properties | usesFindBar, usesFindPanel | findInteraction, isFindInteractionEnabled |
| Incremental search | ✅ Built-in | ✅ Built-in |
| Custom providers | NSTextFinderClient protocol | UITextSearching protocol |
Both platforms now support find, but AppKit’s has existed much longer with more customization.
UITextView Can Do — NSTextView Cannot
Section titled “UITextView Can Do — NSTextView Cannot”Data Detector Types (Declarative)
Section titled “Data Detector Types (Declarative)”// UIKit — single property, granular type selectiontextView.dataDetectorTypes = [.link, .phoneNumber, .address, .calendarEvent, .shipmentTrackingNumber, .flightNumber]// Detected items become tappable automatically// Only works when isEditable = false
// AppKit — Boolean toggles, less granulartextView.isAutomaticLinkDetectionEnabled = truetextView.isAutomaticDataDetectionEnabled = true// Requires explicit checkTextInDocument: callUITextInteraction (Modular Gestures)
Section titled “UITextInteraction (Modular Gestures)”// UIKit — add system text gestures to ANY UIViewlet interaction = UITextInteraction(for: .editable)interaction.textInput = customView // Any UITextInput conformercustomView.addInteraction(interaction)
// AppKit — no equivalent modular component// NSTextView has built-in gestures, but they can't be extractedText Item Interactions (iOS 17+)
Section titled “Text Item Interactions (iOS 17+)”// UIKit — rich interaction with links, attachments, tagged rangesfunc textView(_ textView: UITextView, primaryActionFor textItem: UITextItem, defaultAction: UIAction) -> UIAction? { // Customize tap behavior}
func textView(_ textView: UITextView, menuConfigurationFor textItem: UITextItem, defaultMenu: UIMenu) -> UITextItem.MenuConfiguration? { // Custom context menu}
// Tag arbitrary ranges for interactionlet attrs: [NSAttributedString.Key: Any] = [ .uiTextItemTag: "myCustomTag"]
// AppKit — only has textView(_:clickedOnLink:at:)UITextSelectionDisplayInteraction (iOS 17+)
Section titled “UITextSelectionDisplayInteraction (iOS 17+)”System selection UI for custom views — cursor, handles, highlights:
let selectionDisplay = UITextSelectionDisplayInteraction(textInput: myView)myView.addInteraction(selectionDisplay)selectionDisplay.setNeedsSelectionUpdate()AppKit gained NSTextInsertionIndicator (macOS Sonoma) for the cursor only, but nothing as comprehensive.
UITextLoupeSession (iOS 17+)
Section titled “UITextLoupeSession (iOS 17+)”let session = UITextLoupeSession.begin(at: point, from: cursorView, in: self)session.move(to: newPoint)session.invalidate()No AppKit equivalent — macOS doesn’t use a loupe for text selection.
Architecture Differences
Section titled “Architecture Differences”Inheritance
Section titled “Inheritance”AppKit: NSObject → NSResponder → NSView → NSText → NSTextView (Rich NSText base: RTF, font panel, ruler, field editor, speech)
UIKit: NSObject → UIResponder → UIView → UIScrollView → UITextView (Scrolling built-in, but no text-specific base class)Scrolling
Section titled “Scrolling”| AppKit | UIKit | |
|---|---|---|
| Built-in scroll | ❌ Must embed in NSScrollView | ✅ IS a UIScrollView |
| Convenience | NSTextView.scrollableTextView() | Always scrollable |
| Non-scrolling | NSTextView is non-scrolling by default | Set isScrollEnabled = false |
Text Storage Access
Section titled “Text Storage Access”| AppKit | UIKit | |
|---|---|---|
| Property | textStorage: NSTextStorage? (optional) | textStorage: NSTextStorage (non-optional) |
| Full content | attributedString() method | attributedText property |
Delegate Richness
Section titled “Delegate Richness”NSTextViewDelegate is significantly richer than UITextViewDelegate:
- Modify selection during changes
- Intercept link clicks
- Customize drag operations
- Control tooltip display
- Completion handling
UITextViewDelegate is minimal, though iOS 17 text item interactions narrowed the gap.
Writing Tools (Equivalent)
Section titled “Writing Tools (Equivalent)”Both platforms have identical Writing Tools support as of iOS 18 / macOS 15:
writingToolsBehavior/writingToolsAllowedInputOptionsisWritingToolsActive- Matching delegate methods
- Both require TextKit 2 for full inline experience
Quick Decision Guide
Section titled “Quick Decision Guide”| Need | Platform |
|---|---|
| Text tables | AppKit only (NSTextTable) |
| Grammar checking API | AppKit only |
| Text completion API | AppKit only |
| Services menu | AppKit only (macOS concept) |
| Font panel integration | AppKit only |
| Interactive ruler | AppKit only |
| Direct RTF file I/O | AppKit only (NSText heritage) |
| Declarative data detectors | UIKit better (dataDetectorTypes) |
| Modular text interaction | UIKit only (UITextInteraction) |
| Text item context menus | UIKit only (iOS 17+) |
| Selection display component | UIKit only (UITextSelectionDisplayInteraction) |
| Multi-page/multi-column | AppKit better (historically) |
| Built-in scrolling | UIKit (IS a scroll view) |
Documentation Scope
Section titled “Documentation Scope”This page documents the apple-text-appkit-vs-uikit decision skill. Use it when the main task is choosing the right Apple text API, view, or architecture.
Related
Section titled “Related”apple-text-views: Use when the main task is choosing the right Apple text view or deciding whether a problem belongs in SwiftUI text, UIKit/AppKit text views, or TextKit mode. Reach for this when comparing capabilities and tradeoffs, not when implementing a specific wrapper or low-level API.apple-text-representable: Use when embedding UITextView or NSTextView inside SwiftUI and the hard part is wrapper behavior: two-way binding, focus, sizing, cursor preservation, update loops, toolbars, or environment bridging. Reach for this when native SwiftUI text views are not enough, not when choosing between text stacks at a high level.apple-text-writing-tools: Use when integrating Writing Tools into a native or custom text editor, configuring writingToolsBehavior, adopting UIWritingToolsCoordinator, protecting ranges, or debugging why Writing Tools do not appear. Reach for this when the problem is specifically Writing Tools, not generic editor debugging.
Full SKILL.md source
---name: apple-text-appkit-vs-uikitdescription: Use when comparing NSTextView and UITextView capabilities, porting editor behavior between macOS and iOS, or deciding whether an AppKit-only text feature has a UIKit equivalent. Reach for this when the main task is platform capability tradeoffs, not TextKit 1 vs TextKit 2 choice.license: MIT---
# AppKit vs UIKit Text Capabilities
Use this skill when the main question is which platform text stack can support a capability or editor behavior.
## When to Use
- You are comparing `NSTextView` and `UITextView` capabilities.- You are porting text code between macOS and iOS.- You need to know whether a feature gap is architectural or just an API difference.
## Quick Decision
- Desktop document-editor features, text tables, rulers, or services -> AppKit- Touch-first editing, modular interactions, or iOS-specific selection UI -> UIKit- Unsure whether the feature is platform-only or text-view-only -> keep reading
## Core Guidance
## NSTextView Can Do — UITextView Cannot
### Rich Text Editing Panels (AppKit Only)
NSTextView provides built-in formatting panels with no UIKit equivalent:
| Panel | API | Purpose ||-------|-----|---------|| Font Panel | `usesFontPanel` | System Fonts window syncs with selection || Ruler | `usesRuler` / `isRulerVisible` | Interactive paragraph formatting (margins, tabs) || Link Panel | `orderFrontLinkPanel:` | Insert/edit hyperlinks || List Panel | `orderFrontListPanel:` | Configure list formatting || Table Panel | `orderFrontTablePanel:` | Insert/manipulate text tables || Spacing Panel | `orderFrontSpacingPanel:` | Paragraph spacing configuration || Substitutions Panel | `orderFrontSubstitutionsPanel:` | Smart quotes, dashes, text replacement config |
### Text Tables (NSTextTable / NSTextTableBlock)
**AppKit-only classes.** No UIKit equivalent exists.
```swiftlet table = NSTextTable()table.numberOfColumns = 3
let cell = NSTextTableBlock(table: table, startingRow: 0, rowSpan: 1, startingColumn: 0, columnSpan: 1)cell.backgroundColor = .lightGraycell.setWidth(1.0, type: .absoluteValueType, for: .border)
let style = NSMutableParagraphStyle()style.textBlocks = [cell]```
Features: row/column spanning, per-cell borders/background, padding/margin control, automatic or fixed layout. **Triggers TextKit 1 fallback.**
### Grammar Checking
```swift// AppKittextView.isGrammarCheckingEnabled = truetextView.toggleGrammarChecking(nil)
// UIKit — NO equivalent API// Grammar checking is system-level only, not exposed to developers```
### Text Completion System
```swift// AppKit — full completion infrastructuretextView.complete(nil) // Invoke completion popuptextView.isAutomaticTextCompletionEnabled = true
// Override for custom completions:override func completions(forPartialWordRange charRange: NSRange, indexOfSelectedItem index: UnsafeMutablePointer<Int>) -> [String]? { return ["completion1", "completion2"]}
// UIKit — NO built-in completion API// Must build custom using UITextInput + overlay views```
### Spell Checking (Granular Control)
| Feature | AppKit | UIKit ||---------|--------|-------|| Toggle continuous spell checking | `isContinuousSpellCheckingEnabled` | `spellCheckingType` (.yes/.no) || Mark specific range as misspelled | `setSpellingState(_:range:)` | Not available || Document tag management | `spellCheckerDocumentTag()` | Not available || Grammar checking | `isGrammarCheckingEnabled` | Not available |
### Smart Substitutions (Individual Toggle APIs)
| Feature | AppKit API | UIKit Equivalent ||---------|-----------|-----------------|| Smart quotes | `isAutomaticQuoteSubstitutionEnabled` | `smartQuotesType` (similar) || Smart dashes | `isAutomaticDashSubstitutionEnabled` | `smartDashesType` (similar) || Text replacement | `isAutomaticTextReplacementEnabled` | System-level only || Auto-correction | `isAutomaticSpellingCorrectionEnabled` | `autocorrectionType` (similar) || Link detection | `isAutomaticLinkDetectionEnabled` | `dataDetectorTypes` (different API) || Data detection | `isAutomaticDataDetectionEnabled` | `dataDetectorTypes` (different API) |
AppKit provides individual toggle actions and a Substitutions Panel for user configuration. UIKit has simpler enum properties.
### Services Menu Integration
NSTextView automatically participates in the macOS Services menu (send selected text to other apps):
```swift// Automatic for NSTextView — implements NSServicesMenuRequestor// Services like "Look Up in Dictionary", "Send via Mail" appear in Services menu
// For custom views:override func validRequestor(forSendType sendType: NSPasteboard.PasteboardType?, returnType: NSPasteboard.PasteboardType?) -> Any? { if sendType == .string { return self } return super.validRequestor(forSendType: sendType, returnType: returnType)}```
**iOS has no Services concept.**
### Field Editor Architecture
Unique to AppKit. One shared NSTextView per window handles all NSTextField editing:
```swift// Get the field editorlet fieldEditor = window.fieldEditor(true, for: textField) as? NSTextView
// Custom field editorfunc windowWillReturnFieldEditor(_ sender: NSWindow, to client: Any?) -> Any? { if client is MySpecialTextField { return myCustomFieldEditor } return nil}```
**Key implications:**- Memory efficient (one editor for all fields)- One TK1 fallback in ANY field editor affects ALL fields in the window- Custom field editors can provide per-field customization
**UIKit has no field editor.** Each UITextField manages its own editing.
### NSText Heritage (Direct RTF/RTFD)
NSTextView inherits from NSText, providing:
```swift// Direct RTF/RTFD I/Olet rtfData = textView.rtf(from: range)let rtfdData = textView.rtfd(from: range)textView.replaceCharacters(in: range, withRTF: rtfData)textView.replaceCharacters(in: range, withRTFD: rtfdData)textView.writeRTFD(toFile: path, atomically: true)textView.readRTFD(fromFile: path)
// Font/ruler pasteboardtextView.copyFont(nil) // Copy font attributestextView.pasteFont(nil) // Apply font from pasteboardtextView.copyRuler(nil) // Copy paragraph attributestextView.pasteRuler(nil) // Apply paragraph attributes
// SpeechtextView.startSpeaking(nil)textView.stopSpeaking(nil)```
**UIKit has none of these.** You'd need to manually create RTF data, manage font pasteboards, or use AVSpeechSynthesizer.
### Print Support
```swift// AppKit — native print support via NSViewtextView.printView(nil) // Opens print dialog
// UIKit — separate print systemlet formatter = UISimpleTextPrintFormatter(attributedText: textView.attributedText)let controller = UIPrintInteractionController.sharedcontroller.printFormatter = formattercontroller.present(animated: true)```
**macOS Dark Mode gotcha:** `printView(nil)` renders with the current appearance. In Dark Mode, this produces white text on white paper. Fix: create an off-screen text view sharing the same `NSTextStorage` with `.appearance = NSAppearance(named: .aqua)`, and print from that.
**iOS printing tiers:** `UISimpleTextPrintFormatter` handles most cases. For custom headers/footers or multi-section documents, subclass `UIPrintPageRenderer` and compose formatters per page range.
### Find and Replace
| Feature | AppKit | UIKit ||---------|--------|-------|| API | `NSTextFinder` (since OS X 10.7) | `UIFindInteraction` (since iOS 16) || Properties | `usesFindBar`, `usesFindPanel` | `findInteraction`, `isFindInteractionEnabled` || Incremental search | ✅ Built-in | ✅ Built-in || Custom providers | `NSTextFinderClient` protocol | `UITextSearching` protocol |
Both platforms now support find, but AppKit's has existed much longer with more customization.
## UITextView Can Do — NSTextView Cannot
### Data Detector Types (Declarative)
```swift// UIKit — single property, granular type selectiontextView.dataDetectorTypes = [.link, .phoneNumber, .address, .calendarEvent, .shipmentTrackingNumber, .flightNumber]// Detected items become tappable automatically// Only works when isEditable = false
// AppKit — Boolean toggles, less granulartextView.isAutomaticLinkDetectionEnabled = truetextView.isAutomaticDataDetectionEnabled = true// Requires explicit checkTextInDocument: call```
### UITextInteraction (Modular Gestures)
```swift// UIKit — add system text gestures to ANY UIViewlet interaction = UITextInteraction(for: .editable)interaction.textInput = customView // Any UITextInput conformercustomView.addInteraction(interaction)
// AppKit — no equivalent modular component// NSTextView has built-in gestures, but they can't be extracted```
### Text Item Interactions (iOS 17+)
```swift// UIKit — rich interaction with links, attachments, tagged rangesfunc textView(_ textView: UITextView, primaryActionFor textItem: UITextItem, defaultAction: UIAction) -> UIAction? { // Customize tap behavior}
func textView(_ textView: UITextView, menuConfigurationFor textItem: UITextItem, defaultMenu: UIMenu) -> UITextItem.MenuConfiguration? { // Custom context menu}
// Tag arbitrary ranges for interactionlet attrs: [NSAttributedString.Key: Any] = [ .uiTextItemTag: "myCustomTag"]
// AppKit — only has textView(_:clickedOnLink:at:)```
### UITextSelectionDisplayInteraction (iOS 17+)
System selection UI for custom views — cursor, handles, highlights:
```swiftlet selectionDisplay = UITextSelectionDisplayInteraction(textInput: myView)myView.addInteraction(selectionDisplay)selectionDisplay.setNeedsSelectionUpdate()```
AppKit gained `NSTextInsertionIndicator` (macOS Sonoma) for the cursor only, but nothing as comprehensive.
### UITextLoupeSession (iOS 17+)
```swiftlet session = UITextLoupeSession.begin(at: point, from: cursorView, in: self)session.move(to: newPoint)session.invalidate()```
No AppKit equivalent — macOS doesn't use a loupe for text selection.
## Architecture Differences
### Inheritance
```AppKit: NSObject → NSResponder → NSView → NSText → NSTextView (Rich NSText base: RTF, font panel, ruler, field editor, speech)
UIKit: NSObject → UIResponder → UIView → UIScrollView → UITextView (Scrolling built-in, but no text-specific base class)```
### Scrolling
| | AppKit | UIKit ||-|--------|-------|| Built-in scroll | ❌ Must embed in NSScrollView | ✅ IS a UIScrollView || Convenience | `NSTextView.scrollableTextView()` | Always scrollable || Non-scrolling | NSTextView is non-scrolling by default | Set `isScrollEnabled = false` |
### Text Storage Access
| | AppKit | UIKit ||-|--------|-------|| Property | `textStorage: NSTextStorage?` (optional) | `textStorage: NSTextStorage` (non-optional) || Full content | `attributedString()` method | `attributedText` property |
### Delegate Richness
NSTextViewDelegate is significantly richer than UITextViewDelegate:- Modify selection during changes- Intercept link clicks- Customize drag operations- Control tooltip display- Completion handling
UITextViewDelegate is minimal, though iOS 17 text item interactions narrowed the gap.
### Writing Tools (Equivalent)
Both platforms have identical Writing Tools support as of iOS 18 / macOS 15:- `writingToolsBehavior` / `writingToolsAllowedInputOptions`- `isWritingToolsActive`- Matching delegate methods- Both require TextKit 2 for full inline experience
## Quick Decision Guide
| Need | Platform ||------|----------|| Text tables | AppKit only (NSTextTable) || Grammar checking API | AppKit only || Text completion API | AppKit only || Services menu | AppKit only (macOS concept) || Font panel integration | AppKit only || Interactive ruler | AppKit only || Direct RTF file I/O | AppKit only (NSText heritage) || Declarative data detectors | UIKit better (dataDetectorTypes) || Modular text interaction | UIKit only (UITextInteraction) || Text item context menus | UIKit only (iOS 17+) || Selection display component | UIKit only (UITextSelectionDisplayInteraction) || Multi-page/multi-column | AppKit better (historically) || Built-in scrolling | UIKit (IS a scroll view) |
## Related Skills
- Use `/skill apple-text-views` when the real question is which view class to adopt.- Use `/skill apple-text-representable` when SwiftUI wrapping is part of the platform decision.- Use `/skill apple-text-writing-tools` for Apple Intelligence editor differences.