iPhone App Launch Sequence

May 22 2009 Published by under Brevity, Extensibility, iPhone, Objective C

Send to Kindle

I was going over the stuff I’m going to cover next week in my “From Flash to iPhone” workshop at Flash on Tap, and I started getting into how an iPhone app launches when you tap its icon. It might be a bit heavy to go into detail on it at the workshop, but I thought it was pretty interesting and writing about it might shed some light for developers moving past the newbie phase.

Coming from the Flash/Flex world – app start up is pretty simple. You specify a main class. When the SWF is launched, that class is instantiated, calling its constructor. Whatever is in the constructor of that main class is what happens.

But in your typical iPhone apps, you have C files, Objective-C classes, xibs, pchs, frameworks, and plists. Which is the chicken, which is the egg, and which comes first?

First of all, realized that Objective-C is an extension of C to add object oriented capabilities. If you’ve ever taken a C course, or cracked open a book on C, you’re going to see a file like this:

#include <stdlib.h>

int main(int argc, char *argv[]) {
            printf("Hello World");
            return 0;
}

When you run the program, it looks for a function called “main” and runs it. It passes in the number of command line arguments (int argc) and an array of strings that are the arguments. It executes whatever code is in that function and then exits, returning an int – usually 0 means it exited as usual, anything else means some kind of error.

Well, it turns out that Objective-C programs launch in exactly the same way. In fact, if you fire up XCode and create a Window Based Application named “Test”, you’ll see a file under “Other Sources” called “main.m”. It contains this:

#import <uikit/UIKit.h>

int main(int argc, char *argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

This is the first bit of code that gets executed when you launch an app. For the most part, you don’t want to mess with it, unless you really know what you are doing. So what does it do? Creates an autorelease memory pool and then calls a function called UIApplicationMain, passing in the command line arguments, and two other nil arguments. We’ll see what at least one of those is in a bit.

UIApplicationMain creates whatever UI you have created and starts the program loop. Which means it just loops and sits there waiting for events. Eventually at some point, the user quits the program. This ends the execution of the UIApplicationMain method, returning an int return value. The main function releases the memory pool it created and returns that return value to the system. That’s all.

So if UIApplicationMain is a standard function in UIKit, how does it know about your classes and interfaces and stuff? Well, one of the first things it does is to read your Info.plist file, which looks something like this, if you view it as xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>English</string>
	<key>CFBundleDisplayName</key>
	<string>${PRODUCT_NAME}</string>
	<key>CFBundleExecutable</key>
	<string>${EXECUTABLE_NAME}</string>
	<key>CFBundleIconFile</key>
	<string>icon.png</string>
	<key>CFBundleIdentifier</key>
	<string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>${PRODUCT_NAME}</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1.0</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>NSMainNibFile</key>
	<string>MainWindow</string>
</dict>
</plist>

You’ll notice the last key/value pair theire is NSMainNibFile, which is set to MainWindow. You can also set this value in the Target info window:

target_info

You see the “Main Nib File:” entry way down at the bottom. Setting the value in this window, simply rights it into the plist file.

And what do you know, we happen to have a .xib (nib) file called MainWindow.xib in our project! Coincidence? I think not. Let’s take a look at that. Double click on it to open it in Interface Builder. The window itself is uninteresting, but the Document window shows us all the stuff that’s defined in here:

xibcontents

So main starts UIApplicationMain, which looks at the Info.plist file, sees this nib is the main nib, and loads it in. For most of the items in there that are linked to classes, it will create an instance of that class, deserialize any properties, styles, etc. you might have set on it, and then hook up any outlets or actions you might have set on it. So what do we have here?

First we have an entry called “File’s Owner” This is linked to the UIApplication class. So an instance of UIApplication is created. Yay!As clarified in the comments, UIApplication is actually created by the call to UIApplicationMain. The entry here merely links to that instance that gets created. It exists here mainly so we can set its delegate, as we will see soon.

Then we have “First Responder”. This is a bit of a special case, i.e. I don’t fully understand it. ;) But my understanding is that this does not get instantiated per se, but is a fill in for another class that will be responding to events.

Next up we have “Test App Delegate” which is linked to the class TestAppDelegate. If you jump back into XCode, you’ll see that you do indeed have a class with that name. So this class is instantiated.

Finally, we have “Window”, which is of type UIWindow. So we get one of those.

So the result of this is we now have instances of UIApplication, TestAppDelegate, and UIWindow created. Now, if you click on the Window object and look at the Attributes tab in the Inspector, you’ll see there are various properties you can set on it, such as scale mode, alpha, background color, tag, opaque, hidden, etc. The size tab also has dimensional and layout properties that can be assigned. Any properties set here are then set on the newly created objects.

Finally, we go to the connections tab. This is where our outlets and actions are defined. If you look at UIApplication, you’ll see it has an outlet called delegate, which is set to Test App Delegate. This is the same as calling setDelegate on the UIApplication instance, and passing in the instance of TestAppDelegate. Also note that TestAppDelegate has an outlet called window which is set to the Window instance. We are not going to dive into UIApplication’s code, but if you look at TestAppDelegate.h, you’ll see it has a @property called window, which is indeed a UIWindow.

#import <uikit/UIKit.h>

@interface TestAppDelegate : NSObject <uiapplicationDelegate> {
    UIWindow *window;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end

So now our UIApplication instance has a reference called delegate which points to our instance of TestAppDelegate, and that has a referenced called window which points to the UIWindow instance. We are done with the nib.

Now, UIApplication starts its event loop. When certain events occur, it passes them over to its assigned delegate to handle. The first, and most important event it delegates is “applicationDidFinishLaunching”. This means that all this plist and nib loading and deserializing and whatever else it needs to do, is done. So it calls the applicationDidFinishLaunching on its delegate.

And, look at that, our TestAppDelegate class just happens to have a method with that very same name.

- (void)applicationDidFinishLaunching:(UIApplication *)application {

    // Override point for customization after application launch
    [window makeKeyAndVisible];
}

All this does by default is take that window that was set and make it the key, visible window.

And that’s where it ends. If you want to do more, you would add code in that applicationDidFinishLaunching method to create views, view controllers, or whatever else.

Now, if you had created a View Based application, a lot of this would be the same, but you’d also have a TextViewController nib. This would be loaded by the MainWindow nib. This new nib would contain a reference to the TestViewController class, and a UIView linked to the view outlet on that view controller. So an instance of this view controller and a UIView would be created. In applicationDidFinishLaunching, this view would be added to the window:

- (void)applicationDidFinishLaunching:(UIApplication *)application {

    // Override point for customization after app launch
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];
}

OK, but say you don’t even want to use nibs. Can you just create a UIWindow by yourself and make it key and visible? And can you specify an app delegate without a nib? Yes and yes.

Back in our Test application, delete the MainWindow.xib file completely. Also go into Info.plist and remove the main nib file entry. Then open up main.m

Remember when we called UIApplicationMain and we passed in two nil args at the end? Now we see what those are for. They are string representations for two classes. The first one is the principle class name. If you pass in nil, this will use UIApplication as the principle class. Unless you know enough about UIApplication to recreate it or at least subclass it, you probably don’t ever want to mess with this. The last argument is the name of the delegate class you want to use. Again this is a string. So, now that we’ve gotten rid of our nib, we can tell UIApplication to use our TestAppDelegate directly:

#import <uikit/UIKit.h>

int main(int argc, char *argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, @"TestAppDelegate");
    [pool release];
    return retVal;
}

So now we have our UIApplication instance and our TestAppDelegate instance. But we need our window. We can create this in applicationDidFinishLaunching. It usually looks something like this:

- (void)applicationDidFinishLaunching:(UIApplication *)application {
	window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [window setUserInteractionEnabled:YES];
    [window setMultipleTouchEnabled:YES];
	[window setBackgroundColor:[UIColor whiteColor]];
    [window makeKeyAndVisible];
}

If you now wanted to add a view, you could do so something like this:

UIView *view = [[UIView alloc] initWithFrame:window.frame];
[view setBackgroundColor:[UIColor redColor]];
[window addSubview:view];

Well, hopefully that explains a bit about what happens when you fire up your app. And gives you a little more control and a few more options on how to do things.

Send to Kindle

7 responses so far. Comments will be closed after post is one year old.

  • Jerry Beers says:

    You say, “First we have an entry called ‘File’s Owner’ This is linked to the UIApplication class. So an instance of UIApplication is created.” Whenever you see that translucent type of box in a nib, it is referencing what IB calls an “External Object”. If you read the description for this, it says, “This object is a placeholder for an object that exists outside of the contents of this document.” That means the nib does not create the object. It is created externally, but a placeholder exists in the nib to allow you to create connections to it. Show how does UIApplication get created? Take a look at the documentation for UIApplicationMain. It says that “This function is called in the main entry point to create the application object”, so it is UIApplicationMain that instantiates UIApplication. Now take a look at the delegate. In the nib, it is not a translucent box, it is a solid box. That means the nib creates the instance of it. Back to the UIApplicationMain documentation, “Specify nil if you load the delegate object from your application’s main nib file”.

    • kp says:

      Thanks for the clarification. I kind of realized that as I was writing the last part of the entry. Obviously, if you don’t load a nib, the UIApplication still gets created, and it occurs in that UIApplicationMain function. I will correct this.

  • JLM says:

    Keith

    I am sure haXe could do with your input, they have started testing feasibility of compiling haXe(c++) for the iPhone, early days I doubt there is anything you can download Hugh has only started looking the other day, but exciting times of course I am still hoping that they look at Android soon, but theoretically it will be the same haXe.

    http://ncannasse.fr/blog/haxe_for_iphone

    hope the tip off is useful (if you did not catch the news elsewhere), if you want some help on moving from as3 to haxe just email me although not yet tried c++ haxe last time I tried it was tricky to set up on a mac as its is still fairly new.

    Cheers ;j

  • kp says:

    yeah, i did hear mention of that project. i think i’m going to roll with cocos2d for a while. not really looking at helping reinvent a better wheel, more in using something that already works. :)

  • JLM says:

    Supplying the right bolt is maybe a better analogy. The wheel tooling is in essence not contrived framework but more, a language whore, created with a design based on evolved flash so it tends to be surprisingly pliable and good at real UI. I suspect it is a new concept in language conception that goes beyond, and will succeed where dot net bloated cross language (ie IRuby and IPython) is destined to fail, but haXe is still very raw and sometimes dirty, so only time will tell. However it has an ability, and design that allows haxe platform abstraction to an extent that is write once distribute everywhere works. Sorry if I diverge from Iphone focus but believed it was was worth contrasting my view from a native Iphone approach, I may well be wrong but hopefully the evaluation will confirm your commitment and test some of your current assumptions.

  • Niklas says:

    Thank you. I go crazy if I can’t follow program flow from point A to B. And using XCode and IB there’s a lot of points missing…

  • RTS says:

    Hi:

    I noticed this article is more than 2 years old, and nevertheless it helped me a lot to understand the launch sequence that happens behind the curtains in a Cocoa app. Thanks a lot!