Tom Coomer
Tom Coomer

Reputation: 6527

Open another Mac app

In my app I would like to open another app that is installed on the User's Mac (such as iPhoto). I am not sure what I should be looking for in the documentation. What is this called and how should I do it? Thank you

Upvotes: 20

Views: 19801

Answers (6)

Leo Dabus
Leo Dabus

Reputation: 236305

Swift 5 or later

import Cocoa

func openPhotos() -> Bool {
    if let photosApp = FileManager.default.urls(
        for: .applicationDirectory,
        in: .systemDomainMask
    ).first?.appendingPathComponent("Photos.app") {
        return NSWorkspace.shared.open(photosApp)
    }
    return false
}

Usage:

if openPhotos() {
    print(true)
}

Or using launchApplication with the app name parameter in the method:

import Cocoa

func openApp(_ named: String) -> Bool {
    NSWorkspace.shared.launchApplication(named)
}

Usage:

if openApp("Photos") {
    print(true)
}

Upvotes: 26

mabi99
mabi99

Reputation: 199

I second the answer by vookimedlo - unfortunately, I cannot yet comment (silly reputation limit) so I post this as an extra answer.

This is just one caveat, which might not affect too many: while launchApplication() accepted a path to an executable (e.g. "MyApp.app/Contents/MacOS/MyApp"), this will result in an error (lacking privileges) with openApplication(::). You have to supply the path to the app bundle ("MyApp.app") instead.

Of particular interest when you try to make a helper ("launcher") to add as a login item. See the following and keep my comment in mind:

https://theswiftdev.com/2017/10/27/how-to-launch-a-macos-app-at-login/ (GREAT article by Tibor Bödecs)

BTW, as for vookimedlo's code: in my experience, you don't need to specify the OpenContext.arguments with [path], you can simply pass a default NSWorkspace.OpenContext()...

Upvotes: 3

vookimedlo
vookimedlo

Reputation: 1378

XCode 11 • MacOS Catalina 10.15 • Swift 5

NSWorkspace.shared.launchApplication is deprecated and starting from the MacOS 10.15 the new function NSWorkspace.shared.openApplication shall be used.


Example - open terminal application by its bundle id

guard let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.Terminal") else { return }

let path = "/bin"
let configuration = NSWorkspace.OpenConfiguration()
configuration.arguments = [path]
NSWorkspace.shared.openApplication(at: url,
                                   configuration: configuration,
                                   completionHandler: nil)

Example - open terminal application by its path

let url = NSURL(fileURLWithPath: "/System/Applications/Utilities/Terminal.app", isDirectory: true) as URL

let path = "/bin"
let configuration = NSWorkspace.OpenConfiguration()
configuration.arguments = [path]
NSWorkspace.shared.openApplication(at: url,
                                   configuration: configuration,
                                   completionHandler: nil)

Upvotes: 32

Mike Crawford
Mike Crawford

Reputation: 2278

There are different ways to do that. The most efficient is to use fvork and execve - see man vfork and man execve.

Less efficient but more flexible is to use the system library call. What that actually does is runs a shell - like bash - then passes the string you provide, to bash. So you can set up pipelines, redirection and such.

Or you can send an Apple Event to the Finder: "Tell Finder Open iPhoto".

In the first two cases you want to launch the executable inside the bundle, that is, /Applications/iPhoto.app/Contents/MacOS/iPhoto.

Try the above from the command line, in the Terminal:

$ /Applications/iPhoto.app/Contents/MacOS/iPhoto

You'll see the iPhoto App launch.

Upvotes: 0

qwerty_so
qwerty_so

Reputation: 36295

let task = NSTask.launchedTaskWithLaunchPath(<#path: String#>, arguments: <#[AnyObject]#>) will probably do what you want

Upvotes: 3

Related Questions