11

After upgrading Xcode a critical part of my application has stopped working.

When my app launches I run a function to check boolean flags and set the correct rootViewController.

But the code I have been using to set this has now stopped working

class func setLoginAsInitialViewContoller(window:UIWindow) {
    print("SET LOGIN") 
    let storyboard = UIStoryboard(name: "Login", bundle: nil)
    let controller = storyboard.instantiateViewController(withIdentifier: "LoginViewController")
    controller.modalPresentationStyle = .overFullScreen
    window.rootViewController = controller
    window.makeKeyAndVisible()
}

Specifically when the app gets the the second last line window.rootViewController = controller it crashes with a libc++abi.dylib: terminating with uncaught exception of type NSException error.

The above function is in a class called Utilities.swift and I am calling the function from within my AppDelegate.swift as shown below:

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var storyboard: UIStoryboard? = nil

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        UIApplication.shared.isIdleTimerDisabled = true
        Utilities.decideInitialViewController(window: self.window!)

        return true
    }

Any solutions or fixes on how I can set the root controller is much appreciated.

Thank!

  • Is that the full error message in console? – Larme Sep 24 '19 at 14:40
  • have you tried adding exception breakpoints to get more information about the crash? – Bastian Sep 24 '19 at 14:40
  • there should be more information about what exception happened. – Bastian Sep 24 '19 at 14:41
  • @Larme yes that's the full error, It gives me 'libc++abi.dylib: terminating with uncaught exception of type NSException' followed by '(lldb)' –  Sep 24 '19 at 14:41
  • Nothing before? Like a almost the same one, with uppercase at some letters and "NSUncaugthException" instead of "NSException"? – Larme Sep 24 '19 at 14:42
  • @Larme not a single thing before! Very frustrating that its the only error its giving me.. here's the console log https://imgur.com/a/ixgP1oX –  Sep 24 '19 at 14:45
  • Strange to set the `controller.modalPresentationStyle = .overFullScreen` on the rootVC? no? Could it be the issue? Also, what are the values of the differents object in the debugguer when it crashes. Are some nil? – Larme Sep 24 '19 at 14:46
  • No that doesn't cause any issues, I've removed that and still having same problem.. –  Sep 24 '19 at 14:48
  • @Larme I have updated my question with some further details –  Sep 24 '19 at 14:52
  • In iOS 13, window is now part of `SceneDelegate`. More details [here](https://stackoverflow.com/a/56515686/3411787) – Mohammad Zaid Pathan Sep 25 '19 at 12:04

5 Answers5

15

This is because AppDelegate doesn't have window property anymore. Now you must use SceneDelegate's scene(_:willConnectTo:options:) method to change root view controller. Like shown in this example:

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let scene = (scene as? UIWindowScene) else { return }

        // Instantiate UIWindow with scene
        let window = UIWindow(windowScene: scene)
        // Assign window to SceneDelegate window property
        self.window = window
        // Set initial view controller from Main storyboard as root view controller of UIWindow
        self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
        // Present window to screen
        self.window?.makeKeyAndVisible()
    }
lacefarin
  • 1,018
  • 2
  • 13
  • 18
7

It is available in SceneDelegate.swift file in your project

It will have delegate method :

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)

Example

func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
 options connectionOptions: UIScene.ConnectionOptions) {


if let windowScene = scene as? UIWindowScene {

    self.window = UIWindow(windowScene: windowScene) 

    let initialViewController = 
        storyboard.instantiateViewController(withIdentifier: "FirstViewController")            
        self.window!.rootViewController = initialViewController
        self.window!.makeKeyAndVisible()
    }

}
Renan Lopes
  • 1,229
  • 2
  • 10
  • 17
Nidhi
  • 754
  • 5
  • 9
  • 1
    where to set rootViewController for notification Clicked. Earlier i was doing this in app_delegate func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { . But on setting rootViewController it is found nil. – Amit Verma Dec 26 '19 at 01:22
  • This solved an unrelated problem for me. I used to be able to grab the rootVC in AppDelegate by accessing keyWindow which is deprecated now. Sure enough when opening the app from a url, keywindow is none and the rootVC is some on the windowScene object. Thanks – Rolf Locher Jun 21 '20 at 19:41
5

In viewDidAppear you can set root:-

  override func viewDidAppear(_ animated: Bool) {
            print(self.view.window)
            let vc = self.storyboard?.instantiateViewController(identifier: "SecondViewController") as? SecondViewController
            self.view.window?.rootViewController = vc
        }
dinesh sharma
  • 577
  • 10
  • 20
1

For anyone looking to create a couple of extensions to change the root view controller and need to support both delegate types (UISceneDelegate and AppDelegate), here's a couple:

    extension UIViewController {
        var appDelegate: AppDelegate {
        return UIApplication.shared.delegate as! AppDelegate
    }
    
    var sceneDelegate: SceneDelegate? {
        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
            let delegate = windowScene.delegate as? SceneDelegate else { return nil }
         return delegate
    }
}

And if you're in need of an extension to reach the UIWindow from a ViewController with iOS12 and iOS13 Support:

extension UIViewController {
    
    var window: UIWindow? {
        if #available(iOS 13, *) {
            guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
                let delegate = windowScene.delegate as? SceneDelegate, let window = delegate.window else { return nil }
                   return window
        }
        
        guard let delegate = UIApplication.shared.delegate as? AppDelegate, let window = delegate.window else { return nil }
        return window
    }
}
Larry Mickie
  • 441
  • 4
  • 9
  • Also, Can you share the code how to use these extensions? Let's say I have to set "LoginVC" as my rootViewController. FYI: My deployment target is iOS 13.0 – Bhanuteja Jul 01 '21 at 18:35
  • @Bhanuteja sure with these extensions you can do something like : self.window?.rootViewController = LoginVC for example. – Larry Mickie Jul 04 '21 at 03:12
0
  • If you want to use ScenceDelegate. In scene(_:willConnectTo:options:, creates a new UIWindow, sets the window’s rootViewController and makes this window the key window.
  • If you don't want to use ScenceDelegate, you can try to remove it by following this.
RY_ Zheng
  • 3,041
  • 29
  • 36