Migrating an App to a Sandbox

An app that is not sandboxed places its support files in locations that are inaccessible to a sandboxed version of the same app. For example, the typical locations for support files are shown here:

Path
Description

~/Library/Application Support/<app_name>/

Legacy location

~/Library/Containers/<bundle_id>/Data/Library/Application Support/<app_name>/

Sandbox location

As you can see, the sandbox location for the Application Support directory is within an app’s container—thus allowing the sandboxed app unrestricted read/write access to those files. If you previously distributed your app without sandboxing and you now want to provide a sandboxed version, you must tell OS X to move your support files into their new, sandbox-accessible locations.

When a user launches any app, before the app starts running, OS X checks to see if the app is sandboxed. If so, OS X checks to see if a sandbox container directory exists for that particular app within the user’s Library/Containers folder. If the app’s container directory already exists, then no migration is needed, and your app begins running immediately.

However, if the app’s container directory does not exist, OS X creates the missing sandbox container directory and then migrates the per-user preference file for your app, along with any files that you have specifically listed in the app's container migration manifest.

A container migration manifest is a special property list file containing an array of strings that identify the support files and directories you want to migrate when a user first launches the sandboxed version of your app. The file must be named container-migration.plist, and must appear in the Contents/Resources directory within your app bundle.

For each file or directory you specify for migration, you have a choice of allowing the system to place the item appropriately in your container or explicitly specifying the destination location.

OS X moves—it does not copy—the files and directories you specify in a container migration manifest. That is, the files and directories migrated into your app’s container no longer exist at their original locations. In addition, container migration is a one-way process: You are responsible for providing a way to undo it, should you need to do so during development or testing. The section “Undoing a Migration for Testing” provides a suggestion about this.

Creating a Container Migration Manifest

To support migration of app support files when a user first launches the sandboxed version of your app, create a container migration manifest.

bullet
To create and add a container migration manifest to an Xcode project
  1. Add a property list file to the Xcode project.

    The Property List template is in the OS X “Resource” group in the file template dialog.

  2. Add a Move property to the container migration manifest.

    The Move property contains an array of files to move into your container directory. This property is usually the lone top-level key in a container migration manifest. You add it to the empty file as follows:

    • Right-click the empty editor for the new .plist file, then choose Add Row.

    • In the Key column, enter Move as the name of the key.

      You must use this exact casing and spelling.

    • In the Type column, choose Array.

  3. Optionally add a Symlink property to the container migration manifest.

    The Symlink property contains an array of files to link into your container directory. The resulting symbolic links in your container directory point to files elsewhere.

  4. Add a string to the Move (or Symlink) array for the first file or folder you want to migrate.

    For example, suppose you want to migrate your Application Support directory (along with its contained files and subdirectories) to your container. If your directory is called App Sandbox Quick Start and is currently within the ~/Library/Application Support directory, use the following string as the value for the new property list item:

    ${ApplicationSupport}/App Sandbox Quick Start

    No trailing slash character is required, and space characters are permitted. The search-path constant in the path is equivalent to ~/Library/Application Support. This constant is described, along with other commonly used directories, in “Use Variables to Specify Support-File Directories.”

Similarly, add additional strings to identify the original (before sandboxing) paths of additional files or folders you want to migrate.

When you specify a directory to be moved, keep in mind that the move is recursive—it includes all the subdirectories and files within the directory you specify.

Before you first test a migration manifest, provide a way to undo the migration, such as suggested in “Undoing a Migration for Testing.”

bullet
To test a container migration manifest
  1. In the Finder, open two windows as follows:

    • In one window, view the contents of the ~/Library/Containers/ directory.

    • In the other window, view the contents of the directory containing the support files named in the container migration manifest—that is, the files you want to migrate.

  2. Build and run the Xcode project.

Upon successful migration, the support files disappear from the original (nonsandbox) directory and appear in your app’s container.

If you want to alter the arrangement of support files during migration, use a slightly more complicated .plist structure. Specifically, for a file or directory whose migration destination you want to control, provide both a starting and an ending path. The ending path is relative to the Data directory in your container. In specifying an ending path, you can use any of the search-path constants described in “Use Variables to Specify Support-File Directories.”

If your destination path specifies a custom directory (one that isn’t part of a standard container), the system creates the directory during migration.

The following task assumes that you’re using the Xcode property list editor and working with the container migration manifest you created earlier in this chapter.

bullet
To control the destination of a migrated file or directory
  1. In the container migration manifest, add a new item to the Move (or Symlink) array.

  2. In the Type column, choose Array.

  3. Add two strings as children of the new array item.

  4. In the top string of the pair, specify the origin path of the file or directory you want to migrate.

  5. In the bottom string of the pair, specify the destination (sandbox) custom path for the file or directory you want to migrate.

File migration proceeds from top-to-bottom through the container migration manifest. Take care to list items in an order that works. For example, suppose you want to move your entire Application Support directory as-is, except for one file. You want that file to go into a new directory parallel to Application Support in the container.

For this approach to work, you must specify the individual file move before you specify the move of the Application Support directory—that is, specify the individual file move higher in the container migration manifest. (If Application Support were specified to be moved first, the individual file would no longer be at its original location at the time the migration process attempted to move it to its new, custom location in the container.)

Undoing a Migration for Testing

When testing migration of support files, you may find it necessary to perform migration more than once. To support this, you need a way to restore your starting directory structures—that is, the structures as they exist prior to migration.

One way to do this is to make a copy of the directories to migrate, before you perform a first migration. Save this copy in a location unaffected by the migration manifest. The following task assumes you have created this sort of backup copy.

bullet
To manually undo a container migration for testing purposes
  1. Manually copy the files and directories—those specified in the manifest—from your backup copy to their original (premigration) locations. (Be careful not to remove the files from your backup copy as you do so.)

  2. Delete your app’s container.

The next time you launch the app, the system recreates the container and migrates the support files according to the current version of the container migration manifest.

An Example Container Migration Manifest

Listing 4-1 shows an example manifest as viewed in a text editor.

Listing 4-1  An example container migration manifest

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
   <key>Move</key>
   <array>
      <string>${Library}/MyApp/MyConfiguration.plist</string>
      <array>
         <string>${Library}/MyApp/MyDataStore.xml</string>
         <string>${ApplicationSupport}/MyApp/MyDataStore.xml</string>
     </array>
  </array>
</dict>
</plist>

This manifest specifies the migration of two items from the user’s Library directory to the app’s container. For the first item, MyConfiguration.plist, only the origin path is specified, leaving it to the migration process to place the file appropriately.

For the second item, MyDataStore.xml, both an origin and a custom destination path are specified.

The ${Library} and ${ApplicationSupport} portions of the paths are variables you can use as a convenience. For a list of variables you can use in a container migration manifest, see “Use Variables to Specify Support-File Directories.”

Use Variables to Specify Support-File Directories

When you specify a path in a container migration manifest, you can use certain variables that correspond to commonly used support file directories. These variables work in origin and destination paths, but the path that a variable resolves to depends on the context. Refer to Table 4-1.

Table 4-1  How system directory variables resolve depending on context

Context

Variable resolves to

Origin path

Home-relative path (relative to the ~ directory)

Destination path

Container-relative path (relative to the Data directory in the container)

The variables you can use for specifying support-file directories are described in Table 4-2. For an example of how to use these variables, see Listing 4-1.

You can also use a special variable that resolves to your app’s bundle identifier, allowing you to conveniently incorporate it into an origin or destination path. This variable is ${BundleId}.

Table 4-2  Variables for support-file directories

Variable

Directory

${ApplicationSupport}

The directory containing application support files. Corresponds to the NSApplicationSupportDirectory search-path constant.

${AutosavedInformation}

The directory containing the user’s autosaved documents. Corresponds to the NSAutosavedInformationDirectory search-path constant.

${Caches}

The directory containing discardable cache files. Corresponds to the NSCachesDirectory search-path constant.

${Document}

${Documents}

Each variable corresponds to the directory containing the user’s documents. Corresponds to the NSDocumentDirectory search-path constant.

${Home}

The current user’s home directory. Corresponds to the directory returned by the NSHomeDirectory function. When in a destination path in a manifest, resolves to the container directory.

${Library}

The directory containing application-related support and configuration files. Corresponds to the NSLibraryDirectory search-path constant.