Best for
- Use when managing @FocusState, defaultFocus, focused values, focusable interactions, focus sections, tvOS geometric focus and Siri Remote navigation, watchOS Digital Crown input, visionOS connected-device focus versus g…
dpearson2699/swift-ios-skills/skills/focus-engine/SKILL.md
Implements keyboard, directional, and scene-level focus behavior across SwiftUI and UIKit. Use when managing @FocusState, defaultFocus, focused values, focusable interactions, focus sections, tvOS geometric focus and Siri Remote navigation, watchOS Digital Crown input, visionOS connected-device focus versus gaze hover/input targets, macOS key view loop and Full Keyboard Access, focus restoration after presentation changes, custom focus routing with UIFocusGuide, or debugging focus with UIFocusDe
Decision brief
Focus behavior for SwiftUI and UIKit apps targeting iOS 26+, iPadOS, macOS, tvOS, and visionOS connected-input paths. Covers keyboard focus, directional focus, scene-focused values, focus restoration, and UIKit focus guides. focusSection() guidance in this skill applies to macOS…
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill "skills/focus-engine"Inspect the Agent Skill "focus-engine" from https://github.com/dpearson2699/swift-ios-skills/blob/90c9573272531337962fbb3505036d61ed23389a/skills/focus-engine/SKILL.md at commit 90c9573272531337962fbb3505036d61ed23389a. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
[ ] @FocusState is local to the view that owns the controls
Use @FocusState to read and write focus placement inside a scene. Use Bool for a single target or an optional Hashable enum for multiple targets.
Use .defaultFocus to set the preferred initial focus region or control when a view appears or when focus is reassigned automatically.
Use focused values to expose state from the currently focused view. Use scene-focused values when commands or scene-wide UI should keep access to the value even after focus moves within that scene.
Use .focusable(:interactions:) on custom SwiftUI views that should participate in keyboard or directional focus.
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 67/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 933 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Focus behavior for SwiftUI and UIKit apps targeting iOS 26+, iPadOS, macOS, tvOS, and visionOS connected-input paths. Covers keyboard focus, directional focus, scene-focused values, focus restoration, and UIKit focus guides. focusSection() guidance in this skill applies to macOS and tvOS. visionOS gaze-driven hover is an input affordance, not focus. Accessibility-specific focus for VoiceOver and Switch Control lives in the ios-accessibility skill.
When a request mixes focus with accessibility or spatial input, keep the boundary explicit:
ios-accessibility.Use @FocusState to read and write focus placement inside a scene. Use Bool for a single target or an optional Hashable enum for multiple targets.
struct LoginView: View {
enum Field: Hashable { case email, password }
@State private var email = ""
@State private var password = ""
@FocusState private var focusedField: Field?
var body: some View {
Form {
TextField("Email", text: $email)
.focused($focusedField, equals: .email)
SecureField("Password", text: $password)
.focused($focusedField, equals: .password)
}
.onAppear { focusedField = .email }
.onSubmit {
switch focusedField {
case .email: focusedField = .password
case .password, nil: submit()
}
}
}
}
Keep focus state local to the view that owns the focusable controls.
Use .defaultFocus to set the preferred initial focus region or control when a view appears or when focus is reassigned automatically.
struct SidebarView: View {
enum Target: Hashable { case library, settings }
@FocusState private var focusedTarget: Target?
var body: some View {
VStack {
Button("Library") { }
.focused($focusedTarget, equals: .library)
Button("Settings") { }
.focused($focusedTarget, equals: .settings)
}
.defaultFocus($focusedTarget, .library)
}
}
Prefer one clear default destination per screen or focus region.
Use focused values to expose state from the currently focused view. Use scene-focused values when commands or scene-wide UI should keep access to the value even after focus moves within that scene.
struct SelectedRecipeKey: FocusedValueKey {
typealias Value = Binding<Recipe>
}
extension FocusedValues {
var selectedRecipe: Binding<Recipe>? {
get { self[SelectedRecipeKey.self] }
set { self[SelectedRecipeKey.self] = newValue }
}
}
struct RecipeDetailView: View {
@Binding var recipe: Recipe
var body: some View {
Text(recipe.title)
.focusedSceneValue(\.selectedRecipe, $recipe)
}
}
Use this pattern for menus, commands, and toolbars that need to act on the focused scene's current content.
Use .focusable(_:interactions:) on custom SwiftUI views that should participate in keyboard or directional focus.
struct SelectableCard: View {
let title: String
let action: () -> Void
@FocusState private var isFocused: Bool
var body: some View {
Button(action: action) {
RoundedRectangle(cornerRadius: 12)
.fill(isFocused ? Color.accentColor.opacity(0.15) : .clear)
.overlay { Text(title) }
}
.buttonStyle(.plain)
.focusable(interactions: .activate)
.focused($isFocused)
}
}
Prefer semantic Button, Toggle, TextField, and other system controls before making arbitrary gesture-driven views focusable. Use .focusable(interactions: .activate) for custom button-like controls only when a semantic control cannot express the UI. Reserve broader interactions for views that genuinely need editing or multiple focus-driven behaviors.
Use focusSection() on macOS 13+ and tvOS 15+ to guide directional movement across groups of focusable descendants in uneven layouts.
struct TVLibraryView: View {
var body: some View {
HStack {
VStack {
Button("Recent") { }
Button("Favorites") { }
Button("Downloaded") { }
}
.focusSection()
VStack {
Button("Featured") { }
Button("Top Picks") { }
Button("Continue Watching") { }
}
.focusSection()
}
}
}
Use focus sections on macOS and tvOS when default left/right or up/down movement skips the intended group.
After dismissing a sheet, popover, or transient overlay, return focus to a stable trigger or logical next target.
struct FiltersView: View {
@State private var showSheet = false
@FocusState private var isFilterButtonFocused: Bool
var body: some View {
Button("Filters") { showSheet = true }
.focused($isFilterButtonFocused)
.sheet(isPresented: $showSheet) {
FilterEditor()
.onDisappear {
Task { @MainActor in
isFilterButtonFocused = true
}
}
}
}
}
Restore focus intentionally whenever presentation changes would otherwise leave users disoriented.
Use UIFocusGuide when UIKit or tvOS layouts need custom routing across empty space or awkward geometry.
final class DashboardViewController: UIViewController {
private let focusGuide = UIFocusGuide()
@IBOutlet private weak var leadingButton: UIButton!
@IBOutlet private weak var trailingButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
view.addLayoutGuide(focusGuide)
focusGuide.preferredFocusEnvironments = [trailingButton]
NSLayoutConstraint.activate([
focusGuide.leadingAnchor.constraint(equalTo: leadingButton.trailingAnchor),
focusGuide.trailingAnchor.constraint(equalTo: trailingButton.leadingAnchor),
focusGuide.topAnchor.constraint(equalTo: leadingButton.topAnchor),
focusGuide.bottomAnchor.constraint(equalTo: leadingButton.bottomAnchor)
])
}
}
UIFocusGuide is invisible and not a view. Use it to redirect focus without adding decorative UI.
@FocusState in shared models instead of the owning view..focusable() on decorative views.UIFocusGuide before trying focusSection() on macOS or tvOS, or better layout grouping in SwiftUI.Button when possible.@FocusState is local to the view that owns the controlsfocusedSceneValue or related focused-value APIs are used when commands need current scene statefocusSection() is used for uneven directional layouts on macOS or tvOS before dropping to UIKitUIFocusGuide geometry and preferred destinations match the intended routeios-accessibility, not mixed into keyboard-directional focus logic