Balancing implicit startAccessingSecurityScopedResource

If I'm reading this guide correctly, macOS will automatically/implicitly startAccessingSecurityScopedResource for files opened via drag to Dock icon or NSOpenPanel.

And I'm expected to call stopAccessingSecurityScopedResource to balance it once I'm done. Does that mean that if I don't, I'm leaking kernel resources as per the docs?

If you fail to relinquish your access to file-system resources when you no longer need them, your app leaks kernel resources. If sufficient kernel resources leak, your app loses its ability to add file-system locations to its sandbox, such as with Powerbox or security-scoped bookmarks, until relaunched.

What is this limit in practice for macOS and iOS? I've seen number ranging from 1000-2500.

From my testing, iOS does not provide the same implicit startAccessingSecurityScopedResource when using UIDocumentPickerViewController. Is this a correct observation/per design?

Now, in the cases where I'm creating an NSURL by resolving a saved bookmark, I'm expected to explicitly startAccessingSecurityScopedResource.

Based on this, from what I can tell, this means that I can't universally call startAccessingSecurityScopedResource whenever I access a resource by URL, balanced with a stopAccessingSecurityScopedResource when done, as depending on how I got the URL it might already be implicitly started.

Is this a correct observation? Do I need to explicitly check whether I'm on iOS (never implicit?), or macOS (sometimes implicit?), and selectively startAccessingSecurityScopedResource based on every call site that may give me a security-scoped file? If so, is there a complete list of the entrypoints that may give me such files (file dialog, drag and drop, etc)?

Thanks!

From my testing, iOS does not provide the same implicit startAccessingSecurityScopedResource when using UIDocumentPickerViewController. Is this a correct observation/per design?

The docs at https://developer.apple.com/documentation/uikit/uidocumentpickerviewcontroller does seem to corroborate this observation.

One idea I had for making iOS and macOS behave similar in this regard was to explicitly stop accessing any resource from the file diaogs, but according to https://stackoverflow.com/questions/25627628/sandboxed-mac-app-exhausting-security-scoped-url-resources that practice has at some point been discouraged by Apple (unfortunately the original thread seems to have been lost in the forum upgrade). Is this recommendation still valid?

A URL picked via NSOpenPanel, UIDocumentPickerViewController, and fileImporter is security-scoped bookmarked, and so you will need to call start/stopAccessingSecurityScopedResource to manage the access, unless you use something like NSDocument or UIDocument, which handle the security-scoped bookmark for you.

If the picked URL happens to be in the app sandbox, you can access it without calling start/stopAccessingSecurityScopedResource, but calling the methods isn't harmful.

I typically do start/stopAccessingSecurityScopedResource on a URL provided by the file pickers, unless I'd just pass the URL to NSDocument or UIDocument. If you see any issue by doing this, please share the details and I'd look into it.

I'm unclear about the limit, but when you reach the limit, startAccessingSecurityScopedResource will return false, and you can handle the situation from there.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

I typically do start/stopAccessingSecurityScopedResource on a URL provided by the file pickers

If I'm reading this guide correctly, the macOS file picker will already have implicitly started accessing the resource on your behalf.

So if you do an explicit start/stopAccessingSecurityScopedResource on top of that, the resource is still missing one stopAccessingSecurityScopedResource to balance things out, and the resource access will leak.

OK, I had a conversation with a Foundation framework engineer and my colleague Kevin Elliott, and would like to confirm that the behavior the guide describes is right:

  • On iOS (and its variants), you need to call startAccessingSecurityScopedResource before accessing a URL from a file picker, and match with a stopAccessingSecurityScopedResource call when you are done with the file.

  • On macOS, you can access a URL from a file picker without calling startAccessingSecurityScopedResource beforehand, but need to call stopAccessingSecurityScopedResource when you are done with the file. This is so that existing code written before sandboxing being introduced can still consume URLs from a picker. Failing to call stopAccessingSecurityScopedResource will leak a security scope resource.

With this in mind, now to your questions:

From my testing, iOS does not provide the same implicit startAccessingSecurityScopedResource when using UIDocumentPickerViewController. Is this a correct observation/per design?

Correct.

Now, in the cases where I'm creating an NSURL by resolving a saved bookmark, I'm expected to explicitly startAccessingSecurityScopedResource.

Based on this, from what I can tell, this means that I can't universally call startAccessingSecurityScopedResource whenever I access a resource by URL, balanced with a stopAccessingSecurityScopedResource when done, as depending on how I got the URL it might already be implicitly started.

Is this a correct observation?

Correct.

Do I need to explicitly check whether I'm on iOS (never implicit?), or macOS (sometimes implicit?), and selectively startAccessingSecurityScopedResource based on every call site that may give me a security-scoped file?

Yeah, you will need an extra stopAccessingSecurityScopedResource call on macOS. Here is an example flow that makes the URL access structured:

  1. Call startAccessingSecurityScopedResource on a picked URL.
  2. Convert the URL to a piece of security scoped bookmark data using bookmarkData(options:includingResourceValuesForKeys:relativeTo:).
  3. Call stopAccessingSecurityScopedResource to match step 1.
  4. On macOS, call stopAccessingSecurityScopedResource again to release the resource implicitly started by the picker.

From now on, when you need to use the URL, create it with the bookmark data (init(resolvingBookmarkData:options:relativeTo:bookmarkDataIsStale:), and use start/stopAccessingSecurityScopedResource to access it.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Balancing implicit startAccessingSecurityScopedResource
 
 
Q