Why is AppDelegate.swift window an optional? - ios

I was reading Apple docs, when I found this sentence:
The AppDelegate class contains a single property: window.
var window: UIWindow?
This property stores a reference to the app’s window. This window
represents the root of your app’s view hierarchy. It is where all of
your app content is drawn. Note that the window property is an
optional, which means it may have no value (be nil) at some point.
What I don't understand is: why this property at some point could be nil? What's the case for it to be(come) nil?

When you close your application, your app can still receive silentNotifications or download data in the background, track your location, play music, etc.
In the images below, the encircled red are for when your app is still doing something, however it is no longer on the screen. It's in the background, so AppDelegate doesn't need a window anymore. As a result it will be set to nil
Simple overview
Detail overview
FWIW, the code below won't make the app launch with vc.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let vc = ViewController()
window?.rootViewController = vc
window?.makeKeyAndVisible()
return true
}
Why it doesn't work? Because the window property is an optional—initially set to nil.It needs to be instantiated
The code below will work
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let vc = ViewController()
window = UIWindow(frame: UIScreen.main.bounds) // Now it is instantiated!!
window?.rootViewController = vc
window?.makeKeyAndVisible()
return true
}

You may not always need it. For example, when these two methods are called :
application(_:performFetchWithCompletionHandler:)
application(_:handleEventsForBackgroundURLSession:completionHandler:)
your app will not be shown to the user, so there is no need for a window.
As always, more in the docs
Now, I'm not sure that this is the inherent reason, but it seems as a good enough possibility (at least for me). Though if someone is able to provided some more information, I'd gladly learn some more also.

It becomes more obvious when you create the window programmatically instead of using a main storyboard, which automatically sets the window property.
You may not want to or not be able to create the window immediately when your delegate object (AppDelegate in your case) is created. Usually you don't need to create the window and set the property until application(_:didFinishLaunchingWithOptions:) was called. So until the window is created and the property is set it will be nil.
Like Losiowaty already stated this is also the case when the app is started but not displayed to the user - e.g. when just processing location updates or other information in the background.
If the property would be non-optional then you would be forced to create the window at the moment you create your AppDelegate object which is neither desired nor necessary.

Related

How to set the Initial viewController in AppDelegate Programatically?

I know that this question was asked a couple of times but I tried all the solutions and nothing really worked for me.
I am currently working on an iOS Project and encountered a problem.
I am building an App which needs some personal information at the first launch. Therefore I use a NavigationController which I set to be the Initial View Controller in Storyboard.
Now I would like to check in my AppDelegate whether the User already entered the Personal Data. In this case I created a User Defaults KV Pair called "onboarding" of type Bool
If the Value is true, I would like to change the Initial View Controller to the main TabBarController, the rest of the App uses.
I hope this image can illustrate what I meant above:
I hope you can think of a way to solve this problem.
Thanks a lot for your help in advance. Have a great day!
1) Open your AppDelegate.swift and add property
var window: UIWindow?
2) In
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
a) initialize this window:
window = UIWindow(frame: UIScreen.main.bounds)
b) set UIViewController you needed to window's rootViewController property:
window?.rootViewController = myViewController
c) make this window key and visible:
window?.makeKeyAndVisible()
That is all.
And don't forget to remove your storyboard from target settings:
Remove storyboard from target
Hope its help.

How do I transition my project from using a Storyboard to no longer using one?

If I have an Xcode project that already uses a Storyboard, and now I want to switch to loading the view controller programmatically, how do I do that?
Here is an in-depth article about doing this. The TLDR is:
In the project settings, clear the Main Interface:
In your didFinishLaunching use code such as the following:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let homeViewController = UIViewController()
homeViewController.view.backgroundColor = UIColor.red
window!.rootViewController = homeViewController
window!.makeKeyAndVisible()
return true
}
Slowly. One step at a time.
Storyboards are not all-or-nothing. You can continue to use a Storyboard for places where it is helpful, and you can use programmatic loading for places where it isn't, and you can transition between them one step at a time.
A first step if you wanted to unwind a piece from the Storyboard is to remove the segues leading in and out of it, and then programmatically load that view controller from the Storyboard with instantiateViewController(withIdentifier:).
You could then pull a given scene into its own separate Storyboard (select the scene, Editor, Refactor to Storyboard...) so that it's completely independent of all the other scenes. Then you can rework that one view controller as programmatic code without interrupting anything else in the project. You can also mix-and-match here; loading the view controller from the Storyboard, but handling initialization by hand in init(nibName:bundle:).
You can also cut and paste a storyboard view into its own XIB file, which sometimes is easier to work with, without giving up the GUI layout system. Select the view, copy, create a new XIB file (it's called "View" in the templates), and paste it. This is a bit easier to use with init(nibName:bundle:).
But the key point is to not try to rework your entire project all at once. If there's some portion where Storyboards are causing trouble, refactor them out. If you just like programmatic code better, then keep refactoring one step at a time until you're done or until you've done enough to be happy.
As Senseful's answer notes, the top-level scene is slightly special. You can change it with or without changing the other scenes.

Setting root VC programmatically

I'm a Swift beginner, so take it easy on me. I'll be specific in my question.
I deleted the storyboard (I want to learn how to build UI programmatically using Swift).
The below code is placed in the AppDelegate.swift
I also have ViewController.swift in the project explorer.
Firstly, is there a way to use anything other then UINavigationController..? Or having a UINavigationController is a must requirement..?
If not a must, how can I just refer it to a ScrollView for example..?
2ndly, with further research into Apple's own guide, they stated I can also use window?.isHidden = false... Is there a difference between using the former line, and window?.makeKeyAndVisible()..?
Sorry if my question doesn't make sense programmatically, like I said, I'm a beginner, but I'm determined to understand why I write the code I write or copy.
Thank you.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = UINavigationController (rootViewController: ViewController())
return true
}
...
}
A window needs a view controller. It doesn't have to be a UINavigation Controller, but it does need to be a view controller. Not all apps start with a navigation controller. In fact, you can see this yourself by looking at the storyboard that Xcode provides for you in new projects. It's just a ViewController. Can you use a ScrollView instead? If you put the scrollview (which is a UIView) into a UIViewController, sure you can.
Hiding a window just makes it appear and disappear. Making it the key window means that it will be the window that receives events. You might not think that it is important in iOS apps, but iOS apps can have more than one window. In the case of starting an application, it's a good idea to define the window that will be visible and be key, not just the one that will be visible.
Firstly, is there a way to use anything other then UINavigationController..? Or having a UINavigationController is a must requirement..?
You can use any UIViewController subclass as the root view controller. e.g. UITabBarController, UIPageViewController, UIViewController, UITableViewController...
how can I just refer it to a ScrollView for example..?
You can't set a UIScrollView as the root view controller. You can, however, add the UIScrollView as a subview of the UIWindow. I don't recommend you to do this though, because using VCs will make your code more manageable, with different classes managing different views.
Is there a difference between using the former line, and window?.makeKeyAndVisible()..?
Yes, if you look at the docs of makeKeyAndVisible:
This is a convenience method to show the current window and position it in front of all other windows at the same level or lower. If you only want to show the window, change its
isHidden property to false.
So yeah, calling makeKeyAndVisible will make the window the "key window".
According to here, key window behaves like this:
The key window receives keyboard and other non-touch related events. Only one window at a time may be the key window.

iOS programming: Class and Instance

I think I might ask a fool question but it does confuse me for a while.
I am a beginner for iOS developing and the example I have seen online that people always write code inside the viewController's class.
However, according to my experience in C++, I think a class is just a template for reuse. You can only use it when it has been initialized.
So the thing that performs the work is the instance.
My question would be when/who create the viewController instance in an app?
I imagine you are referring to the template ViewController, the one that always comes with a new single page application project, for instance.
It's being created "under the hood" once your app finishes launching, but what it is basically doing is the following:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = ViewController();
return true
}
In fact, that's what you need to do if you want to delete your storyboard and work fully programmatically, in addition to clearing the Main Interface info in you project's general info as follows:
And if you want to show another custom ViewController class, you can present it from another ViewController as in
let secondViewController = MyCustomViewController()
// this line will place the MyCustomViewController instance on top of the current ViewController
present(secondViewController, animated: true, completion: nil)
I hope I could help!

Swift: How should the App Delegate get a reference to the View Controller?

I've started a new Swift project, I'm playing around with things to see how the wiring works with storyboards since I've never used them before.
The project is a single-view app using the default storyboard created by Xcode 6.1. It generates the AppDelegate.swift and ViewController.swift classes as well as Main.storyboard.
Btw, I'm kind of going off of this tutorial:
http://www.raywenderlich.com/74904/swift-tutorial-part-2-simple-ios-app
I've got things working with a button and a couple of textview controls that I added using the storyboard Interface Builder.
What I'd like to do now, is hook up the app delegate's application didFinishLaunching event to the view controller.
func application(application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[NSObject: AnyObject]?) -> Bool {
}
I've found many StackOverflow articles discussing this, however the examples are all around instantiating your own view controller. I want to simply get a reference to the view controller that was launched via the storyboard.
What's the best way to do this? Feel free to point me to the appropriate docs, or other posts.
I finally found this article on checking the current view controller, which had the logic I was looking for:
var myViewController : ViewController!
...
if let viewControllers = self.window?.rootViewController?.childViewControllers {
for viewController in viewControllers {
if viewController.isKindOfClass(ViewController) {
myViewController = viewController as ViewController
println("Found the view controller")
}
}
}

Resources