Creating new text file using Finder Sync API results in deny(1) file-write-create Sandbox error

I am trying to develop an application that also contains an extension for the Finder.

The extension can be used to create a new text file at the current Finder location. I have only added the following permissions to the entitlements file for both the main app and the extension:

com.apple.security.files.user-selected.read-only

I have implemented the FIFinderSync protocol in my extension.

I added a menu item to create a new text file. Whenever I execute this method, the following error is shown in the Console:

Sandbox: Extension(4032) deny(1) file-write-create /Users/username/Documents/New Text.txt

The simplest way to get around this error is to add the following key:

com.apple.security.temporary-exception.files.home-relative-path.read-write

However, applications using this entitlement will be rejected during app review.

The extension is initialised with this code:

FIFinderSyncController.default().directoryURLs = [URL(fileURLWithPath: "/")]

How can I prevent this error to be thrown? I could use security scoped bookmarks, open a dialog and then save this bookmark. However, I have tested plenty of extension available in the App Store and none of them have shown any NSOpenPanel, the files have been created without any problems.

Any help is appreciated.

Answered by DTS Engineer in 684230022

The standard approach here is to have your container app present a file panel as part of the setup process. It then passes the security-scoped URL (or bookmark) to your Finder Sync extension. This serves two purposes:

  • The Finder Sync extension uses this to populate directoryURLs.

  • The Finder Sync extension can save a security-scoped bookmark to retain long-term access to that directory (not just the directory, but the entire hierarchy rooted at that directory).

Oh, btw, this is worrying:

FIFinderSyncController.default().directoryURLs = [URL(fileURLWithPath: "/")]

Finder Sync extensions were not intended to be a general-purpose Finder extension mechanism. Rather, their intended purpose is stated in the name: The expected use case is that the Finder Sync extension own a directory, or small set of directories, that it syncs with some sort of cloud service. From a UI perspective, think iCloud Drive [1].

If you try to use a Finder Sync extension as a general-purpose Finder extension mechanism you will run into problems. For example, if you claim the entire file system starting at / and then the user installs another Finder Sync extension that does the same, things end badly.

Share and Enjoy

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

[1] Although iCloud Drive is not based on this technology.

Accepted Answer

The standard approach here is to have your container app present a file panel as part of the setup process. It then passes the security-scoped URL (or bookmark) to your Finder Sync extension. This serves two purposes:

  • The Finder Sync extension uses this to populate directoryURLs.

  • The Finder Sync extension can save a security-scoped bookmark to retain long-term access to that directory (not just the directory, but the entire hierarchy rooted at that directory).

Oh, btw, this is worrying:

FIFinderSyncController.default().directoryURLs = [URL(fileURLWithPath: "/")]

Finder Sync extensions were not intended to be a general-purpose Finder extension mechanism. Rather, their intended purpose is stated in the name: The expected use case is that the Finder Sync extension own a directory, or small set of directories, that it syncs with some sort of cloud service. From a UI perspective, think iCloud Drive [1].

If you try to use a Finder Sync extension as a general-purpose Finder extension mechanism you will run into problems. For example, if you claim the entire file system starting at / and then the user installs another Finder Sync extension that does the same, things end badly.

Share and Enjoy

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

[1] Although iCloud Drive is not based on this technology.

Thank you for your reply!

So if I understand this correctly, the proper way to handle this problem would be the following:

  • The main application contains a page where the user can add paths to observe. The user would select the path using a NSOpenPanel.
  • The path will be stored as a secure bookmark.
  • All paths that are stored as secure bookmarks can now be used by the extension.

I have one question though: You mentioned that simply using "/" is bad if other extensions do the same thing. Let's assume the user selects "/" because all directories should be used by the Finder extension. What if the user now installs another extension that does the same thing? What does "ned badly" mean in this case?

Regards, Sascha

What does "end badly" mean in this case?

The Finder Sync architecture assumes that each directory has at most one Finder Sync extension monitoring it. If a directory is monitored by multiple extensions, they fight for control over exclusive resources like the various menus and item badges. It generally doesn’t crash, or corrupt user data, or anything really bad, but the UI starts getting weird.

To reiterate, Finder Sync extensions were built to support sync folders (think iCloud Drive, except that isn’t built using this technology :-), not as a general-purpose Finder extension mechanism.

Share and Enjoy

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

Hello,

thanks for the reply, now I understood what you meant. I thought that there was some technical problem. But of course it can get wild, if two extensions try to update the badge icons for the same files / folders.

What is the best way to transfer the data of the scoped URL to the Finder Sync extension? I have tried to use a distributed notification, at least the message is sent and delivered to the extension. All I can pass is a string though.

Regards

What is the best way to transfer the data of the scoped URL to the Finder Sync extension?

There are two standard ways to share state between a Finder Sync extension and its container app:

The best choice depends on the nature of that state. XPC is best if you have lots of dynamic state to manage. For example, in the expected use case for Finder Sync extensions (again, think iCloud Drive) all the state is held in the Service Management login item and the extension acts as a XPC-driven front end to that. This is the only sensible way to design such a product because you need long-running code to manage your uploads and downloads, and the extension’s lifecycle is tied to user activity.

In contrast, for less dynamic state, like configuration items, an App Group will work just fine.

Share and Enjoy

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

Creating new text file using Finder Sync API results in deny(1) file-write-create Sandbox error
 
 
Q