Reading Local JSON Files on iOS

Posted by Grego on July 1, 2015

I was working recently on one of my iOS apps that depends heavily on web services and realized that I never handled the offline scenario for when the user has no internet connectivity. The fix for this was to supply some of the information for my app inside the app bundle in a JSON file in the same format that I would retrieve it from the actual web service.

Here are some quick notes about reading files on iOS. Detailed explanation follows code.

tl;dr - Quick code reference

  //get file name
  NSString *fileName = [[NSBundle mainBundle] pathForResource:@"party"
         ofType:@"json"
    inDirectory:@"data"];

  //check file exists
  if (fileName) {
    //retrieve file content
    NSData *partyData = [[NSData alloc] initWithContentsOfFile:fileName];

    //convert JSON NSData to a usable NSDictionary
    NSError *error;
    NSDictionary *party = [NSJSONSerialization JSONObjectWithData:partyData
      options:0
        error:&error];

    if (error) {
      NSLog(@"Something went wrong! %@", error.localizedDescription);
    }
    else {
      NSLog(@"party info: %@", party);
    }
  }
  else {
    NSLog(@"Couldn't find file!");
  }

Step 1 - Add the file to your project

In this example, I’ll be adding a file called party.json. My code structure for the project looks like this:

.
|-- LoadFile
|   |-- AppDelegate.h
|   |-- AppDelegate.m
|   |-- Base.lproj
|   |-- Images.xcassets
|   |-- Info.plist
|   |-- ViewController.h
|   |-- ViewController.m
|   |-- data
|   |   `-- party.json
|   `-- main.m
|-- LoadFile.xcodeproj
`-- LoadFileTests
    |-- Info.plist
    `-- LoadFileTests.m

As you can see the party.json file is located in a data folder. Since I want to preserve that, I’ll add the whole data folder to my project.

  1. In the Project Navigator, right click and select Add files to “ProjectName”…
    add files to project

  2. Select the file or folder you want to add. If you are adding a folder, select the option to Create folder references this will maintain the relative directory structure on the device by copying the folder as well.
    create folder reference
    Notice how the folder has a blue icon. Blue folder icons represent file or folder references. A regular yellow colored folder represents a group in xcode. Groups are not real folders on the file system, they are just something to use to organize your code in Xcode’s Project Navigator.

Step 2 - Locate the file

Now that we’ve got the file being copied to the app and in the proper folder, we need a way to locate that file location. Would it have made sense for apple to allow us to use a relative path like "data/party.json" to get the file? Yes, it would have. Did they allow us to do something like that? No, no, they didn’t.

Introducing [NSBundle pathForResource:ofType:inDirectory:] using this neat little doo-hicky we can pull out the file name. According the documentation, it returns An array of full pathnames for the subpath or nil if no files are located.

So let’s grab our file name and throw it into a string:

  NSString *fileName = [[NSBundle mainBundle] pathForResource:@"party"
         ofType:@"json"
    inDirectory:@"data"];

Docs say we will get nil if it can’t find the file, so now we check for nil:

  if (fileName) {
      // ...
  }
  else {
    NSLog(@"Couldn't find file!");
  }

Step 3 - Pull file content

To pull file content we’ll use the [NSData initWithContentsOfFile:] initializer:

if (fileName) {
  NSData *partyData = [[NSData alloc] initWithContentsOfFile:fileName];
}

If your file is just a string there’s a different method you can use to pull the string from the file directly:
[NSString stringWithContentsOfFile:encoding:error:] (more details)

Step 4 - Converting JSON NSData into an NSDictionary

Now we’ll convert the NSData object into an NSDictionary, since I’m pulling JSON data:

  NSError *error;
  NSDictionary *party = [NSJSONSerialization JSONObjectWithData:partyData
    options:0
      error:&error];

  if (error) {
    NSLog(@"Something went wrong! %@", error.localizedDescription);
  }
  else {
    NSLog(@"party info: %@", party);
  }

Finishing up

That’s it! Fairly simple.