Using NSWorkspace with Files
As a follow-up to the previous article on NSWorkspace and applications, we're going to take a look at how to use NSWorkspace to deal with files. You can think of NSWorkspace's file management as a Cocoa-front end to the type of functions offered by the Finder (although NSWorkspace predates the Finder).First, let's take a look at the basic task of opening a file:
NSString * path = @"/Developer/About Xcode Tools.pdf";
NSURL * fileURL = [NSURL fileURLWithPath: path];
NSWorkspace * ws = [NSWorkspace sharedWorkspace];
[ws openURL: fileURL];
This should open the Xcode Tools PDF in Preview. Now what if you want to open the PDF in, say, Safari? Easy enough:
NSString * path = @"/Developer/About Xcode Tools.pdf";
NSURL * fileURL = [NSURL fileURLWithPath: path];
NSWorkspace * ws = [NSWorkspace sharedWorkspace];
[ws openFile:[fileURL path] withApplication:@"Safari"];
It might seem strange to take a path, convert it to a NSURL, then convert it back to a path. The problem is that there's no method to open a URL with a specific app in Tiger, so we have to ask the NSURL for its path.
Why bother with this approach at all? Why not just use normal file paths? Slowly but surely, a number of different parts of Cocoa are demphasizing static path strings and recommending the use of NSURLs. The idea here is to get into the habit early and often.
If you want to select a file in the Finder, you can do something like the following example. This is useful for implementing a "Reveal in Finder" contextual menu item.
NSString * path = @"/Developer/About Xcode Tools.pdf";
NSURL * fileURL = [NSURL fileURLWithPath: path];
NSWorkspace * ws = [NSWorkspace sharedWorkspace];
[ws selectFile:[fileURL path] inFileViewerRootedAtPath:nil];
We can also gather some information about a file:
NSString * path = @"/Developer/About Xcode Tools.pdf";
NSURL * fileURL = [NSURL fileURLWithPath: path];
NSWorkspace * ws = [NSWorkspace sharedWorkspace];
NSString * appName;
NSString * fileType;
[ws getInfoForFile: [fileURL path]
application: &appName
type: &fileType];
NSLog (@"appName: %@", appName);
NSLog (@"type: %@", fileType);
Note how we pass the appName and fileType params in with the ampersand (&) in front. C calls this the addressof operator. Without getting into the nitty gritty, this means that the method can write directly to variables we've defined. You know you need to use an addressof operator when you see a (**) parameter like this:
- (BOOL)getInfoForFile:(NSString *)fullPath
application:(NSString **)appName
type:(NSString **)type;
Cocoa does this in some places where a method needs to return more than one thing. In any case, the above example gives us something like this:
appName: /Applications/Preview.app
type: pdf
Now this is an interesting case because the docs for this method say the following about the "type" parameter:
The NSString pointed to by type contains one of the values described in "Constants".
In theory, that means that type should be one of the following:
NSPlainFileType
NSDirectoryFileType
NSApplicationFileType
NSFilesystemFileType
NSShellCommandFileType
In practice, though, the type param seems to actually contain the file type, such as "pdf" or "rtf". I've filed a documentation bug. :)
Moving on, we can also get some information about the things we're allowed to do to a file:
NSString * path = @"/Developer/About Xcode Tools.pdf";
NSURL * fileURL = [NSURL fileURLWithPath: path];
NSWorkspace * ws = [NSWorkspace sharedWorkspace];
BOOL removable;
BOOL writeable;
BOOL unmountable;
[ws getFileSystemInfoForPath:[fileURL path]
isRemovable: &removable
isWritable: &writeable
isUnmountable: &unmountable
description: NULL
type: NULL];
NSLog (@"removable: %i", removable);
NSLog (@"writeable: %i", writeable);
NSLog (@"unmountable: %i", unmountable);
Which gives us output like this:
removable: 0
writeable: 1
unmountable: 0
The "unmountable" param essentially tells us if the file is on a removable device or not. If these two methods don't quite quench your thirst, the NSFileManager class can provide more details about a file.
Now, onto file operations. If you want to copy a file, you can do it as such:
NSString * name = @"About Xcode Tools.pdf";
NSArray * files = [NSArray arrayWithObject: name];
NSWorkspace * ws = [NSWorkspace sharedWorkspace];
[ws performFileOperation: NSWorkspaceCopyOperation
source: @"/Developer/"
destination: @"/Users/scott/Desktop/"
files: files
tag: 0];
Obviously, you can add more file (or directory) names to the files array, but they must all reside at the root level of the source directory.
You can also move files to the Trash:
NSString * name = @"About Xcode Tools.pdf";
NSArray * files = [NSArray arrayWithObject: name];
NSWorkspace * ws = [NSWorkspace sharedWorkspace];
[ws performFileOperation: NSWorkspaceRecycleOperation
source: @"/Users/scott/Desktop/"
destination: @""
files: files
tag: 0];
In the above example, we use an empty string for the destination because it's unnecessary for this operation. You might also want to take a look at NSWorkspaceDestroyOperation and NSWorkspaceDuplicateOperation. (side note: NSFileManager also has some of its own file manipulation methods).
Although this was touched on briefly in the last installment, it's worth mentioning that -iconForFile: works for any kind of file, not just applications.
NSString * path = @"/Developer/About Xcode Tools.pdf";
NSURL * fileURL = [NSURL fileURLWithPath: path];
NSWorkspace * ws = [NSWorkspace sharedWorkspace];
NSImage * icon = [ws iconForFile: [fileURL path]];
NSLog (@"icon: %@", icon);
You can also use -iconForFileType: to get a icon for a particular kind of file, such as @"xcodeproj".
Using NSWorkspace with Files
Posted Nov 11, 2005 — 12 comments below
Posted Nov 11, 2005 — 12 comments below
Olivier — Nov 11, 05 533
Scott Stevenson — Nov 11, 05 535
Jussi — Nov 13, 05 540
Dominik Wagner — Mar 12, 06 928
Do you know of any way to do reveal in finder that doesn't cause all finder windows to come in front of my app?
cheers,
dom
Scott Stevenson — Mar 14, 06 930
As far as I know, that's a behavior of the Finder more than NSWorkspace. Once it's activated, everything comes to the front. If you find a solution, let me know.
Philip Orr — Feb 02, 07 3483
One quick question though. Is it possible to use the NSWorkspace to monitor if a folder has been changed, i.e. a new file added, or removed.
Thanks
Philip
Scott Stevenson — Feb 02, 07 3484
I don't think so. There's a lower-level API for that called kqueue. Uli Kusterer has a Objective-C wrapper for it called UKKQueue. It's on this page, about halfway down.
Philip Orr — Feb 02, 07 3485
Philip
Lars Sonchocky-Helldorf — May 29, 08 5955
(code snipped by Scott because it's far too long for a comment)
sadly Apple closed my bug report long time ago
regards,
Lars
Scott Stevenson — May 30, 08 5959
Hi, Lars. I edited the comment because it was far too verbose for this context. If you want to link the source file, that's fine.
Thanks.
victor jalencas — Apr 17, 09 6689
Great info. Do you know if there is any Cocoa call (I only found how to do it in plain C) to change the label of a file (i.e., the colour of the file in Finder)?
cheers, victor
Andre — Jun 30, 09 6821