Back to the catalog

swift-focusengine-pro

Expert focus management guidance for all Apple platforms (tvOS, iOS/iPadOS, watchOS, visionOS). Covers SwiftUI, UIKit, and RealityKit focus

Open source Repository Open in the app JSON README (API)

About

Expert focus management guidance for all Apple platforms (tvOS, iOS/iPadOS, watchOS, visionOS). Covers SwiftUI, UIKit, and RealityKit focus APIs including @FocusState, UIFocusEnvironment, UIFocusGuide, focusSection, HoverEffect, Digital Crown routing, and 14 critical anti-patterns that break focus navigation. Built from real-world experience shipping production tvOS apps. 2,100+ lines of reference material across 10 files covering focus styling, focus restoration, layout patterns, and debugging techniques.

Details

Kind
Plugins
Topic
Developer tools
Publisher
mhaviv
Origin
marketplace
Category
ferramentas
Stars
20
Last push
2026-08-31T00:53:13Z
Repository state
ativo
License
MIT
Added
2026-08-30 01:48:58
Updated
2026-08-30 01:48:58
Origin id
mhaviv/swift-focusengine-agent-skill/swift-focusengine-pro

README

<p align="center">
  <img src="assets/logo.svg" height="180" alt="Swift FocusEngine Pro" />
</p>

<h3 align="center">Agent skill for focus management across all Apple platforms</h3>

<p align="center">
  <img src="https://img.shields.io/badge/tvOS-15+-000000?logo=apple" />
  <img src="https://img.shields.io/badge/iOS-15+-000000?logo=apple" />
  <img src="https://img.shields.io/badge/watchOS-8+-000000?logo=apple" />
  <img src="https://img.shields.io/badge/visionOS-1+-000000?logo=apple" />
  <img src="https://img.shields.io/badge/macOS-12+-000000?logo=apple" />
  <img src="https://img.shields.io/badge/Swift-5.9+-F05138?logo=swift&logoColor=white" />
  <img src="https://img.shields.io/badge/License-MIT-blue" />
  <img src="https://img.shields.io/badge/version-1.8.1-brightgreen" />
</p>

<p align="center">
  <a href="https://skills.sh/mhaviv/Swift-FocusEngine-Agent-Skill">
    <img src="https://img.shields.io/badge/skills.sh-Listed-6C47FF?logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHRleHQgeD0iNCIgeT0iMTgiIGZvbnQtc2l6ZT0iMTYiIGZpbGw9IndoaXRlIj7wn5ugPC90ZXh0Pjwvc3ZnPg==&logoColor=white" />
  </a>
  <a href="https://github.com/twostraws/Swift-Agent-Skills">
    <img src="https://img.shields.io/badge/Swift_Agent_Skills-Listed-F05138?logo=swift&logoColor=white" />
  </a>
  <a href="https://www.awesomeskills.dev/en/skill/mhaviv-swift-focusengine-agent-skill">
    <img src="https://img.shields.io/badge/awesomeskills.dev-Listed-10B981" />
  </a>
  <a href="https://github.com/mhaviv/Swift-FocusEngine-Agent-Skill/stargazers">
    <img src="https://img.shields.io/github/stars/mhaviv/Swift-FocusEngine-Agent-Skill?style=flat&logo=github&label=Stars" />
  </a>
</p>

<p align="center">
  <a href="https://x.com/michael_haviv">
    <img src="https://img.shields.io/badge/Contact-@michael__haviv-1DA1F2?logo=x&logoColor=white" />
  </a>
  <a href="https://www.linkedin.com/in/michaelhaviv/">
    <img src="https://img.shields.io/badge/LinkedIn-Michael_Haviv-0A66C2?logo=linkedin&logoColor=white" />
  </a>
</p>

---

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](https://claude.ai/code), [Codex](https://openai.com/codex), [Cursor](https://cursor.sh), [GitHub Copilot](https://github.com/features/copilot), [Gemini CLI](https://github.com/google-gemini/gemini-cli), and any tool supporting the [Agent Skills](https://agentskills.io) format.

## Table of Contents

- [Who This Is For](#who-this-is-for)
- [Why Use an Agent Skill for Focus?](#why-use-an-agent-skill-for-focus)
- [Installing](#installing)
- [Using](#using)
- [What It Covers](#what-it-covers)
- [Anti-Patterns It Catches](#anti-patterns-it-catches)
- [FAQ](#faq)
- [Sources](#sources)
- [Complementary Skills](#complementary-skills)
- [Changelog](#changelog)
- [Contributing](#contributing)
- [License](#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

```bash
# 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

```bash
npx skills add https://github.com/mhaviv/Swift-FocusEngine-Agent-Skill --skill swift-focusengine-pro --agent codex
```

### Cursor

```bash
npx skills add https://github.com/mhaviv/Swift-FocusEngine-Agent-Skill --skill swift-focusengine-pro --agent cursor
```

### GitHub Copilot

```bash
npx skills add https://github.com/mhaviv/Swift-FocusEngine-Agent-Skill --skill swift-focusengine-pro --agent github-copilot
```

### Gemini CLI

```bash
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](https://agentskills.io) format can use this skill. See [agentskills.io](https://agentskills.io) for instructions on adding skills to your agent.

<details>
<summary>Don't have Node installed?</summary>

```bash
brew install node
```

Or download from [nodejs.org](https://nodejs.org).
</details>

### Updating

Skills are installed as local copies — they don't auto-update. To pull the latest version:

```bash
# Update all installed skills
npx skills update -g -y

# Or reinstall this skill specifically
npx skills add https://github.com/mhaviv/Swift-FocusEngine-Agent-Skill --skill swift-focusengine-pro -g -y
```

⭐ **Star and Watch** this repo to get notified of new releases.

## 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

### 5,000+ lines of focus expertise across 14 reference files

| Reference | Platform | Coverage |
|-----------|----------|----------|
| **anti-patterns.md** | All | 30 critical mistakes: 17 original tvOS + 6 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)

1. **`.disabled()` removes views from the focus chain on tvOS** — gate the action inside the closure instead (`.allowsHitTesting(false)` is unreliable)
2. **Missing `.focusSection()` on horizontal ScrollViews** — causes cross-row focus jumping in vertical layouts
3. **Adding `.focusable()` to Buttons or NavigationLinks** — creates double-focus artifacts
4. **Mixing SwiftUI and UIKit focus in the same hierarchy** — focus environment conflicts
5. **Calling `reloadData()` during animations** — focus resets to the top of the screen
6. **Using `frame.width` in focus transform calculations** — dimensions change when focused
7. **`setNeedsFocusUpdate()` called from wrong environment** — silently fails with no error
8. **Setting `isUserInteractionEnabled = false` on headers/labels** — removes them and their children from focus chain
9. **`remembersLastFocusedIndexPath` + offscreen `reloadData()`** — remembered index may no longer exist
10. **Using `UIView.animate` for CALayer properties** — animations won't work, use `CABasicAnimation`

### Warning (should fix)

11. **Non-optional `@FocusState` with `focused(_:equals:)`** — can't represent "nothing focused" state
12. **Missing `prepareForReuse()` cleanup for focus state** — stale focus styling on reused cells
13. **`prefersDefaultFocus` inside ScrollView** — may not work as expected, use `defaultFocus` instead
14. **LazyVStack/LazyVGrid performance on Apple TV HD** — A8 chip can't handle lazy layout recalculation during fast scrolling

### tvOS production patterns

15. **`LazyVStack` deallocates offscreen rows** — rapid upward swipe causes focus to jump to tab bar, skipping content
16. **Missing `.focusSection()` on vertical ScrollView** — focus escapes upward to tab bar/nav bar
17. **Allocating objects in `didUpdateFocus`/`shouldUpdateFocus`** — per-frame garbage causes micro-stutters

### macOS-specific

18. **Not overriding `acceptsFirstResponder` on custom NSView** — view is invisible to Tab navigation
19. **Incomplete key view loop** — Tab stops working after reaching the last view
20. **Calling `becomeFirstResponder()` directly** — bypasses resign/become handshake, use `window.makeFirstResponder`
21. **NSPanel stealing focus** — inspector panels take focus from document window, use `becomesKeyOnlyIfNeeded`
22. **Not restoring focus after sheet/alert** — focus lost to window instead of returning to original view
23. **`.focusable()` on NSViewRepresentable** — creates double focus layer conflicting with AppKit
24. **Menu items not checking for nil focusedValue** — crashes when no window is key

## FAQ

<details>
<summary><strong>How do I set initial focus on a specific view in tvOS?</strong></summary>

In SwiftUI, use `defaultFocus(_:_:)` or `prefersDefaultFocus`. In UIKit, override `preferredFocusEnvironments` on the parent view controller. See [swiftui-focus.md](references/swiftui-focus.md) and [uikit-focus.md](references/uikit-focus.md).
</details>

<details>
<summary><strong>Focus resets after reloadData — how do I keep focus position?</strong></summary>

Use `remembersLastFocusedIndexPath` or the safe reload pattern that locks focus before reloading. See [focus-restoration.md](references/focus-restoration.md).
</details>

<details>
<summary><strong>Focus jumps to the wrong row when I swipe right</strong></summary>

The focus engine is geometric, not hierarchical. Add `.focusSection()` to horizontal ScrollViews to keep focus within rows. See [anti-patterns.md](references/anti-patterns.md) (pattern #2).
</details>

<details>
<summary><strong>How do I programmatically move focus?</strong></summary>

You cannot directly set focus. Override `preferredFocusEnvironments` to return the target, then call `setNeedsFocusUpdate()` + `updateFocusIfNeeded()` on the correct focus environment. See [uikit-focus.md](references/uikit-focus.md).
</details>

<details>
<summary><strong>UIFocusGuide not working</strong></summary>

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](references/uikit-focus.md).
</details>

<details>
<summary><strong>What does focusSection() actually do?</strong></summary>

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](references/swiftui-focus.md).
</details>

<details>
<summary><strong>How do I debug focus issues on tvOS?</strong></summary>

Use `UIFocusDebugger.checkFocusability(for:)` in the debugger, `_whyIsThisViewNotFocusable` on any UIView, and the `UIFocusLoggingEnabled` launch argument. See [debugging.md](references/debugging.md).
</details>

<details>
<summary><strong>Why does .disabled() break focus on Apple TV?</strong></summary>

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](references/anti-patterns.md) (patterns #1 and #25).
</details>

<details>
<summary><strong>@FocusState not dismissing keyboard on iOS</strong></summary>

Setting `@FocusState` to `nil` should dismiss the keyboard, but it can fail inside sheets or NavigationStack. See [ios-focus.md](references/ios-focus.md) for workarounds.
</details>

<details>
<summary><strong>How do I move focus between TextFields with the keyboard next button?</strong></summary>

Use `@FocusState` with an enum representing each field, then set the next case in `onSubmit`. See [ios-focus.md](references/ios-focus.md).
</details>

<details>
<summary><strong>How does keyboard focus navigation work on iPad?</strong></summary>

iOS 15+ added UIFocusSystem support for hardware keyboards. Opt in with `UIFocusHaloEffect`, `focusGroupIdentifier`, and `focusEffect`. See [ios-focus.md](references/ios-focus.md).
</details>

<details>
<summary><strong>How does focus work across multiple windows on iPad with Stage Manager?</strong></summary>

Each window scene has its own focus state. Use `focusedSceneValue` to propagate focus information across scenes. See [ios-focus.md](references/ios-focus.md) (Stage Manager section).
</details>

<details>
<summary><strong>How do I use focusedSceneValue for multi-window iPad apps?</strong></summary>

Define a `FocusedValueKey`, set values with `.focusedSceneValue()`, and read them with `@FocusedValue` in your menu bar or toolbar commands. See [ios-focus.md](references/ios-focus.md).
</details>

<details>
<summary><strong>Digital Crown rotation stops working after reordering view modifiers</strong></summary>

The `.digitalCrownRotation()` modifier is order-sensitive. It must be applied in the correct position relative to other modifiers. See [watchos-focus.md](references/watchos-focus.md).
</details>

<details>
<summary><strong>How do I handle nested scrolling conflicts with Digital Crown?</strong></summary>

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](references/watchos-focus.md).
</details>

<details>
<summary><strong>What's the difference between hover and focus on visionOS?</strong></summary>

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](references/visionos-focus.md).
</details>

<details>
<summary><strong>How do I customize hover effects in visionOS?</strong></summary>

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](references/realitykit-focus.md).
</details>

<details>
<summary><strong>How does focus work in Mac Catalyst apps?</strong></summary>

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](references/macos-focus.md) (Mac Catalyst section).
</details>

<details>
<summary><strong>How do I handle keyboard focus in a macOS SwiftUI app?</strong></summary>

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](references/macos-focus.md).
</details>

<details>
<summary><strong>Focus works in simulator but not on device (or vice versa)</strong></summary>

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](references/debugging.md).
</details>

## 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](https://github.com/twostraws/SwiftUI-Agent-Skill) by Paul Hudson — SwiftUI best practices and patterns
- [Swift Concurrency Pro](https://github.com/twostraws/Swift-Concurrency-Agent-Skill) by Paul Hudson — async/await, actors, Sendable
- [Swift Concurrency](https://github.com/AvdLee/Swift-Concurrency-Agent-Skill) by Antoine van der Lee — Swift 6 migration, data race prevention
- [Xcode Build Optimization](https://github.com/AvdLee/Xcode-Build-Optimization-Agent-Skill) by Antoine van der Lee — build benchmarking and optimization

See the [Swift Agent Skills](https://github.com/twostraws/Swift-Agent-Skills) directory for more.

## Changelog

See [CHANGELOG.md](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** — additions from the latest OS releases (iOS/tvOS/macOS/visionOS/watchOS 26 and newer)
- **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](CODE_OF_CONDUCT.md) before contributing.

## License

Swift FocusEngine Pro was created by [Michael Haviv](https://github.com/mhaviv) and is licensed under the [MIT License](LICENSE).

More