Cocoa and Objective-C: Up and Running (by me) is now available from O'Reilly.

Using NSWorkspace with Applications

In a continuation of the Cocoa Snippets experiment, we're going to talk about the basics of NSWorkspace in several parts. First, we're going to look at how you can use this class to work with other applications.

You can use NSWorkspace to get a list of running applications. For example:

NSWorkspace * ws = [NSWorkspace sharedWorkspace];
NSArray * apps = [ws launchedApplications];
NSLog (@"%@", apps);


The output would look something like this:

{
  NSApplicationBundleIdentifier = "com.apple.finder";
  NSApplicationName = Finder;
  NSApplicationPath = "/System/Library/CoreServices/Finder.app";
  NSApplicationProcessIdentifier = 184;
  NSApplicationProcessSerialNumberHigh = 0;
  NSApplicationProcessSerialNumberLow = 1048577;
},
{
  NSApplicationBundleIdentifier = "com.macromates.textmate";
  NSApplicationName = TextMate;
  NSApplicationPath = "/Applications/TextMate.app";
  NSApplicationProcessIdentifier = 220;
  NSApplicationProcessSerialNumberHigh = 0;
  NSApplicationProcessSerialNumberLow = 2490369;
},
{
  NSApplicationBundleIdentifier = "com.apple.iTunes";
  NSApplicationName = iTunes;
  NSApplicationPath = "/Applications/iTunes.app";
  NSApplicationProcessIdentifier = 242;
  NSApplicationProcessSerialNumberHigh = 0;
  NSApplicationProcessSerialNumberLow = 3145729;
},


The bundle identifier is set in Info.plist, and is the unique string that Launch Services needs to manage the application in the system. The process indentifier is the unix pid number. This could be used, for example, with the 'kill' command in the shell.

We can simplify and clean up this output a bit using NSArray's implementation of KVC:

NSWorkspace * ws = [NSWorkspace sharedWorkspace];
BOOL result = [ws launchApplication:@"Safari"];
NSArray * apps;
apps = [ws valueForKeyPath:@"launchedApplications.NSApplicationName"];
NSLog (@"%@", apps);


Which results in this:

(
  Finder,
  TextMate,
  iTunes
)


We can also launch an application. The most simple way to do this is with -launchApplication:

NSWorkspace * ws = [NSWorkspace sharedWorkspace];
BOOL wasLaunched = [ws launchApplication:@"Safari"];
if ( wasLaunched )
  NSLog (@"Safari was launched");
else
  NSLog (@"Safari was not launched");
NSArray * apps;
apps = [ws valueForKeyPath:@"launchedApplications.NSApplicationName"];
NSLog (@"%@", apps);


Keep in mind, though, that Safari may not be actually up and running by the time you reach the next line, even if the result is "true":

MyApp[620] Safari was launched
MyApp[620] (
  Finder,
  TextMate,
  iTunes
)


You may want to launch an application but keep your app in the foreground. This can be done with by sending a more detailed launch message:

NSWorkspace * ws = [NSWorkspace sharedWorkspace];

[ws launchAppWithBundleIdentifier: @"com.apple.Safari"
                          options: NSWorkspaceLaunchWithoutActivation
   additionalEventParamDescriptor: NULL
                 launchIdentifier: nil];


Finally, you might want to get an icon for an application. This is done in two steps:

NSWorkspace * ws    = [NSWorkspace sharedWorkspace];
NSString    * path  = [ws fullPathForApplication:@"Safari"];
NSImage     * icon  = [ws iconForFile: path];
NSLog (@"%@", icon);


This will give you something like the following:

NSImage 0x39b940 Size={32, 32} Reps=(
  NSBitmapImageRep 0x39d5e0 Size={128, 128} ColorSpace=NSDeviceRGBColorSpace BPS=8 BPP=32 Pixels=128x128 Alpha=YES Planar=NO Format=0,
  NSBitmapImageRep 0x39dd20 Size={32, 32} ColorSpace=NSDeviceRGBColorSpace BPS=8 BPP=32 Pixels=32x32 Alpha=YES Planar=NO Format=0,
  NSBitmapImageRep 0x39d740 Size={16, 16} ColorSpace=NSDeviceRGBColorSpace BPS=8 BPP=32 Pixels=16x16 Alpha=YES Planar=NO Format=0
)


... which is only moderately interesting. What you'd probably want to do to display the icon is to use NSImage's -compositeToPoint:operation: to draw into a view. Alternately, you could just use an NSImageView, and use -setImage:.

There's more ground to cover with NSWorkspace beyond just dealing with applications. We'll look at that next time.

Update

By request, here's how to open a particular URL in Safari:

NSWorkspace * ws = [NSWorkspace sharedWorkspace];
NSURL * url = [NSURL URLWithString:@"http://theocacao.com/"];
[ws openURL: url];
Design Element
Using NSWorkspace with Applications
Posted Oct 27, 2005 — 15 comments below




 

Chuck — Oct 27, 05 467

Scott,

Thank you for these Cocoa articles. I really enjoy them. Please continue.

-Chuck

David Weiss — Oct 28, 05 471

Scott, I really enjoy reading your articles on Cocoa. Please keep up the great work!

David Weiss

JB — Oct 28, 05 472

Thank you for these simple, yet insightful, snippets!

Dman — Oct 28, 05 475

Scott,

can you expand the example to show how one would launch Safari with a particular URL?

Cheers.

D.

Scott Stevenson — Oct 28, 05 476 Scotty the Leopard

Done.

Jussi — Oct 30, 05 483

Thanks for great snippets and great new design.

The last example does not actually open the URL in Safari, but in the "Default Web Browser"

George — Oct 31, 05 485

I love seeing these as well. Thanks!

Jorge — Sep 09, 06 1779

Is there a way to get the list of installed apps (apart from looking into the /Applications directory)?

Thanks!

Pir — Jun 23, 07 4437

I'm new to Cocoa. More used to C++. I need to find out the size and position of all open windows on the desktop. I thought that it should be something related to the topic above. But I can't find how to get the window information from an application... because then I could do something like the code above.

Scott Stevenson — Jun 23, 07 4438 Scotty the Leopard

I need to find out the size and position of all open windows on the desktop
I'm not sure how to do that or if it even exists as API. If it exists, it might be somewhere in the CoreGraphics reference. It's also possible that you might somehow be able to do this with the Acessibility API or even AppleScript.

This would also be a good question to ask on Apple's cocoa-dev mailing list.

Nathan Gray — Dec 04, 07 5169

Pir: Check out CGWindow.h. You'll need to search for it and it's only in Leopard. The accessibility API is also a possibility, but it's impossible to match reliably a window inside that API with one outside it.

BufferOverflow — Aug 24, 08 6308

Hi,
I wanted to know as to how i can open the Preferences of Safari from my application. Can i do it through NSWorkspace Options? Please help as i am new to Cocoa Programming.

thanks

Scott Stevenson — Aug 25, 08 6309 Scotty the Leopard

@BufferOverflow: I wanted to know as to how i can open the Preferences of Safari from my application. Can i do it through NSWorkspace Options?

I don't think there's any way to do this using Cocoa alone. You might be able to do it using AppleScript.

Rune Warhuus — Feb 11, 09 6611

Why do some webpages cause Safari to close uppon loading the page, when Safari is launched with launchApplication from a Cocoa App?

Please help me. I am a confused Norwegian n00b :-)

CD Rivera — Jul 18, 09 6834

To Rune Warhuus (on Feb 11, 2009):
I know this is an older post, but I was running into a similar problem and found an unlikely solution that worked for me. My specific issue was that Safari would close on its own immediately after the web page loaded, *unless* Safari was already opened. Here's how I solved the issue:

In Xcode, under the "Run" menu there is an option labeled "Stop on Debugger()/Debugger Str()". Try unchecking that option. It made no sense to me as [[NSWorkspace ... openUrl...] is calling the default browser (in my case, too, Safari) to do whatever it takes to access the url, independent of the app I was writing (so I thought, anyway). However, after unticking that "Stop on Debugger()..." option the problem was resolved in my case, and the "problem" web pages remained open.

Apparently, the problem is caused by web pages using Flash, which may trigger the debugging option mentioned, at least according to my source:
http://techstra.bignerdranch.com/Home
Enter page 352 and click Go, and look for the following entry:
"abraun said the following on Jul 2, 2009:"

I thought the issue described by "abraun" was unrelated, since their discussion regarded a Cocoa app that contained its own WebView. I tried the sample code for the chapter in question, and indeed confirmed the crash, and the solution. But I didn't really expected it to work for [[NSWorkspace... openUrl...] Nevertheless, I gave it a shot and it did. Give it a try.




 

Comments Temporarily Disabled

I had to temporarily disable comments due to spam. I'll re-enable them soon.




Technorati Profile
Copyright © Scott Stevenson 2004-2008