iOS folder bookmarks

I have an iOS app that allows user to select a folder (from Files). I want to bookmark that folder and later on (perhaps on a different launch of the app) access the contents of it. Is that scenario supported or not? Can't make it work for some reason (e.g. I'm getting no error from the call to create a bookmark, from a call to resolve the bookmark, the folder URL is not stale, but... startAccessingSecurityScopedResource() is returning false.

Answered by DTS Engineer in 854824022

I have an iOS app that allows users to select a folder (from Files). I want to bookmark that folder and later on (perhaps on a different launch of the app) access the contents of it. Is that scenario supported or not?

Technically, yes, this works. However, practically speaking... it doesn't work very well. The problem here is that due to a bug (r.102995804), NSURL isn't able to resolve bookmarks across volume mounts. That means bookmarks within the device work fine and bookmarks to other volumes initially work... but then break completely once the volume has been unmounted.

Unfortunately, I think that makes them pretty unusable in practice, since one of the main reasons a user would WANT to persistently reference an external directory is that they're accessing external storage. More to the point, it's hard to build the kind of "automatic" interface that bookmarks allow when the probability is so high that the bookmark simply won't work at all.

Finally, while this is a known bug, I'd appreciate you filing a bug on this and posting the bug number back here. It turns out that is a somewhat tricky issue to resolve, so duplicate bugs are important.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

No, I'm talking about We explicitly say it does not apply to security-scoped bookmarks:

"This option causes an implicit call to startAccessingSecurityScopedResource() on the returned URL when it’s ready to use the resource."

The way this is written makes it sound like there will be a call to startAccessingSecurityScopedResource somewhere, so that a subsequent pair of explicit startAccessingSecurityScopedResource and stopAccessingSecurityScopedResource will still leave access at 1, leaking kernel resources.

It his not the case? Is this consistent across macOS and iOS?

"This option isn’t applicable to security-scoped bookmarks."

The documentation phrasing isn't clear about whether it's talking about NSURLBookmarkResolutionWithSecurityScope specifically (which is only available on macOS), or if it also applies to iOS (where all bookmarks are security scoped AFAIU from your earlier messages). Can you clarify this point?

I think a lot of the confusion in this area is that there are differences between how macOS and iOS behaves, and differences in which APIs are available, and for people like me who's trying to write cross platform code, and account for any differences, we need to know those subtle differences to account for them, to give a consistent behavior out to our users.

A clear guide that incorporates both OSes would be very much appreciated 🙌🏻

(e.g. https://developer.apple.com/documentation/security/accessing-files-from-the-macos-app-sandbox is great, but is specifically about macOS)

First, a quick comment here:

And on iOS, what are the equivalent creation and resolve options? The "minimal" variants?

So, let me actually run through the option "set" here. First, the "standard" cases that are actually in common usage:

  • "0"/NULL/No Option: This is a "default" bookmark and what you'd almost always use on iOS. On macOS, they're also used to transfer access between processes and may be used if/when you're trying to track files in a situation where you're already "known" to have access to the file (for example, "shoebox" apps tracking files within their shoebox directory).

  • NSURLBookmarkCreationSuitableForBookmarkFile: This creates the exact same data used to create "Alias" files in the Finder.

  • NSURLBookmarkCreationWithSecurityScope/OnlyReadAccess: The macOS bookmark type used to preserve access:

Edge cases:

  • NSURLBookmarkCreationWithoutImplicitSecurityScope: Discussed in more detail below, but allows the creation of a bookmark that specifically DOESN'T provide any access to the target. Rarely, if ever, useful to applications..

  • NSURLBookmarkCreationMinimalBookmark: Theoretically, this creates a bookmark that is smaller than a standard bookmark but also more likely to fail at resolution. I say theoretically… because I don't think they're actually any smaller* and never have been.

*I believe this option was originally included because bookmarks duplicated the option set of aliases (the original structure from Classic Mac OS 7), but I'm not sure we ever actually implemented a smaller format. Keep in mind that the Alias Manager was introduced in 1991 when a "smaller" format was FAR more valuable than it is today. FYI, this is hinted at in the quoting and weasel words in the header doc:

"creates bookmark data with "less" information, which may be smaller but still be able to resolve in certain ways"

The "minimal" variants?

Making this explicit, I can't really think of any reason to ever use a minimal bookmark. Within the system, they're sometimes used when creating temporary references for IPC (where immediate resolution makes failure unlikely), but I think that's mostly because it seemed more "correct", not because it had any practical benefit. As I mentioned above, I'm not sure there is ANY benefit compared to passing in "0".

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

The way this is written makes it sound like there will be a call to...

Yes, and I'm actually not sure where that documentation entry actually came from, as it doesn't actually make very much sense (r.165622059). The header doc is a bit more clear:

"Disable implicitly starting access of the ephemeral security-scoped resource during resolution. Instead, call -[NSURL startAccessingSecurityScopedResource] on the returned URL when ready to use the resource. Not applicable to security-scoped bookmarks."

The key word there is "ephemeral". On macOS, there is infrastructure in place such that if an app makes a "normal" bookmark and gives it to another app, that other app has access to the file it JUST received without any additional "action". That mechanism is what "ephemeral" and "implicit" refer to. That system is better described in NSURLBookmarkCreationWithoutImplicitSecurityScope:

"Bookmarks that you create without security scope automatically carry implicit ephemeral security scope. This security scope is valid until reboot at the latest, and confers access to the resource to any other process that resolves the bookmark. Using this option prevents inclusion of this ephemeral security scope.

When using this option, other processes can’t call startAccessingSecurityScopedResource on the resolved URL. The option prevents providing unintended access to resources to other processes, and is also a performance optimization that reduces the size of the bookmark."

Returning to "NSURLBookmarkResolutionWithoutImplicitStartAccessing", the key sentence in its header doc is the combination of:

(1)
"Instead, call -[NSURL startAccessingSecurityScopedResource] on the returned URL when ready to use the resource."

...and this sentence from NSURLBookmarkCreationWithoutImplicitSecurityScope:

(2)

"This security scope is valid until reboot at the latest..."

If the ephemeral access is still valid, then startAccessingSecurityScopedResource on that URL will grant you access. If it isn't, then it won't. The problem is that WHEN it will still be active... isn't actually well defined. The "valid until reboot at the latest" is a bit of a truism as, yes, resetting the entire system does reset "everything". However, we didn't say it WOULD last that long, which is what you actually wanted to "know". I THINK the answer is that your access is actually tied to the providing apps’ accesses, but that's not formally documented nor something that can really be relied on. The ugly part here is that if IF that access has ended, then #2 can fail.

In any case, that's why:

that a subsequent pair of explicit startAccessingSecurityScopedResource and stopAccessingSecurityScopedResource will still leave access at 1, leaking kernel resources.

Is this not the case?

No, I don't think so. I believe the URL itself will end up releasing any ephemeral reference it's holding when the URL object is destroyed.

Is this consistent across macOS and iOS?

I think the behavior of this particular area should be consistent across platforms.

"This option isn’t applicable to security-scoped bookmarks."

The documentation phrasing isn't clear about whether it's talking about NSURLBookmarkResolutionWithSecurityScope specifically (which is only available on macOS).

It's not applicable to "true" security scoped bookmarks (macOS only) because both options are interacting with the same kernel structures, so including both flags would imply an odd resolution that doesn't really make sense.

or if it also applies to iOS (where all bookmarks are security scoped AFAIU from your earlier messages). Can you clarify this point?

Unfortunately, there's a breakdown in vocabulary here that's hard to explain. iOS bookmarks are security scoped in the sense that they're capable of preserving access in the same way macOS does. However, the internals of how that works are different between the two platforms.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

iOS folder bookmarks
 
 
Q