Why Your onExitCommand Isn't Firing on tvOS

February 1, 2026 Reading: 2 min

I was debugging a video player screen on tvOS. The flow was simple enough: static screen → video playing → controls hidden → controls reappearing on menu button press. Each press steps back through these states until the user exits.

Except the user was exiting immediately. My carefully orchestrated state machine was being ignored entirely, the modal dismissed without ceremony.

Looking in All the Wrong Places

I had written a custom UIKit key input interceptor for this screen—the kind of code you write once, forget about, and blame first when things break. So I blamed it. Tweaked it. Added logs. Stared at it. Nothing.

Eventually I resorted to the debugging technique they don’t teach you in school: commenting things out until the problem goes away. Turns out I’d recently changed a .disabled() modifier. Reverting it fixed everything.

The connection between “disabled modifier” and “menu button stops working” was not immediately obvious.

What Apple Forgot to Document

After some digging, I found what the official documentation gracefully omits.

onExitCommand has two requirements that nobody tells you about. First: something in your view hierarchy must be focusable. No focusable views, no callback. The system shrugs and handles the menu button itself—by dismissing your screen.

Second: it’s an interceptor, not a listener. When onExitCommand fires, you’ve taken ownership of the exit behavior. The default “go back” doesn’t happen. That’s on you now.

My .disabled() change had inadvertently removed all focusable views. With nothing to focus, SwiftUI decided my onExitCommand was decorative.

The Workaround Nobody Should Need

The fix is almost insulting in its simplicity: add an invisible button.

.background(Button("") { })
.onExitCommand {
    // your state transition logic
}

A ghost button, sitting in the background, doing nothing except making the focus engine acknowledge that yes, this view exists and would like to receive events, thank you.

The focus engine prioritizes overlaid views, so this phantom button won’t steal focus during normal use. It’s purely there to keep onExitCommand on life support.

I’ve extracted this into a component in case another screen needs menu interception. It passed QA. Nobody asked why there’s an empty button in the view hierarchy, and I didn’t volunteer the information.

The Point

onExitCommand requires a focusable view to fire. Without one, your modifier is silently ignored. Apple’s documentation doesn’t mention this.

Now you know.