iOS 13: Swift - 'Set application root view controller programmatically' does not work
IosSwiftAppdelegateIos13RootviewcontrollerIos Problem Overview
I have following code in my AppDelegate.swift to setup root view controller for an iOS application. But it does not work. It follows Target structure (defined under General tab) and ignores this code.
(Xcode 11, Swift 5.1, iOS 13)
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
guard let rootVC = UIViewController() else {
print("Root VC not found")
return true
}
let rootNC = UINavigationController(rootViewController: rootVC)
window?.rootViewController = rootNC
window?.makeKeyAndVisible()
return true
}
}
Unable to understand where is the issue.
I tried following references also but no luck:
Ios Solutions
Solution 1 - Ios
To choose a previous approach to the one supported by SwiftUI, from a project created in Xcode 11, you can follow these steps.
Solution 2 - Ios
I tried following two options and both of these working for me. With iOS-13 (Xcode 11) a new file SceneDelegate.swift with the concept of UIWindowScene is enabled by default.
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
//self.window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
guard let rootVC = storyboard?.instantiateViewController(identifier: "ViewControllerIdentifierName") as? ViewController else {
print("ViewController not found")
return
}
let rootNC = UINavigationController(rootViewController: rootVC)
self.window?.rootViewController = rootNC
self.window?.makeKeyAndVisible()
}
}
Alternate:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let windowScene = UIWindowScene(session: session, connectionOptions: connectionOptions)
self.window = UIWindow(windowScene: windowScene)
//self.window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
guard let rootVC = storyboard?.instantiateViewController(identifier: "ViewControllerIdentifierName") as? ViewController else {
print("ViewController not found")
return
}
let rootNC = UINavigationController(rootViewController: rootVC)
self.window?.rootViewController = rootNC
self.window?.makeKeyAndVisible()
}
}
I don't know, why and how it works but it resolved my problem.
Reference docs that helped me:
Solution 3 - Ios
I tried the following approach and it's working for me in iOS 13 and also tested on iOS 12.4.2 from Xcode 11.
func resetRoot() {
guard let rootVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as? ViewController else {
return
}
let navigationController = UINavigationController(rootViewController: rootVC)
UIApplication.shared.windows.first?.rootViewController = navigationController
UIApplication.shared.windows.first?.makeKeyAndVisible()
}
Solution 4 - Ios
Here's what work for both iOS 13.x and iOS 12.x and below
For iOS 13,In the Scene Delegate
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(frame: windowScene.coordinateSpace.bounds)
//Make sure to do this else you won't get
//the windowScene object using UIApplication.shared.connectedScenes
self.window?.windowScene = windowScene
let storyBoard: UIStoryboard = UIStoryboard(name: storyBoardName, bundle: nil)
window?.rootViewController = storyBoard.instantiateInitialViewController()
window?.makeKeyAndVisible()
}
In a utility class, I wrote below function to get the window
object and assign it to the appdelegate.window
. According to my needs, I needed to set root view controller at multiple places in different scenarios for which I needed the window object.
static func redirectToMainNavRVC(currentVC: UIViewController){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let vc = UIStoryboard(name: appDelegate.storyBoardName, bundle: nil).instantiateViewController(withIdentifier: "MainNavigationViewController") as! MainNavigationViewController
if #available(iOS 13.0, *){
if let scene = UIApplication.shared.connectedScenes.first{
guard let windowScene = (scene as? UIWindowScene) else { return }
print(">>> windowScene: \(windowScene)")
let window: UIWindow = UIWindow(frame: windowScene.coordinateSpace.bounds)
window.windowScene = windowScene //Make sure to do this
window.rootViewController = vc
window.makeKeyAndVisible()
appDelegate.window = window
}
} else {
appDelegate.window?.rootViewController = vc
appDelegate.window?.makeKeyAndVisible()
}
}
This worked well for me. Hopefully, it works for others too.
Solution 5 - Ios
First Case
If major of your project is build in storyboard and before Xcode 11 is used for development and you are not using SwiftUI you want to use your old classes associated with AppDelegate.
-
Then try to remove "Application Scene Manifest" in info.pllist.
-
Remove ScenceDelegate from project completely.
Then you will able to use your old code.
Second Case
If you want to use both Appdelegte and ScenceDelegate then below is working code.
App Delegate Code:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 13.0, *){
//do nothing we will have a code in SceneceDelegate for this
} else {
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let VC = mainStoryboard.instantiateViewController(withIdentifier: "LoginVC") as! LoginVC
navigationController?.isNavigationBarHidden = true
navigationController = UINavigationController(rootViewController: VC)
navigationController?.isNavigationBarHidden = true // or not, your choice.
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window!.rootViewController = navigationController
}
return true
}
ScenceDelegate Code:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let VC = mainStoryboard.instantiateViewController(withIdentifier: "LoginVC") as! LoginVC
navigationController?.isNavigationBarHidden = true
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window.windowScene = windowScene
window.rootViewController = VC
window.makeKeyAndVisible()
let appDelegate = UIapplication.shared.delegate as! AppDelegate
appDelegate.window = window
}
Solution 6 - Ios
If you follow this answer https://stackoverflow.com/a/58084612/6667477
and if you initialise a rootController programmatically you also have to remove reference to main storyboard from:
- Info.plist.
- Deployment info
Solution 7 - Ios
var window: UIWindow?
has been moved from AppdDelegate.swift to SceneDelegate.swift.
So I used rootViewController in the Scene Delegate class and it works -
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
if let tabBarController = window?.rootViewController as? UITabBarController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "NavController")
vc.tabBarItem = UITabBarItem(tabBarSystemItem: .topRated, tag: 1)
tabBarController.viewControllers?.append(vc)
}
}
Solution 8 - Ios
For Xcode 11+ and Swift 5+ inside SceneDelegate.swift
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return }
let window = UIWindow(windowScene: windowScene)
let submodules = (
home: HomeRouter.createModule(),
search: SearchRouter.createModule(),
exoplanets: ExoplanetsRouter.createModule()
)
let tabBarController = TabBarModuleBuilder.build(usingSubmodules: submodules)
window.rootViewController = tabBarController
self.window = window
window.makeKeyAndVisible()
}
Solution 9 - Ios
If you don't want to use main storyboard, if you want to create you own views programmatically.
For xcode 11.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.makeKeyAndVisible()
window?.rootViewController = UINavigationController(rootViewController: ViewController())
}
This will defenetily work. Thanks
Solution 10 - Ios
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
self.window?.makeKeyAndVisible()
let layout = UICollectionViewFlowLayout()
let swipingController = SwipingController(collectionViewLayout: layout)
self.window?.rootViewController = swipingController
}
Solution 11 - Ios
I just deleted a row on the info.plist that says something about Application scene. Works for me.
Solution 12 - Ios
let MainViewController = mainStoryboard.instantiateViewController(withIdentifier: "MainViewController") as! MainViewController
let nvc:rootNavigation = mainStoryboard.instantiateViewController(withIdentifier: "rootNavigation") as! rootNavigation
nvc.viewControllers = [Mainmap]
leftViewController.mainViewController = nvc
UIApplication.shared.windows.first?.backgroundColor = UIColor(red: 236.0, green: 238.0, blue: 241.0, alpha: 1.0)
UIApplication.shared.windows.first?.rootViewController = nvc
UIApplication.shared.windows.first?.makeKeyAndVisible()
Solution 13 - Ios
let controller = ....
let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
keyWindow?.rootViewController = controller
keyWindow?.makeKeyAndVisible()
These are the basic code required to set root view controller.
Solution 14 - Ios
Important Points to Note: If you plan to add SwiftUI to existing project, then the project's deployment target must be set to iOS 13, and then the project must have SceneDelegate. You cannot launch the app with just AppDelegate. (If you are not intending to use SwiftUI, but still need min deployment target to be iOS 13, follow Carlos García's answer above)
If you are trying to add code in SwiftUI, in an already created project , make sure
- Minimum deployment Target is set to iOS 13
- You have both Appdelegate + SceneDelegate setup correctly
- Mainly, have the
Application Scene Manifest
entry in Info.plist, possibly create a dummy Project and copy entry from there
Solution 15 - Ios
This will help you to set your root view controller through scene delegate.
let navVc = UINavigationController(rootViewController: UIViewController)
navVc.setNavigationBarHidden(true, animated: false )
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let sceneDelegate = windowScene.delegate as? SceneDelegate else { return }
sceneDelegate.window?.rootViewController = navVc
sceneDelegate.window?.makeKeyAndVisible()
Solution 16 - Ios
I did two things. Firstly I set a Notification
into SceneDelegate
, then when I needed to change the RootViewController
I did a Notification Post
and it worked. But it's an anwful solution.
After that
My boss recommended me to change the Controllers
of the NavigationController
, something like this:
func logged() {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "MainTabViewController", bundle: nil)
let mainVC = mainStoryboard.instantiateInitialViewController()
self.navigationController?.setViewControllers([mainVC!], animated: false)
}
I know that perhaps it's not the best solution, but I see it cleaner.
I'm working now on iOS 13 and I didn't want to use deprecated
things.