8

Prior to iOS 13, navigation controllers and root views were defined in AppDelegate. With iOS 13 however, Apple introduced SceneDelegate, which takes over the handling of these view functions. However, AppDelegate still handles things such as Local Notification Handling. See this answer for some code that outlines these changes for root views.

If I wanted a view to be pushed when a user taps a local notification, I would do the something like the following in AppDelegate:

extension AppDelegate: UNUserNotificationCenterDelegate {

    var navigationController: UINavigationController?

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

        if response.actionIdentifier == UNNotificationDefaultActionIdentifier { //User taps notification
             let vc = MyViewController()
             self.navigationController?.pushViewController(vc, animated: true)
        }
    }
}

However, since the root navigation controller for my project can now be defined in SceneDelegate as of iOS 13, I can't seem to figure out how to push a view within a navigation controller managed by SceneDelegate instead of AppDelegate.

elrond140
  • 91
  • 1
  • 6
  • I am in the same boat. In the AppDelegate the `window` variable is nil. Do I set up the window in `SceneDelegate` and use it back in `AppDelegate`. – thenakulchawla May 16 '20 at 22:56

2 Answers2

5

SceneDelegate can handle notification response this way:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
  func scene(
    _ scene: UIScene,
    willConnectTo session: UISceneSession,
    options connectionOptions: UIScene.ConnectionOptions
  ) {
    if let windowScene = scene as? UIWindowScene {
      let window = UIWindow(windowScene: windowScene)
      // rootViewController set up code
      // say,
      // let mainController = ViewController()
      // let navigationController = UINavigationController(rootViewController: mainController)
      // window.rootViewController = navigationController

      // This is UNNotificationResponse
      if let notificationResponse = connectionOptions.notificationResponse {
        window.makeKeyAndVisible()
        // do the pushing on your navigation controller
        // navigationController.push()
        return
      }

      window.makeKeyAndVisible()
    }
  }
}
NeverwinterMoon
  • 2,363
  • 21
  • 24
4

If your app supports multiple windows OR single windows, you can use the targetScene property on UNNotificationResponse to determine which scene (window) the notification will launch.

Example:

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    guard let windowSceneDelegate = response.targetScene?.delegate as? UIWindowSceneDelegate,
          let window = windowSceneDelegate.window,
          let rootViewController = window.rootViewController as? UINavigationController else {
        completionHandler()
        return
    }
    let notificationViewController = UIViewController()
    rootViewController.pushViewController(notificationViewController, animated: true)
    completionHandler()
}
Travis C.
  • 121
  • 1
  • 5