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...)
Evolution of Levels
07/01/14 04:51 PM Filed in: iOS | Development
During my extended absence from blogging, I have been hard at work (well, mostly hard at work!) on a new iOS app. It started off as a fun exercise, and has turned into a game. One of the areas of the game I’ve reworked multiple times is the method of providing game level data. The game didn’t even really have levels, but as I played it and tested it, and played other iOS games, adding progressive levels felt like the right way to go. At first, the levels were completely hardcoded into an array of C structures. This worked to get things started. Then, I decided that being completely generated was the way to go. I wrote and entire level generator, although it had rules and parameters so each level wasn’t completely random. It worked nicely, but didn’t seem all that great for challenges between players, or replaying to improve your score. So, jumping onto the JSON train, I next specified the each level format in a JSON-based file, which I loaded at startup. This was a very pleasant format to write (much better than plists)(you can only click on those little tiny + and - icons in Xcode’s plist editor so many times before going crazy), and gave me a certain amount of flexibility. However, it just felt so heavy... all those arrays of strings. I don’t think it was a huge performance problem, but I just felt like it was heavier than I needed. So, back to C structures. But this time, just a small change in how I formatted the default entries in the header file (comments and putting each field on its own line ) made it feel like JSON, but I got back my beloved bitfields and integer-based efficiencies. One area that had pushed me toward JSON was the need to specify some help text per level. Putting ObjC strings into C structures is not really doable, but I came up with a workaround. The strings I needed for each level were written with each structure, but commented out and included an easily grep-able tag. One quick build phase where I ran some grep/sed magic over my header file and generated a .strings file, and I was in business. A great bonus is that some day I could localize the app more easily. Plus, one quick memcpy of the pre-initialized structure into heap-allocated memory meant I could have the best of both worlds -- some values computed at runtime and most values set up ahead of time in a easy-to-read and modify header file. Having things in C also makes it possible to do some debug-only startup validation of the data that would be more difficult to do in a JSON/Dictionary based solution.
Something that seemed like it should be straightforward has certainly taken me a while to figure out. This might not even be the final iteration. But, I’ve coded up the first 10 levels of the game using the new structure (and due to some good factoring in my code, the changes to support an entirely new level format were mostly hidden in one class), and it feels pretty good. One of the reasons I hadn’t finished specifying all of the levels in the game (I’d like to release with 50 levels to start), had been partly due to the cumbersome nature of adding new levels. I’m hoping that the next 40 levels go as easily as the first 10 went.
Good luck designing your own apps! Don’t be afraid to iterate and throw out something that doesn’t feel like it’s working for you. It might have cost me a few hours to rework this code, but if it means I am able to move forward in completing a part of the app that had been annoying me, then it was most definitely time well spent.
Something that seemed like it should be straightforward has certainly taken me a while to figure out. This might not even be the final iteration. But, I’ve coded up the first 10 levels of the game using the new structure (and due to some good factoring in my code, the changes to support an entirely new level format were mostly hidden in one class), and it feels pretty good. One of the reasons I hadn’t finished specifying all of the levels in the game (I’d like to release with 50 levels to start), had been partly due to the cumbersome nature of adding new levels. I’m hoping that the next 40 levels go as easily as the first 10 went.
Good luck designing your own apps! Don’t be afraid to iterate and throw out something that doesn’t feel like it’s working for you. It might have cost me a few hours to rework this code, but if it means I am able to move forward in completing a part of the app that had been annoying me, then it was most definitely time well spent.