About | ACP | Buy | Industry Watch | Learning Curve | News | Products | Search | Substack
Home » Learning Curve

A Sanity Check for Apple

There are two ways to manage files: there's the intelligent way and then there's Apple's way.

Get It

Try It

Yes, it's true: file management predates Apple. Apple were formalised on April Fools 1976; computers are known to have existed before then.

Once such system claiming to deal in file management is Unix. MS-DOS and later Microsoft Windows borrow a lot from Unix.

Unix doesn't have any primitives for copying and moving files. Files are only streams of bytes; they can be opened either with open() or fopen() and copied (or moved as the case may be) byte for byte to their new location. When copying entire hierarchies, care must be taken to preserve relationships.

Moving files - at least on the same 'volume' - is merely a matter of checking for and removing destination conflicts and then changing directory entries. The files are not actually moved at all - only their references are.

Unix does not have a GUI based API for file management. Unix itself doesn't have a GUI.

Windows does have a GUI and Windows of today has a sophisticated file management module: shell32.dll. And within shell32.dll the function SHFileOperation takes care of file management.

SHFileOperation takes a single argument: a pointer to a SHFILEOPSTRUCT.

int SHFileOperation(

SHFileOperation returns zero if successful, a nonzero value otherwise. SHFILEOPSTRUCT looks like this.

typedef struct _SHFILEOPSTRUCT {
    HWND hwnd;
    UINT wFunc;
    LPCTSTR pFrom;
    LPCTSTR pTo;
    FILEOP_FLAGS fFlags;
    BOOL fAnyOperationsAborted;
    LPVOID hNameMappings;
    LPCTSTR lpszProgressTitle;

It's the wFunc field which is immediately interesting. SHFileOperation namely handles four generic file operations.

  • FO_COPY copies the files specified in the pFrom member to the location specified in the pTo member.
  • FO_DELETE deletes the files specified in pFrom.
  • FO_MOVE moves the files specified in pFrom to the location specified in pTo.
  • FO_RENAME renames the file specified in pFrom. It cannot be used to rename multiple files with a single call. FO_MOVE should be used instead.

The key is when you've filled in all the data - such as the parent window the operations will serve, the function field, the from and to fields, and flags that further control the operation - the system itself takes off on its own. The system itself handles all possible scenarios.

Such as prompting for overwrites, warning that registered applications may be impacted, and so forth. Such as continuing to prompt the user in dicey situations or not prompting the user anymore if that be the user's wishes. And the file management itself is also handled by SHFileOperation - seamlessly and invisibly as it were.

And file operations are complementary: moving a directory to a new location where a directory with the same name already exists does not first remove the destination and then move in the source; each and every member of the destination that exists at the source is updated, and those that don't are moved in. The source 'complements' the destination - it does not 'replace' it.

Consider the following scenario: there are two directories dir1 and dir2. dir2 has a directory called dir1 of its own. That dir1 contains a file called foo whilst the other dir1 contains a file called bar.

If you move dir1 into dir2 with the 'complementary' method, the destination dir1 will now contain both foo and bar; if you use the 'replacement' method, the destination will only contain bar.

This is because with the 'replacement' method the destination dir1 is removed before proceeding; with the 'complementary' method - the standard method in the industry - nothing is 'removed' at all. The source and destination 'complement' each other.


OS X (Cocoa) file management at the lowest level is handled by the class NSFileManager. The most germane methods in this context are copyPath:toPath:handler:, movePath:toPath:handler:, and removeFileAtPath:handler:. What's important is to see how they work.

'If source is a file, the method creates a file at destination that holds the exact contents of the original file. If source is a directory, the method creates a directory at destination and recursively populates it with duplicates of the files and directories contained in source. The file specified in source must exist, while destination must not exist prior to the operation.'

Note 'must not exist': these are 'replacement' rather than 'complementary' methods.

But conflicts are inevitable - after all, people overwrite files and hives all the time. What happens then?

As the system does not allow 'complementary' file operations there's only one alternative: destroy the destination before copying or moving in the source.

And this is where Apple's 'replacement' method gets into trouble.


Apple's counterpart to SHFileOperation is the GUI based NSWorkspace. It doesn't really have a GUI - not for the most part - and it doesn't handle file operations any differently either. NSWorkspace does all its file operations with one instance method - performFileOperation:source:destination:files:tag:.

Superficially it looks like a lot cleaner design than SHFileOperation but it still uses the 'replacement' rather than the 'complementary' method.

Fatal Flaw?

It's clear that 'replacing' rather than 'complementing' hives looks easier at the outset but it's ripe with dangers. Obstacles such as those interposed in ordinary file operations are there for a purpose: to protect the user. Of course it's possible to first delete a directory and then copy in a file with the same name. Or vice versa. That this is not possible at higher levels of abstraction is for a very good reason: protection. The Unix command line primitives are trying to stop you from shooting yourself in the foot.

The Apple 'replacement' method has no such protection. Whether it be with iWeb, or TextEdit, or an ordinary 'Save As' dialog, or Finder file operations, you're always half a step away from catastrophe.

See Also
Industry Watch: Sanity Checks at Apple

About | ACP | Buy | Industry Watch | Learning Curve | News | Products | Search | Substack
Copyright © Rixstep. All rights reserved.