When an app is installed iOS creates a sandbox of several pre made containers which only that app can access. It's composition looks like the following.
Bundle Container
This container includes the application itself, specifically a directory that includes the executable code and the resources like images and sounds files or whatever else the app might use.
Data Container
This container holds all the user in app data. Within it are three main subdirectories - Documents
, Library
, and Temp
.
Temp - used to store very short lived data that does not need to be persisted across launches.
Documents - (important stuff) this is where user data should go. iTunes will makes copies of this directory and iOS will never delete any of it's contents.
Library - this is where any non-user data, or files that you don't want exposed to the user is stored. This is also the place where
NSUserData
is lives. It's path looks something like thisLibrary/Preferences/info.myapp.mobile.plist
- The
Caches
subdirectory lives within theLibrary
directory. This is where things that won't be necessary in the future or are easy to recreate live. iTunes and iCloud will never make a backup its content. - The
Tmp
subdirectory also lives in the sandbox and is very similar toCaches
directory. The main difference is when they are deleted.Tmp
will be deleted more often under normal circumstances, but for the most part we can consider them equivalent.
- The
Both Documents
and Library
both contain a few standard subdirectories, but we also have the ability to create custom subdirectories to better organize our files.
At run time the app might request access to additional containers such as the iCloud
Container.
Saving Content
In order to save something to the sandbox we need to do two things.
- Find where the folder is within the sandbox
- Write to the folder
To achieve that we can use a couple different tools
FileManager
- to get the path to the folder within the sandboxString
- to write or read text filesData
- to write or read binary files
Example Writing & Reading From The Sandbox
Within the following example we create an instance of FileManager
. This gives us the functionality to find folders, copy files, remove files etc. We'll use it to get a path to our Documents
folder within our sandbox.
FileManager
will return an array of urls, which makes sense because there are actually more than one Documents
folder - one for each app installed. This class method in Mark 1 works by returning all of them, which we will filter by adding the second parameter to fm.urls(for: in:)
in Mark 2
We now have an array with just one url which we'll pop off in Mark 3. This is a url to the Documents
folder but we want a url to the new file we are going to create so we append the file name at the end of the url.
At this point we're ready to save some data to the file. We could use String
or Data
, both work in similar ways but we'll use String
to write to our file starting with the do
block of code on Mark 4. Then we read back the data we just saved staring on Mark 5 just to make sure all went as planned.
Below is the base concept - not a very practical way to save or retrieve any significant amount of data containing several objects, versions of the data, or displaying it in views. But there are frameworks like Core Data
that build on top of this concept to handle more complex interactions.
func sandboxPlayground() {
let fm = FileManager.default // Mark 1
let urls = fm.urls(for: .documentDirectory, in: .userDomainMask) // Mark 2
let url = urls.last?.appendingPathComponent("file.txt") // Mark 3
do { // Mark 4
try "Hi There!".write(to: url!, atomically: true, encoding: String.Encoding.utf8)
} catch {
print("Error while writing")
}
do { // Mark 5
let content = try String(contentsOf: url!, encoding: String.Encoding.utf8)
if content == "Hi There!" {
print("yay")
} else {
print("oops")
}
} catch {
print("Something went wrong")
}
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
sandboxPlayground()
return true
}