Search code examples
macosswiftuiappkit

`NSTask.run()` fails with `The operation couldn’t be completed. Permission denied`


I am trying to open a text document from within my SwiftUI app on macOS.

Unfortunately, I get the error The operation couldn’t be completed. Permission denied. How can I work around this?

I tried with sandbox enabled and access to the Downloads folder (where the file actually is located) and with sandbox disabled–yielding the same result.

Opening up the file in Terminal with open ~/Downloads/Test.exampletext -a /System/Applications/TextEdit.app totally works. The url I hand in to the func is correct and the same as stated above.

Code is as follows:

func openFileViaTerminal(_ url: URL) {
  guard let texteditURL = getTextEditURL()
  else { return }
  let shellProcess = Process()
  shellProcess.launchPath = "/usr/bin"
  shellProcess.arguments = [
    "open \(url.path()) -a \(texteditURL.absoluteString)"
  ]
  do {
    try shellProcess.run()
  } catch {
    AppLogger.standard.error("\(error.localizedDescription)")
  }
}

Solution

    • /usr/bin is not an executable file - it is a directory. You should set launchPath to the path to the executable file, not its enclosing directory.
    • You should pass the command line arguments as three separate elements of the arguments array.
    • absoluteString gives you a string representing a URL, but open expects paths. You should do texteditURL.path(...), in the same way you did it for the url parameter.
    • I don't think open understands percent encoding, so you should use .path(percentEncoded: false).

    Taking all that into account, you can write:

    func openFileViaTerminal(_ url: URL) {
        let texteditURL = URL(filePath: "/System/Applications/TextEdit.app")
        
        let shellProcess = Process()
        shellProcess.launchPath = "/usr/bin/open"
        shellProcess.arguments = [
            url.path(percentEncoded: false), "-a", texteditURL.path(percentEncoded: false)
        ]
        do {
            try shellProcess.run()
        } catch {
            print(error)
        }
    }
    

    Note that this should not be in a sandbox.