Set rootViewController iOS 13 - ios

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!

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

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

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
}

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

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.

Related

Can't add viewContoller to Tab Bar programmatically in the AppDelegate

I'm trying to add a viewController to the existing Tab Bar programmatically through didFinishLaunchWithOptions, but my code doesn't work, i can't downcast the rootViewController as UITabBarController.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
if let tabBarController = window?.rootViewController as? UITabBarController {
print("rootVC")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "NavController")
vc.tabBarItem = UITabBarItem(tabBarSystemItem: .topRated, tag: 1)
tabBarController.viewControllers?.append(vc)
}
return true
}
Here is my storyboard, i think here is all okay, Tab Bar Contoller is initial view Controller...
Main.storyboard
In iOS 13+ the window will be nil from didFinishLaunchingWithOptions in the app delegate. Instead move your code to willConnectTo session method in SceneDelegate:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let tabBarController = window?.rootViewController as? UITabBarController {
print("rootVC")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "NavController")
vc.tabBarItem = UITabBarItem(tabBarSystemItem: .topRated, tag: 1)
tabBarController.viewControllers?.append(vc)
}
guard let _ = (scene as? UIWindowScene) else { return }
}

iOS Swift Firebase Checking user at app startup not working

I developed an app with firebase. The application checks to see if any users are logged in every boot in sceneDelegate.swift file. But its works only iOS 13 devices. iOS 12 devices users has to log in every time. How can I fix this problem? Thanks.
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
#available(iOS 13.0, *)
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).
let currentUser = Auth.auth().currentUser
if currentUser != nil {
let board = UIStoryboard(name: "Main", bundle: nil)
let navigationController = board.instantiateViewController(identifier: "navigationController") as! UINavigationController
window?.rootViewController = navigationController
}
guard let _ = (scene as? UIWindowScene) else { return }
}
SceneDelegate was introduced in iOS 13. For iOS 12 and below you should implement this same logic in the AppDelegate's didFinishLaunchingWithOptions method like this:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow()
window?.makeKeyAndVisible()
let currentUser = Auth.auth().currentUser
if currentUser != nil {
let board = UIStoryboard(name: "Main", bundle: nil)
let navigationController = board.instantiateViewController(withIdentifier: "navigationController") as! UINavigationController
window?.rootViewController = navigationController
}
return true
}
}

IOS | Unable to change root view controller in app delegate

I am building an app whose root view controller must be changed if the user is logged in. Say if the user is logged in I must show a tab bar controller as the home screen if the user is not logged in, I must show an Authentication controller. Both my controllers are storyboard controllers. Now in my app delegate, I have put the following code
window = UIWindow(frame: UIScreen.main.bounds)
if UserDefaults.standard.bool(forKey: Constants.UserDefaultsKeys.isLoggedIn){
initialViewController = storyboard.instantiateViewController(identifier: Constants.StoryBoards.homeViewController) as! TabController
}else{
initialViewController = storyboard.instantiateViewController(identifier: Constants.StoryBoards.authenticationController)
}
window?.rootViewController = initialViewController
window?.makeKeyAndVisible()
As per the code if the user is logged in ,TabController must be showed.But it is not being shown.I have tried debugging and TabController's viewDidLoad is being called but still my authenticationController is being shown and that is probably because authenticationController is set as the initial viewcontroller in the storyboard. Can someone help me figure out the issue
If you are targeting only iOS 13+, the only change you should need to make is to add one line:
window?.rootViewController = initialViewController
// add this line
self.window = window
window?.makeKeyAndVisible()
If you want to support earlier iOS versions, here is a complete SceneDelegate / AppDelegate implementation:
SceneDelegate.swift
//
// SceneDelegate.swift
// Created by Don Mag on 3/27/20.
//
import UIKit
// entire class is iOS 13+
#available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
print("Scene Delegate willConnectTo", UserDefaults.standard.bool(forKey: "isLoggedIn"))
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window.windowScene = windowScene
if UserDefaults.standard.bool(forKey: "isLoggedIn") {
guard let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "HomeVC") as? TabController else {
fatalError("Could not instantiate HomeVC!")
}
window.rootViewController = vc
} else {
guard let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "AuthVC") as? AuthViewController else {
fatalError("Could not instantiate HomeVC!")
}
window.rootViewController = vc
}
self.window = window
window.makeKeyAndVisible()
}
}
AppDelegate.swift
//
// AppDelegate.swift
// Created by Don Mag on 3/27/20.
//
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window : UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {
if #available(iOS 13, *) {
// do only pure app launch stuff, not interface stuff
} else {
print("App Delegate didFinishLaunching... isLoggedIn:", UserDefaults.standard.bool(forKey: "isLoggedIn"))
self.window = UIWindow()
if UserDefaults.standard.bool(forKey: "isLoggedIn") {
guard let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "HomeVC") as? TabController else {
fatalError("Could not instantiate HomeVC!")
}
window?.rootViewController = vc
} else {
guard let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "AuthVC") as? AuthViewController else {
fatalError("Could not instantiate HomeVC!")
}
window?.rootViewController = vc
}
window?.makeKeyAndVisible()
}
return true
}
// MARK: UISceneSession Lifecycle
// iOS 13+ only
#available(iOS 13.0, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
// iOS 13+ only
#available(iOS 13.0, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
}
}
Here's a way to extend your UIWindow (and access to your rootViewController) into your view controllers to make it easier to change your rootviewcontroller throughout your app:
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
}}
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
}
}

Updating/Changing root view controller after initially opening app for iOS13+

I'm trying to update the root view controller from ViewController() to fourYearPlan(). When the user initially opens the app, they are greeted in ViewController() and led to a TableViewController(). From there, when they tap on a tableViewCell, it will lead them to fourYearPlan() and automatically change the root view controller to it. If the user presses a button in fourYearPlan() the root view controller resets to ViewController(). How will I be able to approach this?
AppDelegate:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
updateRootVC()
return true
}
// MARK: UISceneSession Lifecycle
#available(iOS 13.0, *)
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.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
#available(iOS 13.0, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
func updateRootVC(){
let status = UserDefaults.standard.bool(forKey: "status")
print(status)
if status == true {
let navController = UINavigationController(rootViewController: fourYearPlan())
window?.rootViewController = navController
}
else{
let navController = UINavigationController(rootViewController: ViewController())
window?.rootViewController = navController
}
window?.makeKeyAndVisible()
}
}
Portion of SceneDelegate:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
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 }
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window = window
appDelegate.updateRootVC()
}
ViewController() will lead to TableViewController() and if clicked, fourYearPlan becomes the new root view controller from ViewController():
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
tableView.deselectRow(at: indexPath, animated: true)
let layout = UICollectionViewFlowLayout()
let destination = fourYearPlan(collectionViewLayout: layout)
// UserDefaults.standard.set(true, forKey: "status")
// let appDelegate = UIApplication.shared.delegate as! AppDelegate
// appDelegate.updateRootVC()
navigationController?.pushViewController(destination, animated: true)
}
button in fourYearPlan() that resets root view controller:
#objc func addTapped(sender: UIBarButtonItem!) {
let addAlert = UIAlertController(title: "Start Over", message: "Are you sure you want to create a new plan?", preferredStyle: .alert)
addAlert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (action:UIAlertAction) in
let defaults = UserDefaults.standard
// defaults.set(false, forKey: "status")
// let appDelegate = UIApplication.shared.delegate as! AppDelegate
// appDelegate.updateRootVC()
let domain = Bundle.main.bundleIdentifier!
defaults.removePersistentDomain(forName: domain)
defaults.synchronize()
let destination = ViewController()
self.navigationController?.pushViewController(destination, animated: true)
}))
addAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(addAlert, animated: true, completion: nil)
}
For Xcode 13+ , Swift 5+
Open SceneDelegate and paste replace this , Replace CustomerMainViewController with your view controller
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 UserDefaultHelper.isLoggedIn! {
print("User logged in")
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) // this assumes your storyboard is titled "Main.storyboard"
let yourVC = mainStoryboard.instantiateViewController(withIdentifier: "CustomerMainViewController") as! CustomerMainViewController // inside "YOUR_VC_IDENTIFIER" substitute the Storyboard ID you created in step 2 for the view controller you want to open here. And substitute YourViewController with the name of your view controller, like, for example, ViewController2.
self.window?.rootViewController = yourVC
self.window?.makeKeyAndVisible()
}
else {
print("User Not logged in")
}
}

iOS 13: Swift - 'Set application root view controller programmatically' does not work

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:
Swift ios set a new root view controller
Root View Controller Swift
iOS: Root Controller Navigation
To choose a previous approach to the one supported by SwiftUI, from a project created in Xcode 11, you can follow these steps.
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:
init(windowScene:)
UIWindow
rootViewController
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()
}
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
}
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.
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
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
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()
}
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)
}
}
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
}
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
I just deleted a row on the info.plist that says something about Application scene. Works for me.
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()
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.
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()
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.

Resources