swift-focusengine-pro
Installation
SKILL.md
Review focus management code for correctness, modern API usage, and adherence to Apple's focus engine rules. Covers all Apple platforms. Report only genuine problems — do not nitpick or invent issues.
Review process:
- Check for critical anti-patterns using
references/anti-patterns.md. - Determine the target platform and load the appropriate references:
- tvOS:
references/swiftui-focus.mdandreferences/uikit-focus.md. - iOS/iPadOS:
references/ios-focus.md(focus groups, halo, keyboard nav). - watchOS:
references/watchos-focus.md(Digital Crown, sequential focus). - visionOS:
references/visionos-focus.md(gaze, hover effects) andreferences/realitykit-focus.md(RealityKit entities, gestures, volumes). - macOS:
references/macos-focus.md(key view loop, focus ring, NSView focus, focusedValue for menus, Mac Catalyst). - For cross-platform: load all relevant references.
- tvOS:
- Check focus styling and visual feedback using
references/focus-styling.md. - Verify focus restoration and data reload handling using
references/focus-restoration.md. - Audit layout patterns for focus section isolation using
references/layout-patterns.md. - Check async/await and data loading focus patterns using
references/async-focus.md. - Verify accessibility integration using
references/accessibility-focus.md. - Check debugging and testing practices using
references/debugging.md.
If doing a partial review, load only the relevant reference files.
Core Instructions
tvOS
- tvOS uses a focus-based navigation model — every interactive element must be reachable via the Siri Remote's directional pad.
- Focus movement is purely geometric — the focus engine draws a rectangle from the currently focused view in the swipe direction and picks the nearest focusable view in that rectangle.
- If nothing is in the geometric path, focus does not move. Period. Use
.focusSection()(SwiftUI) orUIFocusGuide(UIKit) to bridge gaps. - Never use
.disabled()on tvOS to toggle interactivity — it removes views from the focus chain entirely..allowsHitTesting(false)is unreliable on tvOS (may map toisUserInteractionEnabled = false). Preferred: gate the action inside the button closure, or use the dual@FocusState+.disabled()gating pattern for lists (anti-pattern #25). prefersDefaultFocus(_:in:)does NOT work insideScrollViewon tvOS — usedefaultFocus(_:_:priority:)instead. Note:defaultFocuswith.userInitiatedonly fires on initial appearance, NOT on every re-entry.- Prefer
ScrollPositionoverScrollViewReader.scrollTo()— imperative scrollTo creates feedback loops with the focus engine (anti-pattern #26). - Always test on real Apple TV hardware — Simulator focus behavior differs.
iOS/iPadOS
- Focus is a secondary interaction model — it only activates with a hardware keyboard connected.
- Tab moves between focus groups; arrow keys move within a group. This two-level model does NOT exist on tvOS.
- Use
focusGroupIdentifier(iOS 15+, UIKit) to define custom focus groups — this API is NOT available on tvOS. - Use
UIFocusHaloEffectto customize the system focus ring — NOT available on tvOS. - Set
allowsFocus = trueandselectionFollowsFocus = trueon collection/table views for keyboard navigation. - Your app must work perfectly without keyboard focus — always test with touch only.
watchOS
- Focus routes Digital Crown input to the correct view — shown by a green border.
- Focus is sequential (layout order), NOT spatial/directional.
.focusSection()is NOT available on watchOS..focusable()MUST come BEFORE.digitalCrownRotation()— reversing silently breaks crown input.- Do NOT add
.focusable()to system controls (Picker, Stepper, Toggle) — they already handle it.
visionOS
- Eye gaze = hover targeting, NOT focus.
onHover(perform:)does NOT fire from gaze — only from pointer devices. - Use
.hoverEffect()for gaze visual feedback. System controls get it automatically; custom views need it explicitly. @FocusStateonly activates with keyboard (Magic Keyboard), VoiceOver, or Switch Control.- RealityKit entities need
InputTargetComponent+CollisionComponent+HoverEffectComponentfor gaze interaction. .focusEffectDisabled()hides keyboard focus ring;.hoverEffectDisabled()disables gaze hover — they are different.
macOS
- macOS uses a key view loop — Tab/Shift-Tab moves between views in a defined sequence. This is NOT spatial like tvOS.
- Custom NSView subclasses must override
acceptsFirstResponderto returntrue— the default isfalse, making the view invisible to Tab navigation. recalculatesKeyViewLoop = trueon NSWindow overwrites all manualnextKeyViewconnections. Pick one approach.- Focus ring customization: override
focusRingType,focusRingMaskBounds, anddrawFocusRingMask()on NSView. focusedValue/focusedSceneValueare critical on macOS for making menu bar commands respond to the current selection.- Mac Catalyst: inherits iPad
UIFocusSystem. If the iPad app doesn't support keyboard focus, the Catalyst app won't either. - Full Keyboard Access is OFF by default — most users only Tab between text fields and lists, not all controls.
All Platforms
- Never add
.focusable()to Buttons or NavigationLinks — they are already focusable. Adding it creates a double-focus wrapper. - Do not mix
@FocusState(SwiftUI) and UIKit focus APIs (setNeedsFocusUpdate) on the same view hierarchy branch. - VoiceOver focus (
@AccessibilityFocusState) is completely separate from UI focus (@FocusState).
Output Format
Organize findings by file. For each issue:
- State the file and relevant line(s).
- Name the rule being violated (e.g., "Use
.allowsHitTesting(false)instead of.disabled()"). - Show a brief before/after code fix.
Skip files with no issues. End with a prioritized summary of the most impactful changes to make first.
Example output:
TopicsView.swift
Line 49: .disabled() removes view from tvOS focus chain (anti-pattern #1).
// Before
TopicClipsGridView(...)
.disabled(!wrapper.isGridFocusable)
// After — gate the action, not the view
TopicClipsGridView(...)
.opacity(wrapper.isGridFocusable ? 1.0 : 0.5)
// Move the guard inside the button/action closures instead
Line 72: Missing .focusSection() on horizontal ScrollView in vertical layout.
// Before
ScrollView(.horizontal) {
HStack { /* row items */ }
}
// After
ScrollView(.horizontal) {
HStack { /* row items */ }
}
.focusSection()
Summary
- Focus breakage (critical):
.disabled()on line 49 removes grid from focus chain entirely. - Focus jumping (high): Missing
.focusSection()causes cross-row focus jumps.
End of example.
References
references/anti-patterns.md— Critical mistakes that break focus navigation: 14 tvOS + 7 macOS-specific anti-patterns.references/swiftui-focus.md— SwiftUI focus APIs: @FocusState, focusSection, prefersDefaultFocus, focused, defaultFocus, onMoveCommand.references/uikit-focus.md— UIKit focus APIs: UIFocusEnvironment, UIFocusGuide, shouldUpdateFocus, didUpdateFocus, preferredFocusEnvironments, UIFocusDebugger.references/focus-styling.md— Focus visual feedback: ButtonStyle with isFocused, FocusBorder, hover effects, scale/shadow animations, macOS focus ring styling.references/focus-restoration.md— Handling focus after data reloads, navigation, and async updates.references/layout-patterns.md— Common tvOS layouts: table-of-collections, sidebar+content, tab bar, horizontal shelves.references/ios-focus.md— iOS/iPadOS-specific: focus groups, focusGroupIdentifier, UIFocusHaloEffect, keyboard navigation, allowsFocus, selectionFollowsFocus.references/watchos-focus.md— watchOS-specific: Digital Crown routing, sequential focus, digitalCrownRotation, focusable ordering.references/visionos-focus.md— visionOS-specific: gaze vs focus vs hover, HoverEffect, HoverEffectGroup, RealityKit HoverEffectComponent, spatial input.references/macos-focus.md— macOS-specific: key view loop, NSView focus (acceptsFirstResponder, canBecomeKeyView), focus ring customization, focusedValue for menus, Mac Catalyst, Full Keyboard Access.references/realitykit-focus.md— RealityKit entity hover: HoverEffectComponent, collision shapes, gestures, shader effects, mixed SwiftUI+RealityKit hierarchies.references/async-focus.md— Async focus patterns: @MainActor coordination, focus after data load, NavigationStack pop, Task cancellation, debouncing.references/accessibility-focus.md— Accessibility integration: @AccessibilityFocusState, VoiceOver + focus, Full Keyboard Access, Switch Control, Reduce Motion.references/debugging.md— UIFocusDebugger, _whyIsThisViewNotFocusable, launch arguments, Quick Look, macOS first responder debugging.
Weekly Installs
33
Repository
mhaviv/swift-fo…nt-skillGitHub Stars
7
First Seen
9 days ago
Security Audits
Installed on
codex31
cursor29
kimi-cli26
deepagents26
antigravity26
claude-code26