Passing control from one app to another with cooperative activation
Request focus for your app, and coordinate passing control from one app to another.
Overview
When someone uses your app and another app unexpectedly steals focus, the user experience can be compromised. Not only is it inconvenient for people to switch back to their original app, if they’re typing when the switch occurs, they might accidentally enter text into the wrong app, inadvertently disclosing sensitive information.
Cooperative activation addresses this problem by making app activation a request instead of a command. Instead of apps stealing focus, they now request focus from the system when they’re ready. This means apps should set expectations accordingly when requesting activation and not assume the system will grant them activation. The system dynamically determines whether to grant activation state based on the context. This lets you request focus for your app when needed, and hand off focus from one app to another.
Request activation to gain focus
When your app needs focus, call activate() on your app’s shared instance, or its shorthand version NSApp:
Calling this function sends a message to the system requesting activation for the app. Requesting activation doesn’t guarantee your app gains focus. People ultimately decide which apps gain focus by activating them through their device’s user interface. But if, after considering the user’s intent and larger system context, the system honors your app’s request, your app activates.
Transfer control from one app to another by yielding and then activating
Passing control from one app to another is a two-step process.
First yield from the currently running active app to the target app that you want to gain focus.
Then activate the target app when it’s ready.
[Image]
Only the active app can influence the activation context. It does so by yielding to an explicit target app before the target app activates. Then, when the target app requests activation, the system uses the yield as part of the context when making its decision. If the system honors the request, the active app deactivates, and the target app activates. Otherwise, the active app remains active. NSWorkspace automatically handles this for you when opening URLs or applications.
For example, to pass control from your active app to another target app (such as TextEdit, which is a good test candidate because it comes installed on every Mac):
Get an instance of the target app you want to pass control to by calling runningApplications(withBundleIdentifier:), passing in the bundle identifier of the target app.
Yield to the target app by calling yieldActivation(to:), passing the target app instance.
Then activate the target by calling activate() on the target app instance.
Alternatively, if the app you want to pass control to isn’t currently running, call yieldActivation(toApplicationWithBundleIdentifier:) on the NSApp instance, passing in the bundle identifier of the target app you want to activate.
Then call activate() on the target app when it’s ready to gain focus.
Choosing between NSRunningApplication or NSApplication for activation depends on which app you want to initiate the activation. Use NSRunningApplication if you want the active app to control when the target app activates. Use NSApplication (or NSApp) if you want the target app to activate itself.
Replace calls to deactivate
When you call yieldActivation(to:), there’s no need to call deactivate() on the app losing focus. Cooperative activation APIs cause the receiver to draw inactive when another app activates. This is typically the behavior you want.
Replace calls to deactivate() with yieldActivation(to:) or equivalent. There’s also no need to call deactivate() if an app is hidden by calling hide(). The system implies deactivation when the app hides.