Happy 30th Birthday, Macintosh!

Thirty years ago today, on January 24, 1984, Apple introduced the Macintosh. I bought my first Macintosh about a week after it was introduced, and since then I’ve owned many other Macs. Here’s a more or less complete list of the Macs I’ve owned:

  • Mac 128
  • Mac 512
  • Mac SE/30
  • Mac IIsi
  • PowerMac 6100
  • PowerBook 100
  • Duo 270c
  • StarMax clone
  • Blue & White G3
  • Original iBook
  • White iBook
  • 12” Aluminum PowerBook
  • “Yikes” G4 tower
  • DTK (first Intel Mac for developers)
  • Several 15″ MacBook pros
  • 13” MacBook
  • 27” iMac
  • Mac Mini
  • 2012 MacBook Air
  • 13” Retina MacBook Pro
Although I was programming before the Mac, I’m not sure what I would be doing now or where I would be if the Mac didn’t exist.

Detective 1.1 is now available, 50% off through WWDC

Detective 1.1 is now available. This was a major rewrite to support Twitter’s 1.1 API and handle changes to Twitter’s authorization. Rather than asking you to log in to Twitter with a login window, it will now ask your permission to use your Mac’s Twitter accounts.

To celebrate the release, it’s now 50% off (0.99) through the end of WWDC on June 15.

For more information, visit the Detective page or download it here.

Detective update

Twitter recently changed some of their rules for app authorization using OAuth, so as a result, when a new user tries to start Detective, they will get an error when authorizing Twitter. However, if you’re already using Detective it will continue to work as long as you don’t log out of Twitter.

Detective uses MGTwitterEngine, which no longer seems to be actively maintained and which still uses Twitter’s deprecated 1.0 API. As a result, at some point it would stop working when Twitter shuts off that API. I’ve looked into a few options and I’ve determined that the best way to move forward is to use Mac OS X’s built-in social framework. Rather than asking you to log in to Twitter, it will now use your Mac’s twitter accounts. However this means we will no longer be able to support any Mac OS X version earlier than 10.8. I hope to have an update available soon.

Detective Update

I’ve been working on an update to Detective which fixes some drawing problems in Mountain Lion and fixes a problem where it sometimes stops updating when the Mac wakes up from sleep.

Unfortunately it was rejected by Apple because of a sandboxing issue involving Growl. I haven’t been able to successfully set the entitlements for the auxiliary executable required by Growl, so I’m considering eliminating Growl, using Notification Center, and requiring Mountain Lion.

Sandboxing revisited

When I submitted an update to Detective, I discovered a few tricky things related to sandboxing and embedded helper apps.

In order to support ‘start at login’ in a sandboxed app, you need to embed a helper app that launches the main app (the entire process is described here). What I didn’t realize is that the helper app also has to be signed, or it will fail to let you start it at login. However, when you sign the helper app, it will include its own embedded provisioning profile, so when you try to submit your app, it will be rejected with the following message:

Invalid Provisioning Profile Location – The provisioning profile for your Mac OS X app must be located in the Contents directory of the main app bundle. A provisioning profile is optional, but you cannot submit more than one.

One of the suggestions in Apple’s developer forum is to remove the embedded profile from the helper app. Note that deleting the embedded profile doesn’t affect the actual code signing. After some experimentation, I found that the easiest way to do it is to add a Run Script build phase to the main application that deletes the profile from the helper app:

rm ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Contents/Library/LoginItems/DetectiveLoginHelper.app/Contents/embedded.provisionprofile

After doing this, I was able to submit the app successfully.

Login items in the sandbox

Apple will soon require all Mac apps submitted to the app store to be sandboxed for heightened security, which means it needs to request permission for doing even basic things like accessing files or connecting to the internet. A lot of things like disk burning aren’t allowed at all in the sandbox, and some things like sending AppleEvents require temporary exemptions which will be phased out.

One common task that’s complicated by the sandbox is adding a login item. A good tutorial on creating login items is available at delite studio, although some changes to the code are needed. That code follows Apple’s earlier guideline of using LSRegisterURL to register your helper app. However, I found that it always fails with error -10819. According to an Apple engineer in Apple’s developer forum, you should not call LSRegisterURL in a sandboxed app.

Note that your application can’t add itself as a login item. You need to write a simple helper app that launches your main app and it must be included in the main application bundle in the relative path /Contents/Library/LoginItems/.

The method for adding & removing a login item turned out to be very simple:

- (void)addLoginItem {
    NSString *ref = @"com.madebynotion.myLoginHelper";
	if (!SMLoginItemSetEnabled((CFStringRef)ref, true)) {
		NSLog(@"SMLoginItemSetEnabled failed.");

- (void)removeLoginItem {
    NSString *ref = @"com.madebynotion.myLoginHelper";
	if (!SMLoginItemSetEnabled((CFStringRef)ref, false)) {
		NSLog(@"SMLoginItemSetEnabled failed.");

If you need to find out whether your login item is enabled, here’s a way to do it:

    NSString *bundleID = @"com.madebynotion.myLoginHelper";
    NSArray * jobDicts = nil;
    jobDicts = (NSArray *)SMCopyAllJobDictionaries( kSMDomainUserLaunchd );
    // Note: Sandbox issue when using SMJobCopyDictionary()

    if ( (jobDicts != nil) && [jobDicts count] > 0 ) {

        BOOL bOnDemand = NO;

        for ( NSDictionary * job in jobDicts ) {

            if ( [bundleID isEqualToString:[job objectForKey:@"Label"]] ) {
                bOnDemand = [[job objectForKey:@"OnDemand"] boolValue];

        CFRelease((CFDictionaryRef)jobDicts); jobDicts = nil;
        return bOnDemand;

    return NO;

Although this method works and seems to be the preferred way to do it in the sandbox, it’s less optimal than the old non-sandbox method, since your login item won’t appear in the users & groups preference panel’s login items list and can only be turned on & off from your own application.

How far we’ve come

To see how far we’ve come since 1984, just compare the specs of the first Macintosh with today’s iPhone 4GS:

1984 Macintosh 128.png

We now carry in our pocket a device with a faster processor, more storage, and a higher resolution screen in a much smaller package than the computer we had sitting on our desk 27 years ago. Today’s Macintosh is many times faster, with a very different CPU, and has many times more storage, yet it’s still recognizable as a Macintosh and still has the same elegant simplicity.

R.I.P. Steve Jobs

Apple reports that Steve Jobs has died:

Apple has lost a visionary and creative genius, and the world has lost an amazing human being. Those of us who have been fortunate enough to know and work with Steve have lost a dear friend and an inspiring mentor. Steve leaves behind a company that only he could have built, and his spirit will forever be the foundation of Apple.

Apple’s home page is now a tribute to Steve Jobs.

Screen Shot 2011-10-05 at 8.06.19 PM.png

Steve Jobs truly changed the world. His influence has impacted the music & entertainment industries as much as in impacted the computer industry. The devices he created changed the way we listen to music, watch movies, and interact with our phones.

Sync OmniFocus with your own server

OmniFocus provides several ways to sync your data between devices, including MobileMe and their beta Omni Sync Server. If you have a web hosting account (such as DreamHost) that supports WebDAV, you can set up your own sync server without having to subscribe to any other services. Here’s how I set it up on DreamHost.

1. On your web host, set up a WebDAV directory. On DreamHost, go to Goodies -> Htaccess/WebDAV and choose a fully hosted domain. In the next screen, enter the name of a directory you want to use for WebDAV and password protect it.

Screen Shot 2011-09-08 at 8.52.37 PM.png

2. In OmniFocus on your Mac, go to Sync settings and switch to the advanced tab. Enter the name of a subfolder inside the directory you specified in step 1.

Screen Shot 2011-09-08 at 8.49.34 PM.png

3. Enter that same path on all of your mobile devices that you want to sync with it.

I find it to be much faster than MobileMe using my DreamHost server. Your results may vary depending on your web host or server.