ViewDidLoad gets called twice iOS 13+ - ios

I am developing an app for iOS 12+ and the fact that iOS 13 requires a SceneDelegate is causing me some problems when displaying the first ViewController programmatically, as I don't use storyboards.
In the AppDelegate.swift I use this code to present the ViewController for devices that don't have iOS 13 installed yet:
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
Database.database().isPersistenceEnabled = true
IQKeyboardManager.shared.enable = true
window = UIWindow()
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
window?.backgroundColor = .main
window?.rootViewController = UINavigationController(rootViewController: ViewController())
return true
}
This works properly for those devices; in the SceneDelegate.swift tho, I have to put the following code to present the first ViewController:
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 winScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: winScene)
window?.backgroundColor = .main
window?.makeKeyAndVisible()
window?.rootViewController = UINavigationController(rootViewController: ViewController())
}
This works exactly as I expected for the <13.0 iOS versions, as I marked the whole SceneDelegate.swift as available in iOS 13 only, but when I try to run this code on a 13+ iOS version, the ViewDidLoad function of my ViewController gets called twice, because I am technically instantiating it twice. I tried commenting out the code in the AppDelegate.swift, but that won't allow my app to be used in <13.0 iOS versions, and then I tried commenting out the code in the SceneDelegate.swift, but that won't allow my app to be used in 13+ iOS versions.
Is there a way to allow some piece of code to be run only In versions below 13.0?
This question is not related to Nibs, xibs or any other problem mentioned in other questions about the ViewDidLoad running twice, please don't mark this as a duplicated of those ones, as it's not.
Thank you, NicopDev

With ios 13 don't do anything inside didFinishLaunchingWithOptions Try
if #available(iOS 13.0, *) { }
else {
window = UIWindow()
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
window?.backgroundColor = .main
window?.rootViewController = UINavigationController(rootViewController: ViewController())
}
Also you need to comment either
window?.rootViewController = ViewController()
or
window?.rootViewController = UINavigationController(rootViewController: ViewController())

Yes, use #available. In the AppDelegate, do the following:
if #available(iOS 13, *) {
// do nothing in AppDelegate
} else {
// non iOS 13 window management
}
One note here though is that you are setting the rootViewController twice: once with just ViewController (incorrect) and then later inside a UINavigationController. You are also instantiating it twice which is unnecessary.
For your scene delegate, mark it as:
#available(iOS 13, *)
class SceneDelegate: UISceneDelegate {
}

The way I tried to fix this is the following:
Inside of AppDelegate.swift
if #available(iOS 13, *) { // Checks the current version
} else { // If we are not in iOS 13+, then use this way of instantiating the ViewController
window = UIWindow()
window?.makeKeyAndVisible()
window?.backgroundColor = .main
window?.rootViewController = UINavigationController(rootViewController: ViewController())
}

Related

Setting root view controller upon launch, depending on user auth state

This is the storyboard layout I have right now
So what I need is to check if the user is already authenticated when the app launches.
If he is not logged in set the root view controller to the navigation controller with the login and register forms.
If he is logged in set it to the tab bar view controller.
I tried a lot of different solutions but none of them worked. It just kept setting the view controller to the one marked with "Is Initial View Controller".
This is the code I tried in the AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
var viewController: UIViewController
if isAuthenticated() {
viewController = storyboard.instantiateViewController(withIdentifier: Constants.Storyboards.homeViewController) as! UITabBarController
} else {
viewController = storyboard.instantiateViewController(withIdentifier: Constants.Storyboards.authViewController) as! UINavigationController
}
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
return true
}
I can do this by pressing a button easily, but I want it to happen without the user needing to do something.
EDIT: Thanks to #LukaCefarin and #Francesco Deliro I managed to find out that the problem was. I was using XCode 11 and the rootViewController had to be set in the SceneDelegate.swift
This is what my code in the SceneDelegate.swift looks like for anyone who has a similar issue:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: scene as! UIWindowScene)
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
var viewController: UIViewController;
if isAuthenticated() {
viewController = storyboard.instantiateViewController(withIdentifier: "HomeVC")
} else {
viewController = storyboard.instantiateViewController(withIdentifier: "AuthVC")
}
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
}
EDIT:
This solution and setting the window rootViewController in the AppDelegate work for versions prior to Xcode11 and iOS13. As suggested in the comments by Luka Cefarin, if you are using Xcode11 and iOS13 you have to set the window rootViewController in the SceneDelegate.swift file.
You have to remove the Main Interface and to uncheck the initial view controller in the storyboard:
Before
After

Making project created in Xcode 11 backwards compatible

I've created a new project using Xcode 11. I'm used to creating my UIWindow's root view controller programmatically and I first noticed this new SceneDelegate file. After some research I found an article describing how to create root view controller using this new UIScene API and it seems to work properly. However, I couldn't find a way to do the same thing for lower iOS versions because AppDelegate class now does not have a window property anymore. So my question is how do I create a root view controller programmatically for other iOS versions?
Here's the code if someone faces the same thing.
iOS 13 workflow
Code taken from this article.
In your SceneDelegate file:
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 windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(frame: windowScene.coordinateSpace.bounds)
self.window?.windowScene = windowScene
let controller = UIViewController()
let navigationController = UINavigationController(rootViewController: controller)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
iOS 12 and lower
In your AppDelegate file:
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 13.0, *) { return true }
self.window = UIWindow(frame: UIScreen.main.bounds)
let controller = UIViewController()
let navigationController = UINavigationController(rootViewController: controller)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
return true
}
It is important to hold UIWindow in a variable in order for it to show up.

iOS - Setting 'rootViewController' results in black screen on device only

With a very basic single view application, I've deleted the main storyboard file and removed any references to it. As such I'm setting the window rootViewController programmatically. However, while this displays the single view (containing a label) correctly in simulator, it displays a black screen when running it on device. Here is the only code for the app.
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = DummyViewController()
window?.makeKeyAndVisible()
return true
}
I've removed the entry for Main storyboard from the info.plist file, as well as the 'Main Interface' entry in the General settings.
I'm using Swift 3 and targeting an iOS 8 device. I'm using XCode 8.3.1.
There is no output in the console, and there are no exceptions. The viewDidLoad function is even triggering on breakpoint, so the codepath seems to be running correctly.
Any ideas?
Here's the bare bones code for DummyViewController upon request.
class DummyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
General settings showing no reference to Main Interface
Here is the image for the .xib linked to DummyViewController
** The solution to get around this case is to manually specify the .xib to load for the DummyViewController **
It looks like the ViewController is not set to display anything. Unless you are using a xib (in which case you would need to load the view controller in a different way, see below), there is nothing describing how the ViewController's view should render.
To test this out, you can add the line self.view.backgroundColor = UIColor.red to the ViewController's viewDidLoad() method, then run it again on the device- if the background color turns red, then hooray! The next step will be programmatically adding a UILabel.
Loading UIViewController From a XIB
let vc = MyViewController(nibName: "xibname", bundle: nil)
Alternatively, you can mask the loading by adding a custom init inside MyViewController:
class MyViewController: UIViewController {
required init() {
super.init(nibName: "xibname", bundle: nil)
}
}
(Thank you zonily-jame for the addition of hiding it in the class)
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let loginView : BaseClassVC = mainStoryboardIpad.instantiateViewControllerWithIdentifier("BaseClassVC") as BaseClassVC
let navigationController = UINavigationController(rootViewController: loginView)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
return true
}
change Your window root as and set color
let viewController:DummyViewController = DummyViewController()
self.window?.backgroundColor = UIColor.white
self.window?.rootViewController = viewController
And change your controller
override func viewDidLoad() {
self.view.backgroundColor = UIColor.white
}
You just need to initialize viewcontroller object and implement white background color to its instance.
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
//Add below 2 lines
let vc = DummyViewController()
vc.view.backgroundColor = .white
window?.rootViewController = vc
window?.makeKeyAndVisible()
return true
}

How can I put the initialViewController in code from AppDelegate? SWIFT [duplicate]

Creating a new project in XCode 6 doesn't allow to disable Storyboards. You can only select Swift or Objective-C and to use or not Core Data.
I tried deleting the storyboard and from the project removing the main storyboard and manually setting the window from didFinishLaunching
In the AppDelegate I have this:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow
var testNavigationController: UINavigationController
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
testNavigationController = UINavigationController()
var testViewController: UIViewController = UIViewController()
self.testNavigationController.pushViewController(testViewController, animated: false)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window.rootViewController = testNavigationController
self.window.backgroundColor = UIColor.whiteColor()
self.window.makeKeyAndVisible()
return true
}
}
However, XCode gives me an error:
Class 'AppDelegate' has no initializers
Anyone has succeed in this?
All it takes for not using Storyboards for the rootViewController:
1· Change AppDelegate.swift to:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
if let window = window {
window.backgroundColor = UIColor.white
window.rootViewController = ViewController()
window.makeKeyAndVisible()
}
return true
}
}
2· Create a ViewController subclass of UIViewController:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.blue
}
}
3· If you created the project from an Xcode template:
Remove the key-value pair for key "Main storyboard file base name" from Info.plist.
Delete the storyboard file Main.storyboard.
As you can see in the first code snippet, instead of implicitly unwrapping an optional, I rather like the if let syntax for unwrapping the optional window property. Here I'm using it like if let a = a { } so that the optional a becomes a non-optional reference inside the if-statement with the same name – a.
Finally self. is not necessary when referencing the window property inside it own class.
You must mark the window and testNavigationController variables as optional:
var window : UIWindow?
var testNavigationController : UINavigationController?
Swift classes require non-optional properties to be initialized during the instantiation:
Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.
Properties of optional type are automatically initialized with a value of nil, indicating that the property is deliberately intended to have “no value yet” during initialization.
When using optional variables, remember to unwrap them with !, such as:
self.window!.backgroundColor = UIColor.whiteColor();
If you want to Initialize your viewController with xib and and need to use navigation controller. Here is a piece of code.
var window: UIWindow?
var navController:UINavigationController?
var viewController:ViewController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
viewController = ViewController(nibName: "ViewController", bundle: nil);
navController = UINavigationController(rootViewController: viewController!);
window?.rootViewController = navController;
window?.makeKeyAndVisible()
return true
}
Try the following code:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.backgroundColor = UIColor.whiteColor()
// Create a nav/vc pair using the custom ViewController class
let nav = UINavigationController()
let vc = NextViewController ( nibName:"NextViewController", bundle: nil)
// Push the vc onto the nav
nav.pushViewController(vc, animated: false)
// Set the window’s root view controller
self.window!.rootViewController = nav
// Present the window
self.window!.makeKeyAndVisible()
return true
}
I have found the answer it had nothing to do with the xcode setup, removing storyboard and the reference from project is the right thing. It had to do with the swift syntax.
The code is the following:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var testNavigationController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
self.testNavigationController = UINavigationController()
var testViewController: UIViewController? = UIViewController()
testViewController!.view.backgroundColor = UIColor.redColor()
self.testNavigationController!.pushViewController(testViewController, animated: false)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.rootViewController = testNavigationController
self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()
return true
}
}
You can just do it like this:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var IndexNavigationController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
var IndexViewContoller : IndexViewController? = IndexViewController()
self.IndexNavigationController = UINavigationController(rootViewController:IndexViewContoller)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.rootViewController = self.IndexNavigationController
self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()
return true
}
}
Updated for Swift 3.0:
window = UIWindow()
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
Update: Swift 5 and iOS 13:
Create a Single View Application.
Delete Main.storyboard (right-click and delete).
Delete Storyboard Name from the default scene configuration in the Info.plist file:
Open SceneDelegate.swift and change func scene from:
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 }
}
to
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).x
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = ViewController()
self.window = window
window.makeKeyAndVisible()
}
}
I recommend you use controller and xib
MyViewController.swift and MyViewController.xib
(You can create through File->New->File->Cocoa Touch Class and set "also create XIB file" true, sub class of UIViewController)
class MyViewController: UIViewController {
.....
}
and In AppDelegate.swift func application write the following code
....
var controller: MyViewController = MyViewController(nibName:"MyViewController",bundle:nil)
self.window!.rootViewController = controller
return true
It should be work!
Here is a complete swift test example for an UINavigationController
import UIKit
#UIApplicationMain
class KSZAppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var testNavigationController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
// Working WITHOUT Storyboard
// see http://randexdev.com/2014/07/uicollectionview/
// see http://stackoverflow.com/questions/24046898/how-do-i-create-a-new-swift-project-without-using-storyboards
window = UIWindow(frame: UIScreen.mainScreen().bounds)
if let win = window {
win.opaque = true
//you could create the navigation controller in the applicationDidFinishLaunching: method of your application delegate.
var testViewController: UIViewController = UIViewController()
testNavigationController = UINavigationController(rootViewController: testViewController)
win.rootViewController = testNavigationController
win.backgroundColor = UIColor.whiteColor()
win.makeKeyAndVisible()
// see corresponding Obj-C in https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/NavigationControllers.html#//apple_ref/doc/uid/TP40011313-CH2-SW1
// - (void)applicationDidFinishLaunching:(UIApplication *)application {
// UIViewController *myViewController = [[MyViewController alloc] init];
// navigationController = [[UINavigationController alloc]
// initWithRootViewController:myViewController];
// window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// window.rootViewController = navigationController;
// [window makeKeyAndVisible];
//}
}
return true
}
}
Why don't you just create an empty application? the storyboard is not created to me...
We can create navigation-based application without storyboard in Xcode 6 (iOS 8) like as follows:
Create an empty application by selecting the project language as
Swift.
Add new cocoa touch class files with the interface xib. (eg.
TestViewController)
In the swift we have only one file interact with the xib i.e. *.swift
file, there is no .h and .m files.
We can connect the controls of xib with swift file same as in iOS 7.
Following are some snippets for work with the controls and Swift
//
// TestViewController.swift
//
import UIKit
class TestViewController: UIViewController {
#IBOutlet var testBtn : UIButton
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
// Custom initialization
}
#IBAction func testActionOnBtn(sender : UIButton) {
let cancelButtonTitle = NSLocalizedString("OK", comment: "")
let alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .Alert)
// Create the action.
let cancelAction = UIAlertAction(title: cancelButtonTitle, style: .Cancel) { action in
NSLog("The simple alert's cancel action occured.")
}
// Add the action.
alertController.addAction(cancelAction)
presentViewController(alertController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Changes in AppDelegate.swift file
//
// AppDelegate.swift
//
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var navigationController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()
var testController: TestViewController? = TestViewController(nibName: "TestViewController", bundle: nil)
self.navigationController = UINavigationController(rootViewController: testController)
self.window!.rootViewController = self.navigationController
return true
}
func applicationWillResignActive(application: UIApplication) {
}
func applicationDidEnterBackground(application: UIApplication) {
}
func applicationWillEnterForeground(application: UIApplication) {
}
func applicationDidBecomeActive(application: UIApplication) {
}
func applicationWillTerminate(application: UIApplication) {
}
}
Find code sample and other information on
http://ashishkakkad.wordpress.com/2014/06/16/create-a-application-in-xcode-6-ios-8-without-storyborard-in-swift-language-and-work-with-controls/
In iOS 13 and above when you create new project without storyboard use below steps:
Create project using Xcode 11 or above
Delete storyboard nib and class
Add new new file with xib
Need to set root view as UINavigationController SceneDelegate
add below code:
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 windowScene = scene as? UIWindowScene {
self.window = UIWindow(windowScene: windowScene)
let mainController = HomeViewController() as HomeViewController
let navigationController = UINavigationController(rootViewController: mainController)
self.window!.rootViewController = navigationController
self.window!.makeKeyAndVisible()
}
}

How do I create a new Swift project without using Storyboards?

Creating a new project in XCode 6 doesn't allow to disable Storyboards. You can only select Swift or Objective-C and to use or not Core Data.
I tried deleting the storyboard and from the project removing the main storyboard and manually setting the window from didFinishLaunching
In the AppDelegate I have this:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow
var testNavigationController: UINavigationController
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
testNavigationController = UINavigationController()
var testViewController: UIViewController = UIViewController()
self.testNavigationController.pushViewController(testViewController, animated: false)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window.rootViewController = testNavigationController
self.window.backgroundColor = UIColor.whiteColor()
self.window.makeKeyAndVisible()
return true
}
}
However, XCode gives me an error:
Class 'AppDelegate' has no initializers
Anyone has succeed in this?
All it takes for not using Storyboards for the rootViewController:
1· Change AppDelegate.swift to:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
if let window = window {
window.backgroundColor = UIColor.white
window.rootViewController = ViewController()
window.makeKeyAndVisible()
}
return true
}
}
2· Create a ViewController subclass of UIViewController:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.blue
}
}
3· If you created the project from an Xcode template:
Remove the key-value pair for key "Main storyboard file base name" from Info.plist.
Delete the storyboard file Main.storyboard.
As you can see in the first code snippet, instead of implicitly unwrapping an optional, I rather like the if let syntax for unwrapping the optional window property. Here I'm using it like if let a = a { } so that the optional a becomes a non-optional reference inside the if-statement with the same name – a.
Finally self. is not necessary when referencing the window property inside it own class.
You must mark the window and testNavigationController variables as optional:
var window : UIWindow?
var testNavigationController : UINavigationController?
Swift classes require non-optional properties to be initialized during the instantiation:
Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.
Properties of optional type are automatically initialized with a value of nil, indicating that the property is deliberately intended to have “no value yet” during initialization.
When using optional variables, remember to unwrap them with !, such as:
self.window!.backgroundColor = UIColor.whiteColor();
If you want to Initialize your viewController with xib and and need to use navigation controller. Here is a piece of code.
var window: UIWindow?
var navController:UINavigationController?
var viewController:ViewController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
viewController = ViewController(nibName: "ViewController", bundle: nil);
navController = UINavigationController(rootViewController: viewController!);
window?.rootViewController = navController;
window?.makeKeyAndVisible()
return true
}
Try the following code:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.backgroundColor = UIColor.whiteColor()
// Create a nav/vc pair using the custom ViewController class
let nav = UINavigationController()
let vc = NextViewController ( nibName:"NextViewController", bundle: nil)
// Push the vc onto the nav
nav.pushViewController(vc, animated: false)
// Set the window’s root view controller
self.window!.rootViewController = nav
// Present the window
self.window!.makeKeyAndVisible()
return true
}
I have found the answer it had nothing to do with the xcode setup, removing storyboard and the reference from project is the right thing. It had to do with the swift syntax.
The code is the following:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var testNavigationController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
self.testNavigationController = UINavigationController()
var testViewController: UIViewController? = UIViewController()
testViewController!.view.backgroundColor = UIColor.redColor()
self.testNavigationController!.pushViewController(testViewController, animated: false)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.rootViewController = testNavigationController
self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()
return true
}
}
You can just do it like this:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var IndexNavigationController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
var IndexViewContoller : IndexViewController? = IndexViewController()
self.IndexNavigationController = UINavigationController(rootViewController:IndexViewContoller)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.rootViewController = self.IndexNavigationController
self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()
return true
}
}
Updated for Swift 3.0:
window = UIWindow()
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
Update: Swift 5 and iOS 13:
Create a Single View Application.
Delete Main.storyboard (right-click and delete).
Delete Storyboard Name from the default scene configuration in the Info.plist file:
Open SceneDelegate.swift and change func scene from:
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 }
}
to
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).x
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = ViewController()
self.window = window
window.makeKeyAndVisible()
}
}
I recommend you use controller and xib
MyViewController.swift and MyViewController.xib
(You can create through File->New->File->Cocoa Touch Class and set "also create XIB file" true, sub class of UIViewController)
class MyViewController: UIViewController {
.....
}
and In AppDelegate.swift func application write the following code
....
var controller: MyViewController = MyViewController(nibName:"MyViewController",bundle:nil)
self.window!.rootViewController = controller
return true
It should be work!
Here is a complete swift test example for an UINavigationController
import UIKit
#UIApplicationMain
class KSZAppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var testNavigationController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
// Working WITHOUT Storyboard
// see http://randexdev.com/2014/07/uicollectionview/
// see http://stackoverflow.com/questions/24046898/how-do-i-create-a-new-swift-project-without-using-storyboards
window = UIWindow(frame: UIScreen.mainScreen().bounds)
if let win = window {
win.opaque = true
//you could create the navigation controller in the applicationDidFinishLaunching: method of your application delegate.
var testViewController: UIViewController = UIViewController()
testNavigationController = UINavigationController(rootViewController: testViewController)
win.rootViewController = testNavigationController
win.backgroundColor = UIColor.whiteColor()
win.makeKeyAndVisible()
// see corresponding Obj-C in https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/NavigationControllers.html#//apple_ref/doc/uid/TP40011313-CH2-SW1
// - (void)applicationDidFinishLaunching:(UIApplication *)application {
// UIViewController *myViewController = [[MyViewController alloc] init];
// navigationController = [[UINavigationController alloc]
// initWithRootViewController:myViewController];
// window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// window.rootViewController = navigationController;
// [window makeKeyAndVisible];
//}
}
return true
}
}
Why don't you just create an empty application? the storyboard is not created to me...
We can create navigation-based application without storyboard in Xcode 6 (iOS 8) like as follows:
Create an empty application by selecting the project language as
Swift.
Add new cocoa touch class files with the interface xib. (eg.
TestViewController)
In the swift we have only one file interact with the xib i.e. *.swift
file, there is no .h and .m files.
We can connect the controls of xib with swift file same as in iOS 7.
Following are some snippets for work with the controls and Swift
//
// TestViewController.swift
//
import UIKit
class TestViewController: UIViewController {
#IBOutlet var testBtn : UIButton
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
// Custom initialization
}
#IBAction func testActionOnBtn(sender : UIButton) {
let cancelButtonTitle = NSLocalizedString("OK", comment: "")
let alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .Alert)
// Create the action.
let cancelAction = UIAlertAction(title: cancelButtonTitle, style: .Cancel) { action in
NSLog("The simple alert's cancel action occured.")
}
// Add the action.
alertController.addAction(cancelAction)
presentViewController(alertController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Changes in AppDelegate.swift file
//
// AppDelegate.swift
//
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var navigationController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()
var testController: TestViewController? = TestViewController(nibName: "TestViewController", bundle: nil)
self.navigationController = UINavigationController(rootViewController: testController)
self.window!.rootViewController = self.navigationController
return true
}
func applicationWillResignActive(application: UIApplication) {
}
func applicationDidEnterBackground(application: UIApplication) {
}
func applicationWillEnterForeground(application: UIApplication) {
}
func applicationDidBecomeActive(application: UIApplication) {
}
func applicationWillTerminate(application: UIApplication) {
}
}
Find code sample and other information on
http://ashishkakkad.wordpress.com/2014/06/16/create-a-application-in-xcode-6-ios-8-without-storyborard-in-swift-language-and-work-with-controls/
In iOS 13 and above when you create new project without storyboard use below steps:
Create project using Xcode 11 or above
Delete storyboard nib and class
Add new new file with xib
Need to set root view as UINavigationController SceneDelegate
add below code:
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 windowScene = scene as? UIWindowScene {
self.window = UIWindow(windowScene: windowScene)
let mainController = HomeViewController() as HomeViewController
let navigationController = UINavigationController(rootViewController: mainController)
self.window!.rootViewController = navigationController
self.window!.makeKeyAndVisible()
}
}

Resources