Archive for the ‘Xcode’ Category

Failed Slower

Saturday, May 19th, 2007

Normally I refrain from linking to other blog posts. I don’t really consider myself to be part of the blogosphere. (We are the blogocube. We will add your biological and technological distinctiveness to our own. Bring your own beer.) Instead, I figure that you can follow my Favorite Feeds, which you can find in the sidebar of my blog, in my favorite feed reader, which you can get from Vienna Downloads. By the way, I just updated my Favorite Feeds today.

In the case of Fail Faster by Terrence Talbot, resistance to linking was futile. His blog was not yet in my Favorite Feeds, though it is now, and more important, he stole my idea! (I actually have a file named ideas.txt.) Anyway, read the post, because it’s a good idea, naturally. I implemented the idea back in Vienna revision 446, so I guess I can’t complain that I didn’t have enough time to publish it. D’oh! I’ll just add my two cents, which by coincidence is my standard consulting fee. Another source of build failures is Xcode itself. Believe it or not, Xcode still has a few bugs. Sometimes the only way to get your project to build correctly is to Clean first. When you Clean, however, any copied resources are deleted, so if your Copy Resources phase is at the beginning of the build, you may be needlessly repeating the copy phase.

This post as you know it is over. Length is irrelevant. Closing tags, on the other hand, are relevant. You must add </p>. Comply.

Working without a nib, Part 1

Wednesday, May 16th, 2007

There are a number of reasons why you might want to build your application without a nib. As you may know, ‘nib’ is an acronym for no inspecting bindings. Anyone who uses version control (in other words, anyone except R.B.) can see in the diffs that nib files are rather opaque. The next Interface Builder, the one in Mac OS X 10.5 Leopard, may offer some improvements in this area, although I can’t really say yet. This is not to say that I’m under NDA. It’s not to say that I’m not under NDA either. I am in fact under NDA, so many NDA it makes my head spin. I can’t even say how many NDA. This is not to say that my NDA prevent me from saying how many NDA I’m under. I just don’t know. Anyway, I don’t have a Leopard seed. Even if I did have Leopard, which I don’t, it isn’t scheduled for release until October, and besides, many developers will still want to support Tiger at the very least for years to come. Thus, my topic is not necessarily obsolete at startup, unlike the computer you just bought.

It is indeed possible to to run a Cocoa application entirely without a nib: create a Cocoa application project in Xcode, delete MainMenu.nib, and remove the NSMainNibFile entry from Info.plist. (You do get the log error Could not connect the action buttonPressed: to target of class NSApplication.) You can allocate your own objects in main(), though you must do that before calling NSApplicationMain(), not after. (It’s too late, Jim, he’s dead already.) Remember, Jim, main.m is just another Objective-C source file. Objects that you allocate before NSApplicationMain() can even register to receive notifications such as NSApplicationDidFinishLaunching, so you can hook into the launching process and perform whatever setup you need.

For all practical purposes, however, you can’t really work without a nib. The problem is the main menu, or more specifically, the application menu. In a normal Cocoa application, the application menu is what would be returned by [[[NSApp mainMenu] itemAtIndex:0] submenu]. In MainMenu.nib of a default Xcode Cocoa application project, the application menu is the submenu for the item NewApplication. Actually, it doesn’t matter what the title is of this menu item, because regardless of how you set it in Interface Builder, Cocoa replaces the title at runtime with an empty string, and the name of the application boldly goes on the menu bar. The title of the application menu itself is Apple, which is highly misleading, considering that its left-side neighbor on the menu bar has an Apple icon at the top. Just to be clear, the Apple menu is not the menu with an Apple. Got it?

What prevents our nibless existence is that the application menu refuses to be set programmatically. If there is no main nib file or no main menu in the main nib, Cocoa creates an application menu automatically, and there appears to be no API to access it. You could create your own NSMenu with an NSMenuItem at index 0 having a submenu with the title @"Apple", but if you try to set it as the main menu with -[NSApplication setMainMenu:], all of your submenus, including your Apple menu, will appear to the right of the automatically generated application menu. Like a poor marksman, you’ll keep missing the target!

When faced with a no-win situation, what do we do? Cheat! My workaround to the problem is to keep MainMenu.nib in the Cocoa project but leave only the most minimal main menu possible. The Window can be deleted entirely, and in the MainMenu, delete everything except the NewApplication menu item and its submenu, which is listed as the Application Menu in the Inspector. The submenu’s menu items can be deleted. If you allow the main menu to come from the nib, then the Apple menu will automatically become part of -[NSApplication mainMenu], and you can populate it and the rest of the main menu yourself. The most elegant way of populating the main menu, in my opinion, would be to subclass NSApplication, set your subclass as NSPrincipalClass in Info.plist, call [self mainMenu] in your subclass awakeFromNib, and add items to the returned menu. (The NSPrincipalClass of your app needs to be a subclass of NSApplication, otherwise your app crashes on launch with a selector not recognized exception.)

In Part 2 of the series, I’ll attempt to add some functionality to my currently useless, empty app. My ongoing mission will be to explore the strange new world of life without (much of) a nib. It’s a difficult concept, perhaps not very logical, but we learn by doing.

Backing up your app’s support files

Sunday, April 22nd, 2007

I’m not only the co-President of the Vienna Club for Men, Women, and Others: I’m also a client. Before I became a Vienna developer, I was a user. During development and debugging of the app, I don’t want to corrupt my user data, e.g., database and preferences, so I back them up before I do anything dangerous. Believe me, anytime I launch Xcode, it’s dangerous.

A long time ago I created a set of Automator actions to back up the files and restore them afterward. Yes, I’m the one of the five or so people in the world who actually use Automator. (Otto who? Auto Parts?) However, after I bought a laptop — or portable computer not for use on your lap under any circumstances, on penalty of burnt naughty bits, as they say — and I started using a FileVault account, I discovered that Otto and FileVault don’t play well together. (They can’t agree on who has to be Samwise.) Thus, I needed a new method of backing up and restoring my Vienna files.

Presto Change-o, Hocus Pocus, I created a custom Xcode project to do the job. At first I was just going to provide it to Vienna developers, but then I decided that I could (semi-)easily make it configurable to handle the files of other apps, since application support files tend to reside in standard locations. Later, greed got the better of me, and I decided to charge $50 a head for my creation. Finally, sanity got the better of me, and I’m making the source available for download. You can still send me $50 if you like, $100 if you have two heads.

The project is called BackupAppFiles, which is pretty boring, I admit, but hey, my parents named me Jeff Johnson. Anyway, BackupAppFiles comes configured to handle Vienna support files. To handle the files of a different app, just change the string constants in BAFConstants.m to match the file names. You can compile different versions to handle different sets of files on the same computer, but you’ll need to change the PRODUCT_NAME build setting for each version, because the location of BAF’s own support files depends on that setting.

The standard disclaimers apply once again. I shall not cause harm to any vehicle nor the personal contents thereof, etc. Please back up your files before using BackupAppFiles. (Yes, I recognize the irony there. No need to point it out, really.) I take no responsibility if BackupAppFiles erases your hard drive or makes your head explode, though I will refund your $50 in the latter case. (It happens sometimes. People just explode. Natural causes.) Let me know if you have any questions, suggestions, or complaints. About BackupAppFiles, that is. Or a plate of shrimp. I’ll also take bug reports and patches, just no feature requests. Now let’s go get sushi.

Everything you always wanted to know about NSApplication

Saturday, March 10th, 2007

It seems that waiting for me to write a new blog post is like waiting for Larry David to write a new TV show. By the way, season one of Lap Cat Software Blog is available now on DVD for the low price of free ($35 shipping and handling). For those of you who don’t care for reruns, the wait is finally over: today is the season two premiere blog post! If you recall, in last season’s finale I was abducted by aliens and replaced by an evil clone. As we begin this season, I’m still the clone, but I’ve mellowed and become Lieutenant Governor. Confused?

I was confused about calling -[NSObject performSelector:withObject:afterDelay:] during application launch. Specifically, I was interested in a delay of (NSTimeInterval)0.0. When exactly, if ever, would the selector be performed? Is there a chance that user events could intervene? According to the documentation, The run loop for the current thread must already be running and in the default mode (NSDefaultRunLoopMode) for aSelector to be sent. For the purposes of investigation, I created an Xcode test project. In typical overkill fashion, I decided that getting to the bottom of the issue would require overriding every public method of NSApplication, NSMenu, NSRunLoop, NSThread, and NSWindow, along with some selected super and protocol methods. I threw in NSAutoreleasePool for good measure. In my main(), I put the following commands before the call to NSApplicationMain():

[[JJApplication class] poseAsClass:[NSApplication class]];
[[JJAutoreleasePool class] poseAsClass:[NSAutoreleasePool class]];
[[JJMenu class] poseAsClass:[NSMenu class]];
[[JJRunLoop class] poseAsClass:[NSRunLoop class]];
[[JJThread class] poseAsClass:[NSThread class]];
[[JJWindow class] poseAsClass:[NSWindow class]];

There was probably an easier way to do this, but I did what I did. I yam what I yam. You can download my project. As usual, it’s released under the SHAG license.

Leaving out many details, here is the basic sequence of events when you launch a Cocoa application. Your results may differ, so talk to your doctor before betting your life on this sequence.

  1. +[NSApplication sharedApplication].
  2. -[NSMenu initWithCoder:]
  3. -[NSWindow initWithContentRect:styleMask:backing:defer:]
  4. -[NSApplication setMainMenu:].
  5. -[NSApplication setWindowsMenu:].
  6. -[NSApplication setServicesMenu:].
  7. awakeFromNib.
  8. Enter -[NSApplication run]. This is the point of no return. That is, you never return from this method.
  9. Enter -[NSApplication finishLaunching].
  10. NSApplicationWillFinishLaunchingNotification.
  11. -[NSApplication makeWindowsPerform:@selector(_registerDragTypesIfNeeded) inOrder:NO].
  12. Return from -[NSApplication finishLaunching].
  13. Enter -[NSApplication nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:kCFRunLoopDefaultMode dequeue:YES].
  14. -[NSApplication activateIgnoringOtherApps:NO].
  15. NSApplicationDidFinishLaunchingNotification. Notice that the app already returned from finishLaunching a while ago (relatively speaking).
  16. -[NSApplication makeWindowsPerform:@selector(_visibleAndCanBecomeKey) inOrder:NO].
  17. Return NSEvent with type NSAppKitDefined, subtype NSApplicationActivatedEventType, from -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:].
  18. Enter -[NSApplication sendEvent:] with the same event.
  19. NSApplicationWillBecomeActiveNotification.
  20. NSApplicationDidBecomeActiveNotification.
  21. Return from -[NSApplication sendEvent:].

A few interesting notes:

  • NSThread. As you would expect, all of the application’s methods are called on the one, main thread. I’ve defined the macro JJTHREADLOGGING to verify this. For the main thread, which is returned by +[NSThread currentThread], none of the methods +[NSThread alloc], +[NSThread allocWithZone:], or -[NSThread init] are ever called.
  • NSAutoreleasePool. An autorelease pool is not created until after -[NSApplication finishLaunching] returns. If you call autorelease in your application delegate method applicationWillFinishLaunching:, an autorelease pool will not be created! Memory leak, anyone? The methods +[NSAutoreleasePool allocWithZone:] and -[NSAutoreleasePool init] are called many times, but they always return the same object, while -[NSAutoreleasePool release] and -[NSAutoreleasePool dealloc] are never called.
  • NSRunLoop. The run loop for the main thread is created in -[NSApplication setServicesMenu:]. Strangely, the run methods for the run loop ( -[NSRunLoop acceptInputForMode:beforeDate:], -[NSRunLoop limitDateForMode:], -[NSRunLoop run], -[NSRunLoop runMode:beforeDate:], -[NSRunLoop runUntilDate:] ) are never called.

If you’d like to try my project for yourself or modify it, I’d be very interested to hear about what you learn. I haven’t really investigated application termination, for example. You may have noticed that I also haven’t addressed how -[NSObject performSelector:withObject:afterDelay:] works during launch. I’m such a tease! These questions — and many others — will be answered in the next episode.

From the days of yore

Friday, October 20th, 2006

When you work in an office, or more precisely a cubicle, you often receive an inheritance from the previous occupants. In my last office I inherited a number of books, which I didn’t pay much attention to until I moved out. Among the chaff, I recently discovered some wheat. Well, not really wheat: more like wood pulp, quite inedible wood pulp, but interesting inedible wood pulp nonetheless.

  1. Macintosh Pascal by Robert Moll and Rachel Folsom, 1985.
  2. Macintosh Revealed: Unlocking the Toolbox by Stephen Chernicoff, 1987.
  3. Macintosh Revealed: Programming with the Toolbox by Stephen Chernicoff, 1987.
  4. Macintosh 512K enhanced by Carol Kaehler, Apple Computer, 1986.

The last book is particularly interesting, because it’s a manual. That’s right, folks, believe it or not, Macs used to come with a manual! Actually, Macs still come with a manual, but it’s mostly a hardware manual, whereas the old manual gives extensive instructions on how to use the operating system and software. I could tell that it was from the 80s without even looking at the publication date, because the people in the photos are all wearing official 80s uniforms, hairstyle included. I imagine that a-ha is playing in the background.

A few things struck me in looking through these books. First, a cloud of dust in my face. Second, it struck me how much has remained the same over the years. The Finder is still there, along with the Desktop and the majority of menu items, keyboard shortcuts, and mouse behaviors. I was intrigued by something called the MiniFinder, which seems to be a cross between the Dock and the application switcher. (Really, it wasn’t so mini, because it took up the entire screen.)

Another thing that struck me was the status of Pascal as the primary language for the Mac at that time. I wrote Pascal programs a lot as an undergraduate. I think I last saw Pascal on one of those VH-1 Where are they now? specials.

The final thing that struck me was the vast number of years Chris Espinosa has spent at Apple. The acknowledgments of Stephen Chernicoff’s books thank Chris Espinosa as one of his managers at Apple. Chris still posts frequently as a representative of Apple on the Xcode-users mailing list. Of course, I’m just assuming that it’s the same person rather than, say, the second generation of Espinosas, and that Chris has been at Apple the whole time.

Sorry, this post doesn’t offer any useful information. If you didn’t like it, you can send me feedback, but you have to use a #2 pencil.

Build settings for Xcode projects

Sunday, October 15th, 2006

Here’s an iFAQ: Jeff, how do you choose the topics for your blog posts? Answer: I don’t. All of my topics are actually chosen by manatees. For some reason, those crazy manatees have indicated that today I should talk about build settings for Xcode projects. They’re inscrutable! The manatees, that is, not the build settings…at least not for the most part.

Some developers never dare to change the default build settings. Why? Because the Xcode build settings pane is scary — perhaps not as scary as the Oompa-Loompas, but scary nonetheless. And like the Oompa-Loompas, there is more than one pane. Besides the project build settings, each target in the project has its own build settings too. It’s enough to drive you mad! (That’s what happened to this friend of mine, so he had a lobotomy. Now he’s well again.) Still, you might want to change the build settings in your project, because the default settings were designed for backwards compatibility, not for the latest-and-greatest, state-of-the-art, up-to-the-minute, cutting-and-bleeding-edge technologies (such as Mac OS X 10.2). Even Apple recommends changing the value of ALWAYS_SEARCH_USER_PATHS, for example.

The default build settings for a project, indeed many of the elements of a project, come from a template in the folder Project Templates. When you create a new project in Xcode, it uses the template corresponding to the project type. The template for a Cocoa application project is in the Cocoa Application folder. Like other items in the /Library/ folder, you can override the default template in a particular user account, say, yours, by putting a parallel item in the user’s ~/Library/ folder. To change the template for a Cocoa application, create the folder ~/Library/Application Support/Apple/Developer Tools/Project Templates/Application/, copy the Cocoa Application/ template to that folder, and modify your copy of the template. The build settings are stored in the file CocoaApp.xcodeproj/project.pbxproj. If you’re adventurous, you could edit that file directly. On the other hand, you could just open the CocoaApp project in Xcode and change the build settings there, but afterward you’d want to delete the Xcode-generated files such as build/ in the project folder as well as the .mode1 and .pbxuser files in xcodeproj/. You might also want to delete those pesky Finder-generated .DS_Store files. (Please, please get rid of them in Leopard!) Xcode will use your custom template rather than the factory template whenever you create a new Cocoa application project.

There are many different types of project template, even for a Cocoa application — Cocoa Application, Cocoa Document-based Application, Core Data Application, etc. — so it could become tedious to change every template you need. If you only want to change the default build settings, it would be much easier to create a build settings configuration file that you can use in all of your projects.

Which default build settings should you change? As I mentioned, you should uncheck Always Search User Paths. You should check Enable Objective-C Exceptions (GCC_ENABLE_OBJC_EXCEPTIONS) to use @try, @catch, and @throw exception handling. You’ll probably also want to set the value of Mac OS X Deployment Target (MACOSX_DEPLOYMENT_TARGET) to the oldest version that your application will support. If you want to be cruel, set it to 10.5.

The most confusing collection of build settings, in my opinion, are the compiler warnings. Apple has an interesting article about this, and you can find definitions of the warnings in the GCC manual. If you hate to RTFM, you’re in luck, because I already did. I recommend using the warnings Missing Function Prototypes (GCC_WARN_ABOUT_MISSING_PROTOTYPES), Missing Newline At End Of File (GCC_WARN_ABOUT_MISSING_NEWLINE), Sign Comparison (GCC_WARN_SIGN_COMPARE), and for Other Warning Flags (WARNING_CFLAGS), -Wall. You would think that -Wall would cover all warnings, but noooooooooooo! However, the -Wall option does enable the default warnings in a Cocoa application project, Mismatched Return Type (GCC_WARN_ABOUT_RETURN_TYPE) and Unused Variables (GCC_WARN_UNUSED_VARIABLE), so those settings can be deleted when you use this flag.

If you would like to use my settings, you can copy the text below into an .xcconfig file. The values of SDKROOT and PREBINDING are preserved from the default Cocoa application template. For information on prebinding, see the documentation.

P.S. The manatees have asked me to remind you to use precompiled prefix headers!

// Jeff's project build settings
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk

Update: I’ve revised the build settings to remove some warnings. It turns out that the flags -Wall -Wextra already enable GCC_WARN_UNUSED_PARAMETER, and that warning can get annoying anyway, so I removed it and the -Wextra flag. I removed the flag -Wconversion too, because it does far more than I wanted it to. The warning GCC_WARN_SHADOW was ditched because it doesn’t like you to use the variable index. I added GCC_WARN_SIGN_COMPARE, which was enabled by -Wextra.