Vincent Gable’s Blog

June 2, 2010

NSHomeDirectory() is a Bad Thing

Filed under: Announcement,Cocoa,iPhone,MacOSX,Objective-C,Programming | , ,
― Vincent Gable on June 2, 2010

Code that uses NSHomeDirectory() is probably doing The Wrong Thing. It’s not appropriate to clutter up the user’s home directory — internal application-data should be stored in the Application Support directory (or a temporary file if it’s transient). So I can’t think of a good reason to get the path to the user’s home directory. Every use of NSHomeDirectory() I’ve seen is spamming the home directory, or getting a subdirectory in a brittle way.

For sample code that gets a directory robustly, using NSSearchPathForDirectoriesInDomains(), see Finding or creating the application support directory.

Because NSHomeDirectory() encourages so many bad practices, it should be deprecated.

Disabling NSHomeDirectory() in Your Projects

Add the following macro to your prefix file:

#define NSHomeDirectory() NSHomeDirectory_IS_DISCOURAGED_USE_NSSearchPathForDirectoriesInDomains_TO_GET_A_SUBDIRECTORY_OF_HOME

Then any use of NSHomeDirectory() will give the compiler error:

error:
‘NSHomeDirectory_IS_DISCOURAGED_USE_NSSearchPathForDirectoriesInDomains_TO_GET_A_SUBDIRECTORY_OF_HOME’ undeclared (first use in this function)

Tell Me I’m Wrong

If you’ve seen a legitimate use of NSHomeDirectory() please leave a comment! Just because I can’t think of one doesn’t mean they don’t exist.

September 5, 2008

ASCII is Dangerous

Never use NSASCIIStringEncoding

“Foreign” characters, like the ï in “naïve”, will break your code, if you use NSASCIIStringEncoding. Such characters are more common then you might expect, even if you do not have an internationalized application. “Smart quotes”, and most well-rendered punctuation marks, are not 7-bit ASCII. For example, that last sentence can’t be encoded into ASCII, because my blog uses smart-quotes. (Seriously, [thatSentence cStringUsingEncoding:NSASCIIStringEncoding] will return nil!)

Here are some simple alternatives:

C-String Paths
Use - (const char *)fileSystemRepresentation; to get a C-string that you can pass to POSIX functions. The C-string will be freed when the NSString it came from is freed.

An Alternate Encoding
NSUTF8StringEncoding is the closest safe alternative to NSASCIIStringEncoding. ASCII characters have the same representation in UTF-8 as in ASCII. UTF-8 strings will printf correctly, but will look wrong (‘fancy’ characters will be garbage) if you use NSLog(%s).

Native Foundation (NSLog) Encoding
Generally, Foundation uses UTF-16. It is my understanding that this is what NSStrings are by default under the hood. UTF-16 strings will look right if you print them with NSLog(%s), but will not print correctly using printf. In my experience printf truncates UTF-16 strings in an unpredictable way. Do not mix UTF-16 and printf.

Convenience C-Ctrings
[someNSString UTF8String] will give you a const char * to a NULL-terminated UTF8-string. ASCII characters have the same representation in UTF-8 as in ASCII.

Take a minute to search all your projects for NSASCIIStringEncoding, and replace it with a more robust option.

It never hurts to brush up on unicode.

July 3, 2008

NSApplicationName Inconsistencies

Filed under: Bug Bite,Cocoa,MacOSX,Objective-C,Programming,Sample Code | , , ,
― Vincent Gable on July 3, 2008

The value stored under the NSApplicationName key of the result of [[NSWorkspace sharedWorkspace] activeApplication] is not the always the name the user knows the application by. Worse, it’s not always the same as the name for the application that other APIs expect or return. Even fullPathForApplication: in NSWorkspace sometimes won’t recognize it!

The problem stems from the fact that there are at least five application names floating around, at least in concept: (1) the file name the Finder sees, which in the case of an application package is the package (bundle) name; (2) the name of the executable inside the package, (3) the long name used in many places for display purposes only; (4) the short name used as the application menu title and in a few other places where a long name won’t fit for display purposes; and (5) the process name of a running application. They aren’t always the same, especially in Microsoft and Adobe products.

–From an informative message by Bill Cheeseman.

So instead of relying on NSApplicationName I now use -[[NSFileManager defaultManager] displayNameAtPath:] then strip off the filename extension. This should give exactly the filename the user sees. Every time.


NSDictionary *appInfo = [[NSWorkspace sharedWorkspace] activeApplication];
NSString *appPath = [appInfo objectForKey:@"NSApplicationPath"];
NSString *name = [[[NSFileManager defaultManager] displayNameAtPath:appPath] stringByDeletingPathExtension];

And of course, you really should be using bundle identifiers, instead of names, to identify an application. Unfortunately, a very few applications are not bundles. (For example, Microsoft stuff prior to Office 2008), so it might be necessary to fall back on using a name to locate them in a path-independent way.

Creating a custom CFBundleName in an application’s info.plist file seems to confuse NSApplicationName. For this reason I don’t think setting it is a good idea.

UPDATE 2010-01-20: See also, Technical Q&A QA1544: Obtaining the localized application name in Cocoa

Powered by WordPress