Set rootViewController iOS 13
Asked Answered
T

6

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!

Teenateenage answered 24/9, 2019 at 14:36 Comment(10)
Is that the full error message in console?Ailyn
have you tried adding exception breakpoints to get more information about the crash?Translate
there should be more information about what exception happened.Translate
@Ailyn yes that's the full error, It gives me 'libc++abi.dylib: terminating with uncaught exception of type NSException' followed by '(lldb)'Teenateenage
Nothing before? Like a almost the same one, with uppercase at some letters and "NSUncaugthException" instead of "NSException"?Ailyn
@Ailyn not a single thing before! Very frustrating that its the only error its giving me.. here's the console log imgur.com/a/ixgP1oXTeenateenage
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?Ailyn
No that doesn't cause any issues, I've removed that and still having same problem..Teenateenage
@Ailyn I have updated my question with some further detailsTeenateenage
In iOS 13, window is now part of SceneDelegate. More details hereSpikes
P
16

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()
    }
Pompei answered 25/9, 2019 at 12:0 Comment(0)
K
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()
    }

}
Keratogenous answered 10/10, 2019 at 12:33 Comment(2)
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.Buhr
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. ThanksInvestigation
C
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
        }
Chopin answered 17/1, 2020 at 12:42 Comment(0)
D
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
    }
}
Darleen answered 10/9, 2020 at 2:0 Comment(2)
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.0Bultman
@Bultman sure with these extensions you can do something like : self.window?.rootViewController = LoginVC for example.Darleen
S
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.
Simaroubaceous answered 3/6, 2020 at 1:41 Comment(0)
B
0

Swift 5+ IOS 13+ very easy way :-) happy coding

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
        }
}



//Calling Method
    let storyBoard : UIStoryboard = UIStoryboard(name: "Home", bundle:nil)
    if let navigationController = storyBoard.instantiateViewController(withIdentifier: "NavigationHomeViewController") as? UINavigationController {
        self.sceneDelegate?.window?.rootViewController = navigationController
    }
Biisk answered 10/7 at 6:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.