Here's the problem I'm trying to solve: Create an iOS app which can scan the Downloads folder (where airdropped audio files arrive), identify audio media files, and play them, retaining some of its own metadata about them (basically, create textual notes mapped to timestamps and store that information in the apps own storage).
I am not able to access that folder. I am able to get a path from
NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.downloadsDirectory, FileManager.SearchPathDomainMask(arrayLiteral: FileManager.SearchPathDomainMask.userDomainMask), true)
or a URL from
NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.downloadsDirectory, FileManager.SearchPathDomainMask(arrayLiteral: FileManager.SearchPathDomainMask.userDomainMask), true)
but
let fileUrls = try fileManager.contentsOfDirectory(at:downloads, includingPropertiesForKeys: [])
fails with an error that the folder does not actually exist, with or without a call to downloadsUrl.startAccessingSecurityScopedResource()
.
Determining whether this is a permissions issue, or if I'm getting a URL to an application-container local folder that has nothing to do with the one I am looking for is compounded by the fact that if I set the build setting Enable App Sandbox, then deployment to my phone fails with Failed to verify code signature. I have spent hours trying every possible combination of certificates and deployment profiles, and ensured that every possibly relevant certificate is trusted on my phone.
Disable app-sandbox and it deploys fine, either with automatic signing or an explicit cert and profile.
I have an entitlements file with the following - though, without the ability to enable app sandbox and run it on a phone with actual contents in the downloads folder, it is probably not affecting anything:
<key>com.apple.security.files.downloads.read-only</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
So, questions:
- Should the URL returned by the above call be the
Downloads/
folder airdropped to in the first place? Or is it a URL to some app-local folder that does not exist? - Does the entitlement
com.apple.security.files.downloads.read-only
even allow an app to list all files in the downloads directory (presumably asking the user's permission the first time), or does the permission only get requested when using a picker dialog? (the point here is to find any new audio files without making the user jump through hoops) - If I could get it deployed with app-sandbox enabled, would the above code work?
Backstory: I'm a software engineer, audio plugin author, Logic Pro user and musician. My workflow (and probably many other Logic user's) for work-in-progress music is to airdrop a mix to my phone, listen to it in a variety of places, make notes about what to change, edit - rinse and repeat. For years I used VLC for iOS to keep and play these in-progress mixes - you could airdrop and select VLC as the destination (yes, Logic can add to your Apple Music library, but trust me, you do not want 20 revisions of the same song cluttering your music library and sync'd to all your devices).
Last year, the behavior of Airdrop changed so that the target app for audio is always Files, period, wrecking that workflow. While I eventually discovered that, with an elaborate and non-obvious dance of steps, it is possible to copy files into VLC's folders, and make them available that way, it is inconvenient, to say the least - and VLC is less than fabulous anyway - it would be nice to have an app that could associate to-do notes with specific timestamps in a tune, A/B compare sections between old and new versions and things like that.
So, figuring sooner or later I was going to get into a car accident futzing with the Files app to listen to mixes while driving, perhaps I should write that app.
But the ability to do that at all relies on the ability of an app to list and access the Downloads folder airdropped audio files land in (assuming the user has given permission to access it, but that should be needed once).
Your post talks a lot about App Sandbox. Be aware that App Sandbox is only a thing on macOS. iOS and its child platforms do sandbox apps, but that’s not the App Sandbox.
There are a bunch of differences between App Sandbox on macOS and the sandbox on iOS, but the key things for this discussion are:
-
The iOS sandbox is mandatory. On macOS you opt it to it using the
com.apple.security.app-sandbox
. On iOS you don’t need to opt it and you can’t opt out. -
The App Sandox entitlements, as listed here, don’t apply on iOS.
-
There are no temporary exception entitlements on iOS.
So, coming back to your specific questions:
Should the URL returned by the above call the Downloads/ folder airdropped to in the first place?
No. That’ll return the Downloads folder within your app’s container.
Does the entitlement com.apple.security.files.downloads.read-only even allow an app to list all files in the downloads directory … ?
No. That’s an App Sandbox entitlement and thus only relevant on macOS.
If I could get it deployed with app-sandbox enabled, would the above code work?
No. App Sandbox is only relevant on macOS.
It might be possible to get persistent access to the Downloads folder on iOS via the following:
-
Present a file picker that lets the user select a folder.
-
Have the user select the Downloads folder.
-
Create a bookmark to that folder.
-
Persist that.
-
Resolve that when you need access again in the future.
I say “might” because the Downloads folder is kinda special and so there might be caveats specific to it that I’m not aware of. However, this process works for other folders.
One final caveat: If you read the macOS App Sandbox documentation, like this, you’ll see lots of talk about security-scoped bookmarks. The specific flags in question — for example, the the .withSecurityScope option — are not supported on iOS. However, iOS apps are able to persist access without that flag. So, on iOS you can use the same basic technique as you would on macOS, you just omit the flags that specifically enable the security scope.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"