In the beginning...
07/15/14 11:02 AM Filed in: Development
I’m taking a Swift break today and getting my feet wet with a few sample apps. Even though I’ve read the whole Swift book and watched a fair number of the WWDC Swift videos, playing around with “real” apps is the only way I’ll start to get a handle on what Swift really is like. Plus, when you are feeling frustrated with your current project (I’m looking at you - annoying GameCenter bugs!), trying out a shiny new language is just the ticket.
In my typical dive-way-too-deep approach, I got curious about how Swift apps get started. I found some answers at StackOverflow, but thought I’d summarize my findings here, if only to remind myself of them in the future!
App startup is a bit different between iOS and Mac apps. In both cases, some form of the function UIApplicationMain gets called. In the case of Mac apps, there is still a main.swift file that is created as part of a default Swift project template. Like main.c before it, main.swift appears to exist solely to call NSApplicationMain. NSApplicationMain is responsible for loading up the initial nib file. This nib file is listed in the Info.plist file, under the NSMainNibFile key. The default nib file that is created for you takes care of the rest. There is an object declared in the nib which implements the NSApplicationDelegate protocol. It is also linked to the file owner (NSApplication in this case) so that the main application object’s delegate property points to your AppDelegate object. And so all is good and the your application delegate gets registered with the main application object, and the app proceeds normally.
For an iOS app created with one of the default XCode templates, it is a little bit different. First, there is no main.swift. The Supporting Files folder is a bit empty and lonely with just Info.plist hanging out there. But, there is an automatically created AppDelegate.swift file, just like in an Objective-C project. At first, I missed the magic bit of code which makes this all work. Also, I was thinking that for many simple iOS apps, you wouldn’t even really NEED an application delegate. If you were explaining to a first-time iOS programmer why that class even existed, it would be “well, just ignore it for now”. But, turns out that you can’t just ignore application delegates. The Apple docs state that “Every app must have an app delegate object to respond to app-related messages.” But then again, every single method of the UIApplicationDelegate protocol is optional, so even though it has to exist, it doesn’t need to actually do anything.
The equivalent function to NSApplicationMain on MacOS is UIApplicationMain on iOS. It takes two additional arguments, which are not present on the Mac version - the name of the UIApplication subclass name (if one exists - otherwise it assumes you just want to use UIApplication. It also takes the name of the UIApplication delegate class name. In Objective-C based projects, Xcode would just fill this in with the name of the AppDelegate class it created for you. However, since this file and explicit call to UIApplicationMain are now gone in Swift, how does it find your application delegate?
The secret lies in a special type attribute associated with your application delegate class. Just before the declaration of that class, you will see the @UIApplicationMain attribute specified. If you remove it, then your app fails to compile because it is missing a main entry point. If you remove the class declaration, but leave the attribute, you get a compiler error since the attribute needs to be associated with a class. If you change your delegate class to not conform to the UIApplicationDelegate protocol, you’ll also get an error, telling you that the @UIApplicationMain class must implement that protocol. So, this attribute is special and needs to be associated with an application delegate.
So in the case of Swift apps, the application delegate is also the means by which the initial entry-point into the app is created.
Thank you to this StackOverflow thread, which pointed me in the right direction!
This thread discusses creating your own entry point to NSApplication by creating a main.swift file, removing the @UIApplicationMain attribute and calling UIApplicationMain yourself, passing in a custom subclass of UIApplication. This brings things back closer to the way they used to work in Objective-C. Doing it the new Swift way saves you creating the main.swift file, at least. If you include the main.swift file in your project, even if it doesn’t have any code in it, the compiler will yell at you and make you choose between using main.swift and the @UIApplicationMain attribute. When specifying the name of the delegate class, the example code from SO used “NSStringFromClass(AppDelegate)”. However, you could also pass in “AppName.AppDelegateClassName” instead -- a good reminder that all classes now exist in a namespace based on the app name.
I don’t think this is anything I’ll have to think about in the future, but I like knowing how the flow of the application works (“It just starts up magically” isn’t as satisfying an answer...)
In my typical dive-way-too-deep approach, I got curious about how Swift apps get started. I found some answers at StackOverflow, but thought I’d summarize my findings here, if only to remind myself of them in the future!
App startup is a bit different between iOS and Mac apps. In both cases, some form of the function UIApplicationMain gets called. In the case of Mac apps, there is still a main.swift file that is created as part of a default Swift project template. Like main.c before it, main.swift appears to exist solely to call NSApplicationMain. NSApplicationMain is responsible for loading up the initial nib file. This nib file is listed in the Info.plist file, under the NSMainNibFile key. The default nib file that is created for you takes care of the rest. There is an object declared in the nib which implements the NSApplicationDelegate protocol. It is also linked to the file owner (NSApplication in this case) so that the main application object’s delegate property points to your AppDelegate object. And so all is good and the your application delegate gets registered with the main application object, and the app proceeds normally.
For an iOS app created with one of the default XCode templates, it is a little bit different. First, there is no main.swift. The Supporting Files folder is a bit empty and lonely with just Info.plist hanging out there. But, there is an automatically created AppDelegate.swift file, just like in an Objective-C project. At first, I missed the magic bit of code which makes this all work. Also, I was thinking that for many simple iOS apps, you wouldn’t even really NEED an application delegate. If you were explaining to a first-time iOS programmer why that class even existed, it would be “well, just ignore it for now”. But, turns out that you can’t just ignore application delegates. The Apple docs state that “Every app must have an app delegate object to respond to app-related messages.” But then again, every single method of the UIApplicationDelegate protocol is optional, so even though it has to exist, it doesn’t need to actually do anything.
The equivalent function to NSApplicationMain on MacOS is UIApplicationMain on iOS. It takes two additional arguments, which are not present on the Mac version - the name of the UIApplication subclass name (if one exists - otherwise it assumes you just want to use UIApplication. It also takes the name of the UIApplication delegate class name. In Objective-C based projects, Xcode would just fill this in with the name of the AppDelegate class it created for you. However, since this file and explicit call to UIApplicationMain are now gone in Swift, how does it find your application delegate?
The secret lies in a special type attribute associated with your application delegate class. Just before the declaration of that class, you will see the @UIApplicationMain attribute specified. If you remove it, then your app fails to compile because it is missing a main entry point. If you remove the class declaration, but leave the attribute, you get a compiler error since the attribute needs to be associated with a class. If you change your delegate class to not conform to the UIApplicationDelegate protocol, you’ll also get an error, telling you that the @UIApplicationMain class must implement that protocol. So, this attribute is special and needs to be associated with an application delegate.
So in the case of Swift apps, the application delegate is also the means by which the initial entry-point into the app is created.
Thank you to this StackOverflow thread, which pointed me in the right direction!
This thread discusses creating your own entry point to NSApplication by creating a main.swift file, removing the @UIApplicationMain attribute and calling UIApplicationMain yourself, passing in a custom subclass of UIApplication. This brings things back closer to the way they used to work in Objective-C. Doing it the new Swift way saves you creating the main.swift file, at least. If you include the main.swift file in your project, even if it doesn’t have any code in it, the compiler will yell at you and make you choose between using main.swift and the @UIApplicationMain attribute. When specifying the name of the delegate class, the example code from SO used “NSStringFromClass(AppDelegate)”. However, you could also pass in “AppName.AppDelegateClassName” instead -- a good reminder that all classes now exist in a namespace based on the app name.
I don’t think this is anything I’ll have to think about in the future, but I like knowing how the flow of the application works (“It just starts up magically” isn’t as satisfying an answer...)