Read and Write Data in a Sandboxed App (Part 1)
Discover how to get read and write permission to user's Mac folders and files in an app with Sandbox enabled.
• 4 min read
If you are developing a Mac app you may need to read and write files to the user's Mac folders, outside of the app container.
I found myself in the same situation many times. So in this guide I want to share what is my understanding of this topic so far.
I gathered this information across forums, tutorials and the Apple documentation.
This is a vast topic that involves also user privacy and security of its data, in this guide we will cover only the actual implementation of the reading and writing process.
App Sandbox
What is the App Sandbox?
Apple's Documentation states that:
App Sandbox is used to) Restrict access to system resources and user data in macOS apps to contain damage if an app becomes compromised. App Sandbox provides protection to system resources and user data by limiting your app's access to resources requested through entitlements.
The app sandbox is meant to keep users safe from apps that contain malicious code or contain vulnerabilities that an attacker can exploit for malicious purposes. The sandbox protects users' assets from damage or theft.
Apple now mandates app sandboxing in iOS and macOS apps.
To distribute a macOS app through the Mac App Store, you must enable the App Sandbox capability.
So, the App Sandbox is like a wall that surrounds our app and lets us communicate only with the external resources that the user grant us permission.
Setup App Sandbox
To configure the App Sandbox, go to your Target and under Signin & Capabilities you should already see App Sandbox.
If that's not the case, hit the + button and search and add App Sandbox.
You should also see the Hardened Runtime, basically this is another security capability that protects the integrity of the software.
Apple's Documentation states that:
The Hardened Runtime, along with System Integrity Protection (SIP), protects the runtime integrity of your software by preventing certain classes of exploits, like code injection, dynamically linked library (DLL) hijacking, and process memory space tampering.
Now, back to our App Sandbox.
Under App Sandbox you can see a lot of options, in this guide we will look at the File Access section.
These options controls, at a high level, which folders your app can ask permission to read, write or both.
This doesn't mean that by selecting read/write for User Selected File in the App Sandbox settings, you can access those folders right away. But you still need to ask the user to give you permission to read/write to those folder.
Sandboxed vs. Un-sandboxed app
Say we need to create a file in the user's Desktop.
In an un-sandboxed app you can simply call write(to:)
try data.write(to: fileURL)
And you're done.
In a sandboxed app, instead, if we do the same process, the function returns an error that looks like this:
You don't have permission to write to user folder.
This happens because in a sandboxed app you can only write to and read from the folders in the app’s container.
This is one of Apple's methods for limiting the vulnerable surface area of the app to attack by malware.
Write to a sandboxed app
Now that we saw what is the behaviour of sandboxed and un-sandboxed app, let's see how to actually write from a sandboxed app.
Since we want to write to a non-default folder (like Downloads
, Images
, ...), we need to ask the user where to save our file.
So, in TARGETS -> [TARGET_NAME] -> Signing & Capabilities -> App Sandbox -> File Access -> Type -> User Selected File
change Permission & Access
from None
to Read/Write
.
This will add the com.apple.security.files.user-selected.read-write
entitlement to your project.
Ask where to save the file
Now, before to write the file, we must ask the user where he wants to save the file. We will do this using the NSOpenPanel
class.
let openPanel = NSOpenPanel()
openPanel.prompt = "Select"
openPanel.message = "Please select a folder"
openPanel.allowedContentTypes = [.directory]
openPanel.canChooseFiles = false
openPanel.allowsOtherFileTypes = false
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = true
// Open the modal folder selection panel.
let okButtonPressed = openPanel.runModal()
if okButtonPressed == .OK {
// If the user doesn't select anything, then the URL \"file:///\" is returned, which we ignore
if let url = openPanel.urls.first,
url.absoluteString != "file:///" {
print("User selected folder: \\(url)")
// Persist user selected folder for later launches
} else {
print("User did not select a folder")
}
} else {
print("User cancelled folder selection panel")
}
Note
This is a synchronous process even if there is user interaction. So you don't need completion blocks to perform actions when the user selects the folder.
Now, once the user selected the folder, you can write like we did in the un-sandboxed app.
let fileURL = URL(fileURLWithPath: "file_name.json", relativeTo: selectedFolderURL)
try data.write(to: fileURL)
Persist user intent across launches
See how to persist the selected folder, so in the next launches you don't have to ask permission again. And the user will have a smoother experience.
How to persist user folders permission
Conclusion
I hope I've helped you understand how the sandbox works and how to read and write files outside of the app container.
If you have any question about this article, feel free to email me or tweet me @franceleonidev and share your opinion.
Thank you for reading and see you in the next article!
Share this article