How to Use Files to Save Data for iOS apps

By Rajiv Ramnath

In iOS apps, files are used as the means for storing and restoring the state of an active game. If you’ve done any programming in any language (C, C++, Java, C#, you name it), you’ve used files to read data from and write data to. Objective-C allows you to work with files as wellFiles in iOS are organized into directories.

Each app gets its own directory where the app resides and its data can be stored. This directory is known as the app’s sandbox.

Diagram of an app's directory, also know as sandbox.

An app is prohibited from accessing or creating files outside its sandbox. When an app needs to work with the user’s contacts or music, the system needs to (and does) allow the file-related operations needed to read from or modify these data stores, which are outside the app’s sandbox.

This sandbox directory is organized in a set of subdirectories. Apple specifies how each of these subdirectories should be used, as follows:

  • The <app name>.app directory is where the app’s executable and all the files in its bundle reside (such as the X and O images for Tic-Tac-Toe).

  • The Documents directory holds data that your app can’t re-create, such as user-generated documents or content. This is the directory where the Tic-Tac-Toe app saves games, keeps its SQLite files, and so on. iTunes backs up this directory, so when you connect your device to your Mac, the data here will be saved.

  • The Inbox directory within the Documents directory has special meaning. This directory stores files that other apps ask your app to open. For example, the Mail program places e-mail attachments associated with your app in this directory. Your app can read and delete files in this directory but can’t create new files or write to existing files.

  • The Library directory is used for files that aren’t user data files but need to be backed up. With the exception of a subdirectory named Caches (which is specifically for data the app temporarily wants to save for faster access), the files here are backed up by iTunes.

  • The tmp subdirectory saves temporary data that doesn’t need to persist between app runs. Your app should remove files from this directory when they’re no longer needed. The system may also purge lingering files from this directory when your app isn’t running or disk space is low. The contents of this directory are not backed up.

Now, look at a quick example in Tic-Tac-Toe that shows how to write to and read from files. In this example, use a file to save and restore the state of a game.

When a user touches Save Game, the current state of the game is saved to a file named SavedGames.data in the Documents directory in the app’s sandbox (the path to this file was set using #define as the constant TTTGAMESESSIONSAVEDFILEPATH in the file TTTGameSessionViewController.h).

Preview of all the files in an app.

If the user exits without finishing the game, she can return to the game session screen and return to the saved game by selecting Restore Game.

Now for some code. The following code shows saveGame:

 - (IBAction) saveGame:(id)sender {
  NSDictionary* savedGameDictionary = [activeGame toDictionary];
  NSError *error;
  NSData *jsonData =
   [NSJSONSerialization dataWithJSONObject:savedGameDictionary             
         options:NSJSONWritingPrettyPrinted
         error:&error];
  NSString *savedGameString =
   [[NSString alloc] initWithData:jsonData
        encoding:NSUTF8StringEncoding];
  NSString *savedGamesFilePath =
   [NSHomeDirectory()
    stringByAppendingPathComponent:@TTTGAMESESSIONSAVEDFILEPATH];
  [savedGameString writeToFile:savedGamesFilePath
          atomically:YES
          encoding:NSUTF8StringEncoding
          error:NULL];
 }

Writing to a file is easy. You just build the pathname of the file using the method stringByAppendingPathComponent on an object representing the app’s home directory (which you get by calling the function NSHomeDirectory). Then [savedGameString writeToFile:savedGamesFilePath …] does the actual writing.

Reading from a file is straightforward, too. Here’s the code for restoreGame:

 - (IBAction) restoreGame:(id)sender {
  NSString *savedGamesFilePath =
   [NSHomeDirectory()
    stringByAppendingPathComponent:@TTTGAMESESSIONSAVEDFILEPATH];
  NSString *savedGameString =
   [NSString stringWithContentsOfFile:savedGamesFilePath
      encoding:NSUTF8StringEncoding
      error:NULL];
  NSError *restoreError = nil;
  NSMutableDictionary *savedDictionary =
   [NSJSONSerialization JSONObjectWithData:[savedGameString
         dataUsingEncoding:NSUTF8StringEncoding]
         options:NSJSONReadingMutableContainers
         error:&restoreError ];
  activeGame = [[TTTGame alloc] initFromDictionary:savedDictionary];
  TTTGameGrid *gameGrid = [activeGame getGameGrid];
  if (![activeGame isActive])[boardView disableInput];
  [boardView setGrid:gameGrid];
  [gameView redraw];
 }

You’ve already seen how a path to the file is created using the NSHomeDirectory function to get the home directory object, and then using this object’s method stringByAppendingPathComponent to create the string. Next, you read the entire file into a string using [NSString stringWithContentsOfFile:savedGamesFilePath …], and then you’re free to process the string as needed.