Vincent Gable’s Blog

August 22, 2008

Is an Application Running?

Filed under: Cocoa,MacOSX,Objective-C,Programming,Sample Code,UNIX | ,
― Vincent Gable on August 22, 2008

UPDATED 2009-01-13 to correctly detect background applications.

Pass in the bundle-identfier of an application, and this will tell you if it’s currently running or not:
- (BOOL) ThereIsARunningApplicationWithBundleID:(NSString*)bundleID; {
   ProcessSerialNumber PSN = {kNoProcess};
   while(GetNextProcess(&PSN) == noErr) {
      CFDictionaryRef info = ProcessInformationCopyDictionary(&PSN,kProcessDictionaryIncludeAllInformationMask);
      if(info) {
         NSString *theirBundleID = (NSString*)CFDictionaryGetValue(info, kIOBundleIdentifierKey);
         BOOL bundleIDMatches = [bundleID isEqualTo:(NSString*)theirBundleID];
         CFRelease(info);
         if(bundleIDMatches)
            return YES;
      }
   }
   return NO;
}

(It’s a good idea to test if an application is running before sending it an AppleEvent, because that will launch it.)

Interestingly, none of the the Process Manager APIs can see into another user’s process-space; but ps can; at least on OS X 10.5 and 10.4.

August 20, 2008

Localizing In Xcode

Let me say this again in slow motion: NEVER type in ANY English string without typing NSLocalizedString() around it! This will save you SO MUCH HASSLE later on when your app is popular. Remember that enterprising polyglots can localize your code from just the binary you ship if you follow a few rules of localization, so you may wake up one day and find that someone from across the world has mailed you a your app in another language. It’s a fuzzy feeling and it gets you instant market-share.

Wil Shipley

August 17, 2008

launchctl Gotcha

Filed under: Bug Bite,MacOSX,Programming,UNIX | , ,
― Vincent Gable on August 17, 2008

On Mac OS X 10.5, the launchd you talk to as non-root user is not the same launchd you talk to when root. So
$ launchctl list
$ sudo launchctl list
print out different lists, and the unprivileged `launchctl list` contains processes that the root-`launchctl list` does not know about. So running a launctl command as root may cause it to fail.

I have never seen another UNIX-ish tool that would fail when run as root, but succeed otherwise.

August 14, 2008

Only Skin Deep…

Filed under: Design,MacOSX | , ,
― Vincent Gable on August 14, 2008

Pictures showing that a nerds desktop can look like other operating-systems … at least from a few feet away.

It’s a good reminder that design is not just skin deep.

August 13, 2008

Quickly Switching Between Headers and Source Files in Xcode

Filed under: MacOSX,Programming,Tips |
― Vincent Gable on August 13, 2008

Command + Option + Up-arrow will switch between a header and source-code file in Xcode. I wish I had figured this out years ago.

August 5, 2008

Simplified Logging

Filed under: Cocoa,MacOSX,Objective-C,Programming,Sample Code,Tips,Usability | , ,
― Vincent Gable on August 5, 2008

I have noticed a pattern in my Cocoa code, which I have been able to simplify. I often print out the value of a variable for debugging. 99 times out of 100, the code looks like this: NSLog(@"actionURL = %@", actionURL);, where actionURL is some variable.

But using the macros, I can say LOG_ID(actionURL);. This is shorter, and non-repetitive.

The macros I use to simplify debugging (2008-09-16):

#define FourCharCode2NSString(err) NSFileTypeForHFSTypeCode(err)

#define LOG_4CC(x) NSLog(@"%s = %@", # x, FourCharCode2NSString(x))
#define LOG_FUNCTION() NSLog(@"%s", __FUNCTION__)
#define LOG_ID(o) NSLog(@"%s = %@", # o, o)
#define LOG_INT(i) NSLog(@"%s = %d", # i, i)
#define LOG_INT64(ll) NSLog(@"%s = %lld", # ll, ll)
#define LOG_FLOAT(f) NSLog(@"%s = %f", # f, f)
#define LOG_LONG_FLOAT(f) NSLog(@"%s = %Lf", # f, f)
#define LOG_OBJECT(o) LOG_ID(o)
#define LOG_POINT(p) NSLog(@"%s = %@", # p, NSStringFromPoint(p))
#define LOG_RECT(r) NSLog(@"%s = %@", # r, NSStringFromRect(r))
#define LOG_SIZE(s) NSLog(@"%s = %@", # s, NSStringFromSize(s))

Look in assert.h for insight on how to roll your own debugging macros.

Neat iSight Trick

Filed under: MacOSX,Tips |
― Vincent Gable on August 5, 2008

Your iSight camera can see into the IR spectrum. You can see the invisible IR light on your remote control if you watch it thought your iSight.

August 3, 2008

How to Implement Automatic Updating

Filed under: Cocoa,Design,MacOSX,Objective-C,Programming,Sample Code,Usability
― Vincent Gable on August 3, 2008

If you are about to add automatic updating to your application (and you should), then check out the Sparkle Framework before you start. I wish I had done this before I spent a lot of time writing my own automatic updating code.

A quick-and-dirty hack.

Get your application’s version number by:
[[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleVersion"];
(This articles explains setting up Xcode to bake the current subversion-number into every build.)

Get the the latest version number out of a text-file on a website:
[NSString stringWithContentsOfURL:[NSURL URLWithString:@"www.your-url-here.com/whaterver/version.txt" encoding:NSASCIIStringEncoding error:nil]];
If there is any trouble, it will return nil. CAUTION: This will block the main thread for up to 30 seconds, while -stringWithContentsOfURL: tries to access the URL.

Making it Usable

If you don’t want your program to be unresponsive while it tries to get something off the internet, try this:

- (void) updatesReady
{
   //do something....
}

- (void) synchronousCheck
{
   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

   NSURL* latestURL = [NSURL URLWithString:@"http://www.your-url-here.com/currentversion.txt"];
   NSString* latestVersion = [NSString stringWithContentsOfURL:latestURL encoding:NSASCIIStringEncoding error:nil];
   //latestVersion will be nil if there trouble was accessing latestURL.

   NSString* ourVersion = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleVersion"];
   if([ourVersion isLessThan: latestVersion])
      [self performSelectorOnMainThread:@selector(updatesReady) withObject:nil waitUntilDone:YES];

   [pool drain];
}

//will call updatesReady iff a new version is ready, otherwise does nothing.
- (void) checkForUpdates
{
   [NSThread detachNewThreadSelector:@selector(synchronousCheck) toTarget:self withObject:nil];
}

A few thoughts on automatic updating:

When the user explicitly runs an update-check, they should get immediate feedback that something happened. You should put up a window with a spinner. Even though checking for updates may only take 100msec in your lab, if the user is on a flakey wireless connection while traveling, it could take several seconds for them.

It feels like it takes several years for an HTTP fetch to time out, if you can’t do anything while you wait.

Anything having to do with threads is harder then it looks.

“Check for updates {daily, weekly, monthly}” is a useless preference. Just because Apple’s Software Update has it does not mean you should offer those choices! They do not give the user any real control over when and how they are annoyed by requests to update. Users don’t care when the update checks are performed — they only care when new software is announced to them. Ether go with the a simple check-box that toggles automatic updating, or go with something like:
“Let me know about new software: at 12 PM”.

Display the date of the last successful update check, never display “Last Update Monday 9AM — update failed”. (This should be obvious, but I see it all the time). Version checking should be silent and unobtrusive. Don’t tell the user about it — only tell them about results.

Automatically downloading updates in the background is a great idea if they are small, and the computer has a reasonably fast internet connection.

You might also want to check out: NiftyFeatures 0.4 By Uli Kusterer .

An example project containing two classes, UKUpdateChecker and UKFeedbackProvider that can be plugged into your applications to add “Check for Update…” and “Send Feedback…” menu items to your applications.

July 23, 2008

Wow, Macs Really Are Easier

Filed under: Accessibility,Design,MacOSX,Usability | ,
― Vincent Gable on July 23, 2008

Atul Varma learns to love his Mac

…In a nutshell, though, I had always assumed that Macs were only marginally easier to use than PCs. I guess I’ve found over the past two months that in some ways, this holds true—the Mac is essentially an incredibly sexy-looking PC, with the same annoyances and a few polishes that make it a bit more humane to use. In other ways, however, the difference is truly like night and day.

This is a story about such a situation.

… I had to go through 8 wizards in all, so that’s a grand total of twenty-four clicks required to unplug my keyboard and mouse from one side of my computer and plug them into the other side. I’m not actually installing brand-new hardware here.

The first time I had to plug this keyboard and mouse into my Mac, I was floored. In the best-case scenario, I expected it to think for a second or two and then give me a reasonably unintrusive message informing me that I could use my USB mouse and keyboard. That would have been pretty humane.

But it did one better.

The Mac didn’t tell me anything, because my mouse and keyboard just worked the moment I plugged them in. When you plug in a power cable or a pair of headphones into a computer, you don’t get some kind of confirmation message from your operating system, because it’s obviously supposed to just work—why should plugging in a USB keyboard and mouse be any different?

… I have to admit that when it all adds up, I find my Mac to be significantly easier to use than my PC.

July 14, 2008

Crash Reporter Prefs

Filed under: MacOSX,Reverse Engineering,Tips |
― Vincent Gable on July 14, 2008

/Developer/Applications/Utilities/CrashReporterPrefs.app can make crash reports much more useful. It will show crash-reports for background processes, and stack-trace in the notification dialog.

Crash report-files are stored in ~/Library/Logs/CrashReporter/

Unfortunately, crash reports are not sent back to third-party developers, even when you send them to Apple.

EDITED TO ADD: This still works in Snow Leopard (Mac OS X 10.5.6)

« Newer PostsOlder Posts »

Powered by WordPress