txt-spell-autocorrect — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited txt-spell-autocorrect (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Authored against iOS 26.x / Swift 6.x / Xcode 26.x.
This skill covers the configuration knobs on stock text views, the UITextChecker/NSSpellChecker services for custom logic, and the gotcha that derails almost every custom-UITextInput editor: the correction popover invokes a private API (UITextReplacement) to apply selected suggestions, so a fully-custom view can show red underlines but cannot apply the correction without rejection-grade private symbols. Before claiming a specific autocorrect property, fetch the current API via Sosumi (sosumi.ai/documentation/uikit/uitextinputtraits) — the trait set has grown across iOS 17, 18, and 26.
If the user's editor is UITextView or NSTextView, the work is almost always configuration. If it's a custom UITextInput view, the answer is structurally different and the trap section below matters more than the configuration section.
UITextView and NSTextView get spell check, autocorrect, smart quotes/dashes, and (on iOS 17+) inline prediction for free. Configuration is via properties.
// iOS — UITextInputTraits
textView.spellCheckingType = .yes // .default | .no | .yes
textView.autocorrectionType = .yes
textView.autocapitalizationType = .sentences
textView.smartQuotesType = .yes
textView.smartDashesType = .yes
textView.smartInsertDeleteType = .yes
textView.inlinePredictionType = .yes // iOS 17+
// macOS — explicit per-feature toggles
textView.isContinuousSpellCheckingEnabled = true
textView.isGrammarCheckingEnabled = true // macOS only
textView.isAutomaticSpellingCorrectionEnabled = true
textView.isAutomaticQuoteSubstitutionEnabled = true
textView.isAutomaticDashSubstitutionEnabled = true
textView.isAutomaticTextReplacementEnabled = true
textView.isAutomaticTextCompletionEnabled = true
textView.isAutomaticLinkDetectionEnabled = true
textView.isAutomaticDataDetectionEnabled = trueFor code editors, the right configuration is "disable everything that mangles literal text":
// iOS
textView.spellCheckingType = .no
textView.autocorrectionType = .no
textView.autocapitalizationType = .none
textView.smartQuotesType = .no
textView.smartDashesType = .no
textView.smartInsertDeleteType = .no
textView.inlinePredictionType = .no
// macOS
textView.isContinuousSpellCheckingEnabled = false
textView.isAutomaticSpellingCorrectionEnabled = false
textView.isAutomaticQuoteSubstitutionEnabled = false
textView.isAutomaticDashSubstitutionEnabled = false
textView.isAutomaticTextReplacementEnabled = false
textView.isAutomaticTextCompletionEnabled = falseTrait changes on iOS only take effect when the view is not first responder. If users see no change after toggling autocorrectionType, resign first responder, change the trait, then become first responder again.
This is the part of the skill most worth slowing down for.
A custom view that adopts UITextInput and adds a UITextInteraction will get spell-check underlines for free. The system detects misspellings, draws red underlines, and shows a correction popover when the user taps an underlined word. So far so good.
The trap is what happens when the user taps a suggestion in that popover. The popover dispatches the correction through UITextReplacement — a private API. A fully custom view has no public way to receive that callback. The result:
There are three workable responses. First, disable the system spell check on your custom view (spellCheckingType = .no, autocorrectionType = .no) and run your own checker UI built on UITextChecker. Second, leave system check enabled but accept that inline corrections won't apply on iOS — users use the system Spelling panel on macOS, and on iOS they edit by hand. Third, abandon the custom view and use UITextView, which has the private bridging that custom views lack.
For the build-your-own path:
let checker = UITextChecker()
let nsText = text as NSString
let range = NSRange(location: 0, length: nsText.length)
let misspelled = checker.rangeOfMisspelledWord(
in: text, range: range, startingAt: 0,
wrap: false, language: "en"
)
if misspelled.location != NSNotFound {
let guesses = checker.guesses(forWordRange: misspelled,
in: text, language: "en") ?? []
presentCustomCorrectionUI(at: misspelled, guesses: guesses)
}Custom correction UI applies the replacement through your normal edit path, so undo, autocorrect, and inputDelegate notifications all flow correctly.
UITextChecker is a standalone, view-independent spell checker. It works on iOS and macOS. Use it for find-misspelled-word loops, custom autocomplete, and the build-your-own path above.
let checker = UITextChecker()
// Misspelled word scan
var offset = 0
let nsText = text as NSString
let fullRange = NSRange(location: 0, length: nsText.length)
while offset < nsText.length {
let r = checker.rangeOfMisspelledWord(in: text, range: fullRange,
startingAt: offset, wrap: false, language: "en")
if r.location == NSNotFound { break }
let word = nsText.substring(with: r)
let guesses = checker.guesses(forWordRange: r, in: text, language: "en") ?? []
print("'\(word)' → \(guesses)")
offset = r.location + r.length
}
// Word completion (alphabetical despite the docs)
let completions = checker.completions(
forPartialWordRange: NSRange(location: 0, length: 4),
in: "prog", language: "en"
) ?? []The completions API is documented as ranking by probability; it actually returns alphabetical order. If your UI claims "best suggestion first," you have to do that ranking yourself.
User dictionary additions persist across launches:
UITextChecker.learnWord("SwiftUI")
UITextChecker.hasLearnedWord("SwiftUI") // true
UITextChecker.unlearnWord("SwiftUI")Per-document ignore lists need a tag:
let tag = UITextChecker.uniqueSpellDocumentTag()
checker.ignoreWord("xyzzy", inSpellDocumentWithTag: tag)The macOS spell checker is a singleton and significantly more capable than UITextChecker. It supports grammar checking, async batch checks, and a unified check that bundles spelling, grammar, and data detection.
let spell = NSSpellChecker.shared
// Async, large-document checking
let tag = NSSpellChecker.uniqueSpellDocumentTag()
spell.requestChecking(
of: text,
range: NSRange(location: 0, length: (text as NSString).length),
types: NSTextCheckingAllTypes,
options: nil,
inSpellDocumentWithTag: tag
) { _, results, _, _ in
DispatchQueue.main.async { self.applyResults(results) }
}For a custom AppKit view, you can set the spelling state attribute on a range and the system draws the misspelling underline:
textView.setSpellingState(NSSpellingStateSpellingFlag, range: misspelledRange)Close document tags when the document closes — they hold per-document ignore lists in memory:
spell.closeSpellDocument(withTag: tag)NSSpellChecker itself is main-thread-only despite the async-feeling requestChecking callback. Treat it as you would a UI singleton.
For a custom UITextInput view to get autocorrect at all, four things have to be true:
UITextInputTraits properties (spellCheckingType, autocorrectionType) that are .yes or .default.inputDelegate?.textWillChange(self) and textDidChange(self) around every text mutation, and selectionWillChange(self) / selectionDidChange(self) around every selection change. Skipping these silently desyncs the autocorrect cache — there's no error and no crash, autocorrect just stops surfacing suggestions.caretRect(for:) and firstRect(for:) return correct geometry. The autocorrect bubble and spell-check popover are positioned by these rects.UITextInteraction is added to the view. The interaction supplies the gesture recognizers that trigger the correction popover.When autocorrect "stops working" in a custom view, walk this list. Trait wrong, inputDelegate skipped, geometry wrong, or interaction missing — almost always one of those four. (And if all four are right, the correction trap above kicks in.)
| Capability | iOS UITextView | macOS NSTextView |
|---|---|---|
| Continuous spell check | spellCheckingType enum | isContinuousSpellCheckingEnabled Bool |
| Grammar check | not exposed | isGrammarCheckingEnabled |
| Mark a range misspelled | not exposed | setSpellingState(_:range:) |
| Async batch check | not exposed | requestChecking(...) |
| Spelling panel | not exposed | orderFrontSpellingPanel(_:) |
| Substitutions panel | not exposed | orderFrontSubstitutionsPanel(_:) |
| Inline prediction | iOS 17+ trait | not exposed |
| Spell-check pre-existing text | only near edits | full document |
The grammar/panel features simply don't exist on iOS. If a feature in this table is iOS-blank, it isn't a missing API call — it's not a public capability.
UITextView.let into Let, and smart quotes break string literals. For code editors, disable every trait that rewrites characters.NSSpellChecker.shared until the process exits.requestChecking is a wrapper; you still consume results on main.txt-uitextinput — full UITextInput protocol surface, inputDelegate notifications, the marked-text lifecycle that autocorrect coordinates withtxt-selection-menus — UITextInteraction setup, edit menu, gesture coordinationtxt-view-picker — picker between custom UITextInput, UITextView, and SwiftUI text fields~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.