I'm on Objective-C, Xcode 11, macOS not iOS, sandboxed application.
I need to manually update recent documents menu. I store the URLs in bookmarks so i can access them according to sandbox.
What is weird is that code A works but B does not. Does anyone have an explanation for this?
// Code A
NSURL* bookmarkURL = (some valid URL from bookmark);
[bookmarkURL startAccessingSecurityScopedResource]; <- returns TRUE
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:bookmarkURL];
//[bookmarkURL stopAccessingSecurityScopedResource]; <-- Without closing access it works
// Code B
NSURL* bookmarkURL = (some valid URL from bookmark);
[bookmarkURL startAccessingSecurityScopedResource]; <- returns TRUE
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:bookmarkURL];
[bookmarkURL stopAccessingSecurityScopedResource];
B doesn't work (with closing security access)! It feels wrong to not close security access. For case B the following error is thrown
Insert failed for list identifier com.apple.LSSharedFileList.ApplicationRecentDocuments Error: Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted" (Restricted by sandbox) UserInfo={NSDebugDescription=Restricted by sandbox}
Without starting security access at all same error is thrown (obviously).
It seems that the application should hold onto the security scoped permission as long as the document is in the (manually managed) recent list.
In my case the error manifested in that
It seems that internally the recent documents list is managed by a separate thread, which in turn communicates with the system process
sharedfilelistd
. If IstopAccessingSecurityScopedResource
, I can observe asandboxd
violation in Console.app with the following call stack (abbreviated):What this shows is there is a thread started by the system, performing some asynchronously dispatched operation called
[_NSRecentItemsMenuController _notePendingRecentDocumentURLs...]
, which in turn issues XPC calls to a SFL (shared file list), which somehow results in an attempt to read the specified document, which is blocked by App Sandbox.We can therefore conclude that after calling
noteNewRecentDocumentURL
, the application still has to hold the permission to access the file because the actual management of the recent list happens on another thread asynchronously, and that thread has to be able to check that the file exists or read it for some other reason.I rewrote my application so that it holds onto the security scoped resources as long as the associated files are in the recent list, and stops accessing them when they are removed from the list. Internet wisdom says there are a few thousand such resources available, see What are the current kernel resource limits on security-scoped bookmarks?. Since my recent list never grows to such a size, my application will never run out of kernel resources.
See also https://developer.apple.com/forums/thread/710278