Privacy and security have always been top priorities for Apple. Sandboxing is a result of that commitment. On iOS, applications have been sandboxed from day one. This isn't true for macOS applications. Sandboxing was added to macOS with the announcement of the Mac App Store in 2010.
But what is sandboxing? How does it impact developers? And why does the operating system sandbox applications?
Why Is Sandboxing Necessary?
Sandboxing significantly increases the security and integrity of the operating system by limiting what an application is allowed to do. On iOS, for example, an application cannot access the sandbox of another application. Why is that important?
Even though Apple reviews every application before it is allowed into the App Store, the review process isn't perfect. It is possible that malicious applications make their way into the App Store. If every third party application would be able to access the file system and the sandbox of other applications, the platform would soon be in disarray.
But an application doesn't need to be malicious for it to cause mayhem. An innocent bug could also cause havoc. By sandboxing applications, this is much less likely.
What Is Sandboxing?
For historical reasons, sandboxing rules for macOS are less strict than those for iOS, tvOS, and watchOS. The macOS operating system and its file system operate differently and are structured differently.
The idea is similar, though. Every application is given a sandbox, a directory it can use to store data in. If the application needs access to data on the device that isn't located in the application's sandbox, it needs to request the data through a system interface. And even the system interfaces have their limitations. Not everything is accessible through a system interface.
The photos library of a device is a fine example. An application doesn't have direct access to the photos stored on the user's device. To access the user's photos, third party application need to use a system interface. The idea is that the operating system knows exactly what data the application accesses if it isn't located in the application's sandbox.
System interfaces add a layer of security, but they also protect the user's privacy. Third party applications can access the user's health data through the HealthKit framework. But the user also needs to grant access to the application before it is allowed to read or write health data.
Where Is the Sandbox Located?
Even though the location of the application sandbox isn't important from a development perspective, you can ask the operating system for its location on the device. The following example targets the iOS platform.
Fire up Xcode and create a new project. In the AppDelegate
class, update the application(_:didFinishLaunchingWithOptions:)
method as shown below. We ask the operating system for the location of the home directory, the root of the application sandbox.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
print(NSHomeDirectory())
return true
}
If you run the application on a physical device, the output in the console looks something like this.
/var/mobile/Containers/Data/Application/1F1468AA-A6D4-4379-84C7-ABC99BDC9302
The output looks a bit different if you run the application in the simulator.
/Users/Bart/Library/Developer/CoreSimulator/Devices/55B96C49-D3CE-4DFC-BE39-17CC475A8396/data/Containers/Data/Application/035918FB-26F5-434C-AA58-C069427EF41C
What Does the Sandbox Look Like?
The sandbox of an application doesn't start its life as an empty container. It houses several directories and each of these directories has a clear purpose.
Update the implementation of application(_:didFinishLaunchingWithOptions:)
as shown below. We ask the operating system for the contents of the home directory, the root of the application's sandbox.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if let items = try? FileManager.default.contentsOfDirectory(atPath: NSHomeDirectory()) {
for item in items {
print(item)
}
}
return true
}
The output in the console should look something like this.
.com.apple.mobile_container_manager.metadata.plist
Documents
Library
tmp
The first item is a property list used by the operating system. The remaining items are directories that live in every application sandbox on iOS.
Even though you are free to store data wherever you like, as long as it is in the application sandbox, you need to understand the purpose of each of these directories. Apple has provided developers with a set of guidelines, which discuss the purpose of these directories and, more importantly, how the operating system treats the contents of these directories.
For example, the contents of the tmp directory are not backed up by iTunes and iCloud. As the name of the directory implies, this directory should only be used for storing temporary documents or other blobs of data.
Documents
The Documents directory is ideal for storing data that is directly related to the user. It is very similar to the Documents directory on macOS.
If your application uses a SQLite database to store the user's data, then the database files could be stored in the Documents directory.
Library
The Library directory, contains the Caches and Preferences directories. As the name implies, the Caches directory is ideal for storing cached data. This directory can be purged by the operating system when it decides it needs to free up space on the user's device. It goes without saying that this directory is not backed up by iTunes and iCloud.
The Preferences directory contains the property list of the default defaults database. What does that mean? If you store a key-value pair in the default defaults database, that key-value pair is stored in a property list in the Preferences directory.
Add the following snippet to the application(_:didFinishLaunchingWithOptions:)
method and run the application.
let userDefaults = UserDefaults.standard
userDefaults.set(true, forKey: "DidLaunch")
As you can see, a property list is created and stored in the Preferences directory of the Library directory.
tmp
Temporary data should be stored in the tmp directory. This directory is purged from time to time to make sure it doesn't unnecessarily take up disk space on the user's device. Like the Caches directory, it isn't backed up by iTunes and iCloud.
Application Bundle
The application itself isn't located in the sandbox. This is a common misconception. To find out where the application bundle is located, you can ask the bundle of the application for its URL or path.
Bundle.main.bundleURL
Bundle.main.bundlePath
Update the implementation of the application(_:didFinishLaunchingWithOptions:)
method as shown below.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
print(NSHomeDirectory())
print(Bundle.main.bundlePath)
return true
}
If you run the application on a physical device, the output in the console looks like this. This illustrates that the application bundle isn't located in the application sandbox.
/var/mobile/Containers/Data/Application/6B54E61E-2F37-4203-A11D-AED2C1E6762E
/var/containers/Bundle/Application/2428465B-4D78-4C0B-B989-7290AAD27A7F/Sandboxing.app
You cannot and should not modify the contents of the application bundle. If the application bundle is modified, the operating system refuses to launch the application.
Inspecting the Sandbox
During development, it can be useful to inspect the contents of an application's sandbox. Xcode makes this very easy. Select Devices from Xcode's Window menu and, on the left, select the device the application is installed on.
At the bottom, select the application you are interested in and click the gear icon. You can show, replace, or download the container of the application.
If you use the simulator for development, then this is less trivial. The sandbox of an application installed on a simulator is located in the bowels of the Library directory on your development machine.
Fortunately, there is a solution. I have been using SimPholders for several years and it is an indispensable tool for Cocoa development. It gives you quick access to the applications installed on the various simulators you have access to.
What Is the Purpose of Running Apps In a Sandbox?
Isolation
Sandboxing ensures that the apps installed on the device are isolated from each other. One app cannot access the data or resources of another app without explicit permissions. That strict isolation is crucial for protecting the user's privacy. It also makes sure malicious apps cannot access (sensitive) data from other apps.
Security
If an app turns out to be malicious or is compromised, the damage it can do is limited thanks to the sandbox. It can't access the operating system or other apps, which helps in preventing attacks or data breaches.
Stability
By restricting apps to their own sandbox, the operating system can prevent a misbehaving app from affecting other apps or the system itself. If an app crashes inside its sandbox, it won't take down other apps or the entire system with it.
Resource Management
Sandboxing enables the system to manage resources more effectively and efficiently. It can allocate memory, processing power, and other resources to each app based on its sandboxed environment. By doing so, it prevents that apps hog system resources unnecessarily.
Permission Management
Modern operating systems, such as iOS and macOS, use sandboxing in conjunction with a permissions system. What does that mean? Apps need to request specific permissions (e.g., access to the device's camera or microphone, obtaining the user's location) before they can perform certain operations. The sandbox ensures that apps can only access the resources and data they have permission for.
Data Integrity
Sandboxing also prevents that one app inadvertently or maliciously modifies the data of another app, preserving the integrity of each app's data.
What's Next?
It is important that you know what the application sandbox is and represents. The directories it contains each have a purpose and you need to be careful which directory you store data in.