I am aware that for filenames, somewhere between the UI components and the kernel a conversion between / and : happens. Can someone explain where this happens?
It is easy enough for me to convert ':' in kernel filenames to '/' in the string shown to or accepted from the user. But maybe there is an OS function I should be using to do that, in some more portable way, or something.
More challenging is what to do when the user really wants a ':' in their filename. What is best practice? I was a bit surprised to find that in Finder, if I try to create a file called 'abc/def:ghi', I get a vague error message that tells me to "try with no punctuation marks".
The case that has caused me to look into this now is a desire for filenames that are dates or times. From the user's point of view, a filename '8/10/2026 14:17:42' is not an unreasonable thing to ask for, is it?
What other oddities are there? '.' and '..' of course... anything else?
To be clear, this whole situation is caused by macOS’s mixed heritage:
- Traditional Mac OS used colon (
:) as the path separator and thus slash (/) was allowed in file names. - BSD uses slash as the path separator and thus colon is allowed in file names.
Annoyingly, the canonical case where this crops up is dates and times, and neither of them support the most common English representations of both )-:
Early versions of Mac OS X had to do something to resolve this conflict, and the current situation is largely where we landed back then.
Can someone explain where this happens?
This is mostly at the UI layer. The most obvious example of that is, as Etresoft mentioned, the Finder, but there are others. For example, the standard save sheet accepts the name a/b, just like the Finder.
There are also some APIs that treat colons as path separators, and hence slashes as valid characters, but those are all deprecated. Mostly they are officially deprecated, but I’m sure you can rummage around and find ones that continue to escape official deprecation.
Amusingly, many of these APIs still work. Consider this code:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
FSRef homeRef;
OSStatus err = FSPathMakeRef( (const UInt8 *) "/Users/quinn", &homeRef, NULL);
if (err != noErr) { abort(); }
NSString * name = @"Hello/Cruel/World!.txt";
unichar buffer[name.length];
[name getCharacters:buffer range:NSMakeRange(0, name.length)];
err = FSCreateFileUnicode(&homeRef, name.length, buffer, kFSCatInfoNone, NULL, NULL, NULL);
if (err != noErr) { abort(); }
#pragma clang diagnostic pop
It creates this file:
% ls -lh *.txt
-rw-r--r-- 1 quinn staff 0B 9 Oct 10:47 Hello:Cruel:World!.txt
But maybe there is an OS function I should be using to do that
For a specific file name you can use the various localised (or display) name APIs. For example:
let u = URL(fileURLWithPath: "/Users/quinn/Hello:Cruel:World!.txt")
let v = try u.resourceValues(forKeys: [.localizedNameKey])
print(v.localizedName!)
// Hello/Cruel/World!.txt
If you want to display a path, the best option is NSPathControl. But if you absolutely want to display a path as a string, my advice nowadays is to display the Posix path. Traditional Mac OS paths are now mostly a thing of the path (just don’t tell AppleScript!).
What other oddities are there?
There are so many. Lemme describe just a few:
- Files with a leading dot (
.) are generally considered invisible. The.isHiddenKeyshould take care of that for you. - Be prepared for different normalisations. HFS Plus converts everything to decomposed (Normalization Form D). APFS is normalisation insensitive but preserves it. Windows-y volume formats tend to use precomposed (Normalization Form C). And Unix-y volume formats tend to follow the ‘bag of bytes’ model *shudder*
- That’s if you're working at the BSD level. Higher level APIs, most notably Foundation, can apply their own views on normalisation.
- Similarly, be prepared for different approaches to case sensitivity.
- There’s no easy way to establish a maximum file name length.
- HFS Plus has its Unicode fixed in time, and thus you’ll see some very weird Unicode behaviours with it.
These are just the problems you face when reading the file system. when creating a file things get much much worse (-: Lemme know if you want to descend in that tarpit.
The folks over on Swift Evolution have recently been wrestling with this sort of thing while working on SE-0529 Add FilePath to the Standard Library. Notably, it ignores traditional Mac OS paths |-:
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"