KISS

Be Among the 20% of the Best!

Persistent Application Container Directories in iOS Simulator

| comments

An annoying thing that was introduced a few releases ago in the iOS Simulator is that your apps’ bundle and container directories change their names (a random UUID) every time you run them in Xcode. It makes it very annoying to test your app if you need to check the output files regularly or restore the app’s container to a known state before launching. I’ve found a relatively painless workaround though!

Hard links

An experiment showed that the directory with the app’s container data is just renamed every time, which means its inode number is still the same. Briefly: every file (or rather a blob of data) on a UNIX-like filesystem (such as ext4 or HFS+) is identified by its inode number, and names in FS directories just point to those numbers. Typically, an inode (file) has one name, and it’s possible to create hard links to files, meaning there will be multiple names pointing to the same file (blob of data). That’s exactly what we need — one regular name that keeps changing and one constant name.

Reading https://stackoverflow.com/questions/1432540/creating-directory-hard-links-in-macos-x, we find out that hard links to directories are usually disabled (because they may lead to infinite cycles), and on OSX you can’t even use the ln command for that. Hopefully, we still can use a system call, wrapped in the tiny program hardlink-osx, installed with:

1
$ brew install hardlink-osx

Workaround

Now, to practice.

First, find out your iOS simulator’s UUID with either instruments -s devices or xcrun simctl list devices. Let’s say it’s aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee, so now cd ~/Library/Developer/CoreSimulator/Devices/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/data/Containers/Data/Application/ — that’s where all apps’ data is located on the simulator.

Now, how to find the one for your app? If it writes to NSUserDefaults, there exists a file named bundle_id.plist (where bundle_id is your app’s bundle ID), so you can do this (NB: the ** syntax is zsh’s globbing syntax for matching all directories recursively; if you use bash, you’ll need to employ find):

1
2
3
$ a="$( ls **/bundle_id.plist | cut -d'/' -f1 )"
# a should contain the UUID of your app's container directory:
$ echo $a

The main step here is creating the hard link. NB: you can’t create a hard link to a directory in the same parent directory, which means you need to create another root for links and then link, e.g.:

1
2
$ mkdir ../links
$ hln $a ../links/myapp

That’s it! Now you can use ../links/myapp to list files and other mischiefs (ls ../links/myapp/Documents/).

NB: when you’ve reinstalled your app, you’ll need to manually remove the old directory and recreate the link:

1
2
3
$ a="$( ls **/bundle_id.plist | cut -d'/' -f1 )"
$ rm -rf ../links/myapp
$ hln $a ../links/myapp

Enjoy! Of course, it’s possible to come up with a script to link all your apps and make sure they’re always current, etc. If you have one, be sure to post it in the comments below.

ps

A small tip: I sometimes need to test an app launching it from a known state (for example, logged in). To make a snapshot of the app’s data, I use the great tool rsync to copy the whole hierarchy somewhere else, which is super easy now (NB: make sure you type in the trailing slashes in the following commands):

1
$ rsync -a --delete ../links/myapp/ ~/Desktop/save/

And to restore, I sync a saved snapshot back:

1
$ rsync -a --delete ~/Desktop/save/ ../links/myapp/

Comments