Swift-FocusEngine-Agent-Skill
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Pass
- Code scan — Scanned 4 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
This tool is an AI agent skill designed to help AI coding assistants (like Cursor, Copilot, and Claude) write correct focus management code across Apple platforms, including iOS, tvOS, and macOS. It provides specialized instructions to prevent the common mistakes LLMs typically make when interacting with Apple's focus engine APIs.
Security Assessment
The overall risk is Low. The repository does not request any dangerous permissions or execute shell commands. A lightweight code audit scanned four files and found no dangerous patterns, suspicious network requests, or hardcoded secrets. Because this tool acts primarily as a knowledge base and set of instructions for AI assistants rather than an executable script, its attack surface is minimal.
Quality Assessment
The project is actively maintained, with its most recent push happening just today. It is properly licensed under the permissive MIT license, making it safe for integration into commercial and personal projects. The main drawback is low community visibility, currently sitting at only 5 GitHub stars. However, the project demonstrates clear professionalism through comprehensive documentation, compatibility with major AI developer tools, and references to legitimate sources like WWDC sessions and real-world production apps.
Verdict
Safe to use — a well-documented, actively maintained educational resource with no apparent security risks.
AI agent skill for Claude Code, Copilot, Cursor, Codex & Gemini — correct focus management for tvOS, iOS, macOS, visionOS & watchOS
Agent skill for focus management across all Apple platforms
Swift FocusEngine Pro is a free, open-source agent skill that helps AI coding assistants write correct focus management code for tvOS, iOS/iPadOS, watchOS, visionOS, and macOS. It covers SwiftUI, UIKit, AppKit, and RealityKit — targeting the mistakes LLMs actually make with Apple's focus engine.
Built from real-world experience shipping production tvOS apps, Apple developer documentation, WWDC sessions (2017-2025), and community best practices from Airbnb, Showmax, and others.
Works with Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, and any tool supporting the Agent Skills format.
Table of Contents
- Who This Is For
- Why Use an Agent Skill for Focus?
- Installing
- Using
- What It Covers
- Anti-Patterns It Catches
- FAQ
- Sources
- Complementary Skills
- Changelog
- Contributing
- License
Who This Is For
- tvOS developers — building apps where every interaction depends on the focus engine working correctly
- iOS/iPadOS developers — adding keyboard, game controller, or external display support with focus groups
- visionOS developers — navigating the differences between gaze, hover, and focus in spatial computing
- macOS developers — building keyboard-navigable apps with key view loops, focus rings, and menu commands
Why Use an Agent Skill for Focus?
Focus management on Apple platforms is one of the hardest things to get right — and one of the hardest things to debug when it breaks.
The focus engine is geometric, not hierarchical. It doesn't follow your view tree. When a user swipes right and focus jumps two rows away instead of to the next item, there's no error, no crash, no log — it just looks broken. The item wasn't perfectly vertically aligned, so the engine picked a different candidate. You'll spend hours in UIFocusDebugger before you figure out why.
Apple's documentation covers the APIs but not the real-world edge cases: what happens when you reload data and focus resets to the top, why .disabled() silently removes views from the focus chain on tvOS, why .focusSection() is the difference between a usable scroll view and chaos, or why onHover doesn't fire from eye gaze on visionOS.
LLMs generate focus code that compiles and looks reasonable — but breaks in ways you only discover on a real device with a Siri Remote in your hand. This skill is built from my experience getting focus to actually work in a complex, production tvOS app. Every anti-pattern in here is something I hit, debugged, and fixed.
Installing
Claude Code
# Global (all projects)
npx skills add https://github.com/mhaviv/Swift-FocusEngine-Agent-Skill --skill swift-focusengine-pro -g -y
# Project-level only
npx skills add https://github.com/mhaviv/Swift-FocusEngine-Agent-Skill --skill swift-focusengine-pro -y
Codex
npx skills add https://github.com/mhaviv/Swift-FocusEngine-Agent-Skill --skill swift-focusengine-pro --agent codex
Cursor
npx skills add https://github.com/mhaviv/Swift-FocusEngine-Agent-Skill --skill swift-focusengine-pro --agent cursor
GitHub Copilot
npx skills add https://github.com/mhaviv/Swift-FocusEngine-Agent-Skill --skill swift-focusengine-pro --agent github-copilot
Gemini CLI
npx skills add https://github.com/mhaviv/Swift-FocusEngine-Agent-Skill --skill swift-focusengine-pro --agent gemini
Other Agents
Any agent that supports the Agent Skills format can use this skill. See agentskills.io for instructions on adding skills to your agent.
Don't have Node installed?brew install node
Or download from nodejs.org.
Using
Claude Code
/swift-focusengine-pro Review this view for tvOS focus issues
Codex
$swift-focusengine-pro Check my SwiftUI code for focus anti-patterns
Cursor
/swift-focusengine-pro Review this view for tvOS focus issues
GitHub Copilot
/swift-focusengine-pro Review this view for tvOS focus issues
Gemini CLI
Use the swift-focusengine-pro skill to review my focus handling code
Any Agent
Use the Swift FocusEngine Pro skill to audit my project for focus management problems
Example Prompts
- "Why isn't the first item focused when my view appears?"
- "Focus is jumping to a completely different row when I swipe right — the items aren't perfectly aligned vertically"
- "How do I keep focus position after my data reloads?"
- "I added .disabled() to a button but now focus skips over the entire section"
- "What's the difference between gaze and focus on visionOS?"
- "My Digital Crown rotation stopped working after I reordered my view modifiers"
- "How do I make menu bar commands respond to whichever document window is focused?"
What It Covers
4,500+ lines of focus expertise across 14 reference files (v1.5)
| Reference | Platform | Coverage |
|---|---|---|
| anti-patterns.md | All | 29 critical mistakes: 17 original tvOS + 5 production tvOS + 7 macOS-specific |
| swiftui-focus.md | tvOS | @FocusState, focusSection, prefersDefaultFocus, AutoFocusManager pattern |
| uikit-focus.md | tvOS | UIFocusEnvironment, UIFocusGuide, shouldUpdateFocus, didUpdateFocus |
| ios-focus.md | iOS/iPadOS | SwiftUI + UIKit: focus groups, focusGroupIdentifier, UIFocusHaloEffect, keyboard nav, focusedValue, game controller, Stage Manager |
| watchos-focus.md | watchOS | SwiftUI: Digital Crown routing, sequential focus, Crown conflicts, .digitalCrownAccessory |
| visionos-focus.md | visionOS | SwiftUI + UIKit + RealityKit: gaze vs hover vs focus, HoverEffect, HoverEffectComponent |
| focus-styling.md | All | ButtonStyle + isFocused, FocusBorder, CABasicAnimation, CardButtonStyle, macOS focus ring styling |
| focus-restoration.md | All | Data reload handling, safe reload pattern, row offset tracking |
| layout-patterns.md | tvOS | Table-of-collections, sidebar+content, tab bar, hero+catalog |
| macos-focus.md | macOS | AppKit + SwiftUI: key view loop, focus ring, NSView focus APIs, focusedValue for menus, Mac Catalyst, Full Keyboard Access |
| realitykit-focus.md | visionOS | RealityKit entity hover, collision shapes, gestures, shader effects, mixed hierarchies |
| async-focus.md | All | @MainActor coordination, focus after data load, NavigationStack pop, Task cancellation |
| accessibility-focus.md | All | @AccessibilityFocusState, VoiceOver + focus, Full Keyboard Access, Switch Control, Reduce Motion |
| debugging.md | All | UIFocusDebugger, _whyIsThisViewNotFocusable, launch arguments, macOS first responder debugging |
Anti-Patterns It Catches
Blocking (must fix before ship)
.disabled()removes views from the focus chain on tvOS — gate the action inside the closure instead (.allowsHitTesting(false)is unreliable)- Missing
.focusSection()on horizontal ScrollViews — causes cross-row focus jumping in vertical layouts - Adding
.focusable()to Buttons or NavigationLinks — creates double-focus artifacts - Mixing SwiftUI and UIKit focus in the same hierarchy — focus environment conflicts
- Calling
reloadData()during animations — focus resets to the top of the screen - Using
frame.widthin focus transform calculations — dimensions change when focused setNeedsFocusUpdate()called from wrong environment — silently fails with no error- Setting
isUserInteractionEnabled = falseon headers/labels — removes them and their children from focus chain remembersLastFocusedIndexPath+ offscreenreloadData()— remembered index may no longer exist- Using
UIView.animatefor CALayer properties — animations won't work, useCABasicAnimation
Warning (should fix)
- Non-optional
@FocusStatewithfocused(_:equals:)— can't represent "nothing focused" state - Missing
prepareForReuse()cleanup for focus state — stale focus styling on reused cells prefersDefaultFocusinside ScrollView — may not work as expected, usedefaultFocusinstead- LazyVStack/LazyVGrid performance on Apple TV HD — A8 chip can't handle lazy layout recalculation during fast scrolling
tvOS production patterns
LazyVStackdeallocates offscreen rows — rapid upward swipe causes focus to jump to tab bar, skipping content- Missing
.focusSection()on vertical ScrollView — focus escapes upward to tab bar/nav bar - Allocating objects in
didUpdateFocus/shouldUpdateFocus— per-frame garbage causes micro-stutters
macOS-specific
- Not overriding
acceptsFirstResponderon custom NSView — view is invisible to Tab navigation - Incomplete key view loop — Tab stops working after reaching the last view
- Calling
becomeFirstResponder()directly — bypasses resign/become handshake, usewindow.makeFirstResponder - NSPanel stealing focus — inspector panels take focus from document window, use
becomesKeyOnlyIfNeeded - Not restoring focus after sheet/alert — focus lost to window instead of returning to original view
.focusable()on NSViewRepresentable — creates double focus layer conflicting with AppKit- Menu items not checking for nil focusedValue — crashes when no window is key
FAQ
How do I set initial focus on a specific view in tvOS?In SwiftUI, use defaultFocus(_:_:) or prefersDefaultFocus. In UIKit, override preferredFocusEnvironments on the parent view controller. See swiftui-focus.md and uikit-focus.md.
Use remembersLastFocusedIndexPath or the safe reload pattern that locks focus before reloading. See focus-restoration.md.
The focus engine is geometric, not hierarchical. Add .focusSection() to horizontal ScrollViews to keep focus within rows. See anti-patterns.md (pattern #2).
You cannot directly set focus. Override preferredFocusEnvironments to return the target, then call setNeedsFocusUpdate() + updateFocusIfNeeded() on the correct focus environment. See uikit-focus.md.
Common causes: guide not added to the view hierarchy, preferredFocusEnvironments not set on the guide, or incorrect sizing/positioning. Focus guides bridge empty space between focusable views. See uikit-focus.md.
It creates a focus group that the engine treats as a contiguous region, preventing focus from skipping over the section to items in other rows. Essential for horizontal ScrollViews in vertical layouts. See swiftui-focus.md.
How do I debug focus issues on tvOS?Use UIFocusDebugger.checkFocusability(for:) in the debugger, _whyIsThisViewNotFocusable on any UIView, and the UIFocusLoggingEnabled launch argument. See debugging.md.
On tvOS, .disabled() removes the view entirely from the focus chain. .allowsHitTesting(false) is commonly recommended but is unreliable — it may map to isUserInteractionEnabled = false under the hood. The most reliable approach is to gate the action inside the button closure instead of disabling the view. For lists/sidebars, use the dual @FocusState + .disabled() gating pattern (anti-pattern #25). See anti-patterns.md (patterns #1 and #25).
Setting @FocusState to nil should dismiss the keyboard, but it can fail inside sheets or NavigationStack. See ios-focus.md for workarounds.
Use @FocusState with an enum representing each field, then set the next case in onSubmit. See ios-focus.md.
iOS 15+ added UIFocusSystem support for hardware keyboards. Opt in with UIFocusHaloEffect, focusGroupIdentifier, and focusEffect. See ios-focus.md.
Each window scene has its own focus state. Use focusedSceneValue to propagate focus information across scenes. See ios-focus.md (Stage Manager section).
Define a FocusedValueKey, set values with .focusedSceneValue(), and read them with @FocusedValue in your menu bar or toolbar commands. See ios-focus.md.
The .digitalCrownRotation() modifier is order-sensitive. It must be applied in the correct position relative to other modifiers. See watchos-focus.md.
When a ScrollView contains a Digital Crown control, the Crown drives both scrolling and the control. Use explicit @FocusState to determine which element owns the Crown. See watchos-focus.md.
visionOS uses eye tracking for hover (.hoverEffect()) and indirect input for focus. They are separate systems. Gaze creates hover highlights, but tap gestures are needed for activation. See visionos-focus.md.
Use HoverEffectComponent on RealityKit entities with styles: default, spotlight, shader, or highlight. For SwiftUI views, use .hoverEffect(.highlight) or .hoverEffect(.lift). See realitykit-focus.md.
Mac Catalyst inherits iPad's UIFocusSystem — UIFocusHaloEffect renders as a macOS focus ring, and focusGroupIdentifier maps to Tab navigation groups. If your iPad app doesn't support keyboard focus, neither will the Catalyst version. See macos-focus.md (Mac Catalyst section).
Use @FocusState (same as iOS) and .focusable() for custom views. macOS focus is always active — no hardware keyboard requirement. For menu bar integration, use focusedValue / focusedSceneValue. See macos-focus.md.
The focus engine behaves differently between Xcode Simulator and physical hardware, especially for tvOS remote gestures and visionOS eye tracking. Always test focus on real devices. See debugging.md.
Sources
Built from:
- Apple Developer Documentation (UIFocusEnvironment, UIFocusGuide, FocusState, focusSection, HoverEffect)
- WWDC17: Focus Interaction in tvOS 11
- WWDC21: Direct and reflect focus in SwiftUI + Focus on iPad keyboard navigation
- WWDC23: The SwiftUI cookbook for focus
- WWDC24: Create custom hover effects in visionOS
- WWDC25: Design hover interactions for visionOS
- Production tvOS apps with complex focus requirements
- Community guides (Airbnb, Showmax, Fatbobman, Big Nerd Ranch)
Complementary Skills
Swift FocusEngine Pro pairs well with these skills:
- SwiftUI Pro by Paul Hudson — SwiftUI best practices and patterns
- Swift Concurrency Pro by Paul Hudson — async/await, actors, Sendable
- Swift Concurrency by Antoine van der Lee — Swift 6 migration, data race prevention
- Xcode Build Optimization by Antoine van der Lee — build benchmarking and optimization
See the Swift Agent Skills directory for more.
Changelog
See CHANGELOG.md for release history.
Contributing
Contributions are welcome! Focus on:
- Edge cases — non-obvious focus behaviors that catch developers off guard
- New platform APIs — iOS 19, tvOS 19, visionOS 3, watchOS 12, macOS 16 additions
- Real-world patterns — battle-tested solutions from production apps
- Anti-patterns — mistakes LLMs commonly generate
Keep reference files focused and under 300 lines each. Don't repeat things LLMs already know — focus on what they get wrong. All contributions must be MIT licensed.
Please read the Code of Conduct before contributing.
License
Swift FocusEngine Pro was created by Michael Haviv and is licensed under the MIT License.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found