The application does not have permission to open "Downloads"

My app has the App Sandbox enabled and the File Access to Downloads folder is set to Read / Write in XCode.

Upon clicking on a button the app should open the Finder displaying the Downloads folder. The following code snippet is used to launch the Finder

if let inspirationsDirectory = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first{
            NSWorkspace.shared.open(inspirationsDirectory)
        }

On my MacOS it works well.

After releasing the app to the AppStore and installing it on another Mac the following message is received upon clicking the button:

The application does not have permission to open "Downloads"

Which would be the solution to launch the Finder successfully ? Is it possible to launch the Finder showing the Downloads folder sorted by the Date Added column descending ?

Replies

The Info.plist contains the NSDownloadsFolderUsageDescription key with proper description.

The AppStoreConnect contains under the App Sandbox Information the following key com.apple.security.files.downloads.read-write with proper description.

I put your code into a sandboxed test project and it fails on my machine. That is, I see error alert that you’re seeing on my local machine, without the round trip via the App Store.

If you create a new test project from one of the built-in Xcode templates, how does it behave? Make sure to give it a new name / bundle ID.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

The same as in your case. This seems like a bug to me. My app is properly sandboxed both in the XCode and in the AppStoreConnect as well it contains the correct permission key in the Info.plist to access the Downloads folder. The NSWorkspace instead of raising the error that you do not have permissions, should ask for permissions instead. It should be the user to allow or disallow the access.

Anyway I figured out another way that works properly. Now the app asks for the permission to access the Downloads folder before launching the Finder app.

This seems like a bug to me.

Well, there are two issues in play here:

  • Why is the app behaving differently on your Mac? (A)

  • How to achieve your final goal? (B)


I suspect that the answer to A is related to the state of your app’s container, due to the fact that you’ve been developing your app on your Mac and thus run a bunch of earlier versions that were set up differently from the current version. A good way to check that is to create a new user on your Mac and then run your app from there. I suspect it’ll then behave consistently.

Please try this and let me know what you see.


With regards B, I don’t think there’s a way to achieve your goal as specified. When you open a URL with NSWorkspace, it tries to access properties on that URL. If that URL isn’t within your sandbox, that access fails with a permission error and the open fails.

You can actually see that permissions error if you call open(_:configuration:completionHandler:).

If you change your code to access something available to your sandbox, like /Applications, it works just fine.

If you know the name of a file that exists in the Downloads directory you can pass its URL to activateFileViewerSelecting(_:).

Finally, one interesting tidbit. Your code accesses the Downloads directory within your app’s container. However, for a standard container the Downloads ‘directory’ is not actually a directory but a symlink to the real Downloads directory, which is why you’re hitting this roadblock.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Why is the app behaving differently on your Mac?

I noticed that the app has in the System Settings -> Privacy and Security -> Files and Folders -> the app -> Downloads folder enabled

Not sure if this was done manually in the past or as part of the installation and it somehow disappeared.

How to achieve your final goal?

The URL is within the sandbox. As explained above the app sandbox was configured in XCode and AppStoreConnect as well the privacy key to access the Downloads folder was added in Info.plist. See the attached images.

Regardless if the Downloads folder is a simlink or not the swift code snippet should be capable to open it. It looks like that the NSWorkspace does not function properly. It does not check the app sandbox for the Downloads folder and does not ask the user for permission to access it.

With regards B, I don’t think there’s a way to achieve your goal as specified.

Yes, there is. Voila the solution. Not the best, but at least it works as expected.

func shell(_ command: String) -> String {
        let process = Process()
        let pipe = Pipe()
        
        process.standardOutput = pipe
        process.standardError = pipe
        process.arguments = ["-c", command]
        process.launchPath = "/bin/zsh"
        process.standardInput = nil
        process.launch()
        
        let data = pipe.fileHandleForReading.readDataToEndOfFile()
        let output = String(data: data, encoding: .utf8)!
        
        return output
    }

let result = shell("open Downloads")

Conclusion

Apple should investigate how the NSWorkspace functions beneath. My use case above should be working.

The documentation about the App Sandboxing still mentions entitlement files, which are nowhere to be found nor possible to be added anymore. Seems the entitlements have been moved to the AppStoreConnect. The docs online need some updating as well.

I am here with a similar problem, but I am a step further. (@eskimo: eventually I can open a TSI with Apple..) scenario:

  1. sandobox
  2. user chooses a folder
  3. we save url in UserDefaults using:
let bookmarkData = try url.bookmarkData(options: BOOKMARK_CREATION_OPTIONS,
                                                    includingResourceValuesForKeys: nil,
                                                    relativeTo: nil)
            // save in UserDefaults
            setUserDefault(value: bookmarkData, for: key + SUFFIX, groupId: nil)

  1. when I reopen the bookmark, I can use it to read... BUT...
  2. if I call:

...

        NSWorkspace.shared.open(url)

I got error.

So an unexpected behaviour OR is wanted?

EDIT: if I one usual Open/save dialog before, (keeping app open...) NSworkspace DOES open folder... but now there question is why I cannot leverage on securityScope.

when I reopen the bookmark, I can use it to read

You’re doing that within a {start,stop}AccessingSecurityScopedResource() pair, right?

Are you making the NSWorkspace call in a similar context?

if I one usual open/save dialog before, (keeping app open...) NSWorkspace DOES open folder

That’s because the file dialogs effectively a ‘leak’ a startAccessingSecurityScopedResource() call, which causes its own problems )-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"