I'm trying to read the contents of a file on the filesystem in a macOS Swift app (Xcode 9 / Swift 4).
I'm using the following snippet for it:
let path = "/my/path/string.txt"
let s = try! String(contentsOfFile: path)
print(s)
My problem is the following:
1. This works in a Playground
2. This works when I use the Command Line Tool macOS app template
3. This terminates in a permission error when I use the Cocoa App macOS app template
The permission error is the following:
Fatal error: 'try!' expression unexpectedly raised an error:
Error Domain=NSCocoaErrorDomain Code=257 "The file "data.txt" couldn't be opened because you don't have permission to view it."
UserInfo={NSFilePath=/my/path/data.txt, NSUnderlyingError=0x60c0000449b0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
I guess it's related to sandboxing but I found no information about it.
1. How can I read from the filesystem in a sandboxed app? I mean there are so many GUI apps which need an Open File dialog, it cannot be a realistic restriction of sandboxed apps to not read files from outside the sandbox.
2. Alternatively, how can I switch off sandboxing in Build Settings?
3. Finally, I tried to compare the project.pbxproj files between the default Cocoa Apps and Command Line Tool template and I didn't see any meaningful difference, like something about security or sandbox. If not here, where are those settings stored?
You are correct that this is a sandboxing restriction.
>> there are so many GUI apps which need an Open File dialog, it cannot be a realistic restriction of sandboxed apps to not read files from outside the sandbox
Sandboxed apps cannot read files from outside the sandbox, except where explicitly given permission by the user via an Open File dialog, which returns a "security scoped URL".
You can turn off sandboxing by selecting the project item in the navigator pane, and choosing the Entitlements tab of the project editor. This is OK to do, but it means you can't distribute the app through the Mac App Store. (It should still be code-signed, though, for ad-hoc distribution, to avoid being blocked by GateKeeper.)
Or, you can let your app be sandboxed, and use NSOpenPanel to get permission from the user. In this case, you can save a security scoped bookmark from the URL that can be re-consistituted the next time the app launches, so you only need to get permission once.
Or, if the file you're trying to read, you can arrange to place it inside the sandbox somewhere (such as the Application Support folder, if not you app bundle), so permissions are not an issue.
Finally, all modern Mac apps should not use path-based APIs except in extremely unusual circumstances. URL-based APIs are always preserved. (One reason: there are no security-scoped paths, only URLs.)