Dev Tip: Nibless Development

Posted: August 15, 2010 in Development, Tips & Tricks
Tags: , , , ,

I’ve never really used the Interface Builder all that much when developing, and frankly I don’t like the “magic” it does for me. I prefer being able to visualize what my application does in the code by avoiding the NIB altogether — and I’m going to show you how I go about doing this.

Why ?
Well, there are projects where it just doesn’t make sense to use Interface Builder, rendering the NIB obsolete to the project itself. This is true for projects that have little or no GUI based on the AppKit, like fullscreen games. Games in particular usually incorporate their own GUI, which is managed by the engine.

How ?
As far as I know, there is no Apple-sanctioned method of approach of doing nibless development under cocoa, and the method I will show you is probably not the only way — or the best, for that matter.

The standard main.m looks something like this:

#import <Cocoa/Cocoa.h>

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

    return NSApplicationMain(argc,  (const char **) argv);
}

The return NSApplicationMain(argc, (const char **) argv) instantiates your application by creating the singleton object NSApplication, loading the MainMenu.xib (the nib) and calling the run method on your NSApp. In order to avoid the nib, we need to circumvent this by initiating the application ourselves.

The first thing we do, is to get rid of the NSApplicationMain function call. In it’s place we simply return zero, as cocoa will call exit() before reaching the return instruction of the main function, so this never gets executed anyway. Now that our main function looks like a generic C program, we can begin recreating what the NSApplicationMain did for us.

We will need to wrap everything in an autorelease pool, and make sure we have an instantiated application — we do this as such:

#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[])
{
    // create an autorelease pool
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
    // make sure the application singleton has been instantiated
    NSApplication * application = [NSApplication sharedApplication];
    
    // drain the autorelease pool
    [pool drain];
	
    // execution never gets here ..
    return 0;
}

Now that we have made sure we have an application to run, we can go on and create an application delegate — if not already present in your project.

Create a new Objective-C NSObject subclass, name it AppDelegate. In the @interface section found in the header of your new NSObject subclass, add the NSApplicationDelegate protocol to it as such: @interface AppDelegate : NSObject <NSApplicationDelegate>

Import the AppDelegate’s header to your main.m as such: #import "AppDelegate.h"

Now that your AppDelegate implements the NSApplicationDelegate protocol and your main.m knows about it, we can create an instantiation of the application delegate and assign it to the NSApplication, and then all we really need to do is to call the run method:

#import <Cocoa/Cocoa.h>

#import "AppDelegate.h"

int main(int argc, char *argv[])
{
    // create an autorelease pool
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
    // make sure the application singleton has been instantiated
    NSApplication * application = [NSApplication sharedApplication];
    
    // instantiate our application delegate
    AppDelegate * applicationDelegate = [[[AppDelegate alloc] init] autorelease];
    
    // assign our delegate to the NSApplication
    [application setDelegate:applicationDelegate];
    
    // call the run method of our application
    [application run];
    
    // drain the autorelease pool
    [pool drain];
    
    // execution never gets here ..
    return 0;
}

This takes care of the main function. Now all we need to do, is implement how our application delegate handles our application. You can handle this however you want, I’m just going to show you how to create a window with a content view. Keep in mind, that for the sake for keeping the demonstration short and to the point, I do not use custom classes for these, in real projects I would definitely write a class for the content view.

Inside of the class declaration of the AppDelegate, we put an NSWindow and a NSView as such:

@interface AppDelegate : NSObject  {

    NSWindow * window;
    NSView * view;
}

Because the AppDelegate implements the NSApplicationDelegate protocol, there are quite a few methods that it will now respond to and that will get called during run-time. So in the definition of our application delegate, we add the following methods and implementations as such:

@implemention AppDelegate

- (id)init {
    
    if ( self = [super init] ) {
        
        // create a reference rect
        NSRect contentSize = NSMakeRect(0.0f, 0.0f, 480.0f, 320.0f);
	
        // allocate window
        window = [[NSWindow alloc] initWithContentRect:contentSize styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:YES];
        
        // allocate view
        view = [[NSView alloc] initWithFrame:contentSize];
    }
    return self;
}


- (void)applicationWillFinishLaunching:(NSNotification *)notification {
    
    // attach the view to the window
    [window setContentView:view];
}

- (void)applicationDidFinishLaunching:(NSNotification *)notification {
    
    // make the window visible.
    [window makeKeyAndOrderFront:self];
}

- (void)dealloc {
    
    // don’t forget to release allocated objects!
    [view release];
    [window release];
    
    [super dealloc];
}

@end

.. and that concludes my approach of doing nibless development. Notice that I did not implement the main menu, so when you run this the main menu will not work. But there is a solution for that as well, I might write an article about that some time — please do let me know if this is something you would like to see.

Phew, that got a little lengthier than I originally expected it to.

Hopefully you found this useful. If you have any questions or thoughts on this, please leave a comment — Thank you.

Download Xcode Project
I’ve made an xcode project available for download at, just click the link above to get it. It has been build, tested and it works.

UPDATE Fixed a typo; in AppDelegate.m it said ‘@implemention’, while it should have been ‘@implementation’. Thank you albert7546, for pointing out the source wouldn’t compile.

Advertisements
Comments
  1. albert7546 says:

    I am trying to learn Cocoa but so far I haven’t been able
    to successfully compile an executable of the above.

    Is there an Xcode project available for download?

    • Hi albert7546,

      I see, I’ll look into it today. There may be typos, I will go through the examples given and see to it an Xcode project gets attached to the article as soon as possible.

      Sorry for the inconvience, check back later today for the fix 🙂

      Sincerely
      Casper B. Hansen

      • albert7546 says:

        Casper,

        Thank you for helping me out.

        I bought “Objective-C for Dummies” to help me understand all that Cocoa stuff

  2. Alex says:

    Thanks man. This is really helpful.

  3. baffo32 says:

    Thank you for this! I am designing a cross-platform script (hence no interfacebuilder) to create simple applications, and your code finally got it going properly. It would be great to learn how to add a menu some day.

    • No problem, that was exactly the same reason I did it for.

      I have done menus with this approach before, and it was indeed my intention to post a follow-up article which addressed exactly this issue. But school has been a bit rough on me these past months, so I haven’t really had the time to write the article. However, you can certainly expect one such post once I find the time for it.

      I can tell you this much, if I recall correctly, you’ll have to create an NSMenu for each of your menus (ie. File, Edit, etc.), and then add NSMenuItem’s to those — there’s really not much to it, but it isn’t documented anywhere in the Apple documentation, as far as I know, so it can be a little tricky.

      • baffo32 says:

        Here’s what I have for a “nibless menu hello world”. NSMenu is documented on apple’s site, but the nibless key is the setAppleMenu message, which is undocumented and I had to find via google.

        – (void)applicationDidFinishLaunching:(NSNotification *)notification {
        NSApplication *app = [notification object];
        NSMenu *appleMenu = [[NSMenu alloc] initWithTitle: @”Apple Menu”];
        [appleMenu addItemWithTitle:@”Quit” action:@selector(terminate:) keyEquivalent:@”q”];
        NSMenuItem * menuItem = [[NSMenuItem alloc] initWithTitle:@”” action:nil keyEquivalent:@””];
        NSMenu *mainMenu = [[NSMenu alloc] initWithTitle: @”” ];
        [menuItem setSubmenu:appleMenu];
        [mainMenu addItem:menuItem];
        [app setMainMenu:mainMenu];
        [app setAppleMenu:appleMenu];
        [menuItem release];
        [mainMenu release];
        [appleMenu release];

        [window makeKeyAndOrderFront:self];
        }

      • Looks just about right.

  4. Igor says:

    Thanks for the article!
    Although, I have a question: how do you handle a menu item “quit application”?
    Here is this questions at stack overflow: http://stackoverflow.com/questions/9155015/programmatically-handle-quit-yourapp-menu-event-in-cocoa-app-without-xib

    • baffo32 actually answered that question pretty well, here in the comments. Look for his comment, and you should be able to add menus. It was my intention to write a follow-up tutorial on just that, but I’ve been very busy as of late, so I won’t be writing much on the blog – sorry.

  5. Martin says:

    is missing in the download – results in “not implementing” warning. Keep up the good work.

    • Yes, sorry. The download was located on the server for my website. I’ve moved the blog to my website, not the articles though — please go to instead, if you enjoyed the post. I will not be maintaining this blog anymore.

      Thank you.

  6. Kenneth says:

    I compiled this app using gcc but when I launch it from Finder it opens the terminal. Is there a way to suppress this behavior?

  7. Thanks for this guide, it’s what I was looking for!

    Just one minor issue I found in your code:

    @interface AppDelegate : NSObject {

    should be replaced with

    @interface AppDelegate : NSObject

    • For some reason my comment was changed by WordPress.

      The correct code should be:

      @interface AppDelegate : NSObject

    • I’m not maintaining this blog anymore, sorry. I have moved all my activities -including blogging- to my personal domain . The site is in a somewhat broken state at this time because I haven’t had the time to properly set it up due to my studies. I’m aiming to get it up and running smoothly some time during the summer vacation.

      Thanks for your comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s