In an AppDelegate, how is the main UIWindow instantiated?
From my book:
If you choose the Storyboard option as you specify a template, the process works a little differently. The app is given a main storyboard, pointed to by the Info.plist key “Main storyboard file base name” (
UIMainStoryboardFile
). AfterUIApplicationMain
instantiates the app delegate class, it asks the app delegate for the value of itswindow
property; if that value is nil, the window is created and assigned to the app delegate’swindow
property. The storyboard’s initial view controller is then instantiated and assigned to the window’srootViewController
property, with the result that its view is placed in the window as its root view; the window is then sent themakeKeyAndVisible
message. All of that is done behind the scenes byUIApplicationMain
, with no visible code whatever. That is why, in a storyboard template, theapplication:didFinishLaunchingWithOptions:
implementation is empty.
From Apple's docs (in "Using View Controllers in Your App"):
The Main Storyboard Initializes Your App’s User Interface
The main storyboard is defined in the app’s Information property list file. If a main storyboard is declared in this file, then when your app launches, iOS performs the following steps:
It instantiates a window for you. It loads the main storyboard and instantiates its initial view controller. It assigns the new view controller to the window’s rootViewController property and then makes the window visible on the screen.
From the UIWindow
documentation:
Note: When you use storyboards and the Xcode app templates to create an app, a window is created for you.
If you don't use storyboards, the window is explicitly created, though all the standard project templates do this out of the box. You'll see a line similar to this in the app delegate:
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
Using storyboards, the window is created behind the scenes when the main storyboard is loaded (see the View Controller Programming Guide for more info).
The above answers only who sets the window variable without answering the main questions: "But how does window have a non-nil rootViewController if it is never explicitly initialized? Is this just Xcode init'ing behind the scenes?" and seem to suggest that there is magic afoot. Not a satisfactory answer for me, and so with a little digging, all becomes clear.
The generated code defines AppDelegate as
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
...
}
When you search the project, there is no other reference to window, so apparently it should remain nil, but actually is set to the correct value (by the methods outlined above). The "magic" is that AppDelegate conforms to the UIApplicationDelegate which includes an declaration:
optional public var window: UIWindow? { get set }
Part of conforming to the UIApplicationDelegate is the redeclaration of the public variable window. When the underlying Application references the variable window in the protocol, it is actually linked to the variable window in our class. When the calling Application updates that variable window in the protocol, it is actually updating our variable window. So when we need to access the value in our program it is ready and waiting.
This is not Xcode magic, but an integral part of the Swift language. When using protocols we can employ the same techniques in our own Swift programs. This is just the same as our implementations of various functions in our classes which we do all the time: e.g. UIApplicationDelegate defines
optional public func applicationDidEnterBackground(_ application: UIApplication)
so we can write our own implementation which is then "magically" called!
For completeness, note the @UIApplicationMain tag on the class. This defines the entry point for the application and is what makes everything work together. The actual class name is irrelevant, and can be given any name you require, as long as it is of type UIResponder and conforms to the UIApplicationDelegate.