App Shortcuts in iOS 13

App Shortcuts (also known as Home Screen Quick Actions), are the nice little popups that allow users to quickly jump into a specific section of your app, as seen in the screenshot below.

Apple has a really nice guide on how to implement these in your own apps, but the problem is that said guide seems to be outdated for iOS 13 and Scene-based apps. Basically you can now forget about the AppDelegate for now as the SceneDelegate is the new MVP here.

In the original guide, you were to override a few methods in the AppDelegate. This doesn't work anymore though, as the applicationDidBecomeActive function is never called when your app wakes up from the background. You now need to override a series of similar functions in the SceneDelegate, and slightly modify one of the AppDelegate methods.

Instead of overriding the AppDelegate's application(_:didFinishLaunchingWithOptions:) and application(_:performActionFor:completionHandler:)``</code> methods, you now only need to modify the method that sets up the scene:

// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication,
                 configurationForConnecting connectingSceneSession: UISceneSession,
                 options: UIScene.ConnectionOptions) -> UISceneConfiguration {
    // Called when a new scene session is being created.
    // Use this method to select a configuration to create the new scene with.

    // Grab a reference to the shortcutItem to use in the scene
    if let shortcutItem = options.shortcutItem {
        shortcutItemToProcess = shortcutItem
    }

    return UISceneConfiguration(name: "Default Configuration",
                                sessionRole: connectingSceneSession.role)
}

This takes care of the scenario in which the user opens your app from a quick action while your app isn't already loaded in the background. In case your app is already running in the background, you'll need to implement the SceneDelegate's windowScene(_:performActionFor:completionHandler:) method:

func windowScene(_ windowScene: UIWindowScene,
                 performActionFor shortcutItem: UIApplicationShortcutItem,
                 completionHandler: @escaping (Bool) -> Void) {
    // When the user opens the app through a quick action, this is now the method that will be called
    (UIApplication.shared.delegate as! AppDelegate).shortcutItemToProcess = shortcutItem
}

Then, instead of implementing AppDelegate's applicationDidBecomeActive method, you'll implement the SceneDelegate's equivalent:

func sceneDidBecomeActive(_ scene: UIScene) {
    // Is there a shortcut item that has not yet been processed?
    if let shortcutItem = (UIApplication.shared.delegate as! AppDelegate).shortcutItemToProcess {
        // In this sample an alert is being shown to indicate that the action has been triggered,
        // but in real code the functionality for the quick action would be triggered.
        var message = "\(shortcutItem.type) triggered"
        if let name = shortcutItem.userInfo?["Name"] {
            message += " for \(name)"
        }
        let alertController = UIAlertController(title: "Quick Action", message: message, preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "Close", style: .default, handler: nil))
        window?.rootViewController?.present(alertController, animated: true, completion: nil)
        // Reset the shorcut item so it's never processed twice.
        (UIApplication.shared.delegate as! AppDelegate).shortcutItemToProcess = nil
    }
}