1.27. Loading Data from the Main Bundle

Problem

You have added a resource (such as an image) to your Xcode project and now, at runtime, you would like to access that resource.

Solution

Use the mainBundle class method of the NSBundle class in order to retrieve your main bundle. Once that is done, use the pathForResource:ofType: method of your main bundle to retrieve the path for that specific resource. Once the path is detected, depending on the type of resource, you can either pass the path to your file to a class such as UIImage or NSData, or you can manually access the file using NSFileManager.

Note

It is required that you give a unique name to each resource inside your main bundle. For instance, it is not good practice to have a file named Default.png in more than one place inside your main bundle. Different ways of loading a resource from a bundle could then yield different results. As a result, make sure you give unique names to your files inside any bundle, regardless of whether it is the main bundle or a custom bundle that you’ve created (see Recipe 1.26).

Discussion

To access the main bundle, we can use the mainBundle class method of the NSBundle class. Bundles are all of type NSBundle and once you have an instance of a bundle, you can load resources from that bundle.

Note

Every app’s main bundle has a flat hierarchy on disk when it is compiled for submission to App Store. That means all the files that get wrapped up in your app bundle will be placed on the root folder of the main bundle. In other words, the main bundle has only one folder, the root folder, and all files and resources are stored in that folder. Even if you have a folder on disk with a few files in it and drag and drop it into Xcode, only the files in that folder will be placed in the main bundle’s file hierarchy, not the folder itself.

For instance, let’s say that you have an image called AlanSugar.png sitting on your desktop. Simply drag and drop it into Xcode. At this point, Xcode will display a dialog to you, asking you which project this file has to be added to and whether you want this file to be copied over to the project’s folder, if need be. This dialog will look similar to that shown in Figure 1-34.

Xcode asking which project a file has to be added to

Figure 1-34. Xcode asking which project a file has to be added to

In this dialog, make sure that the “Copy items into destination group’s folder (if needed)” item is selected. This will copy the file that you drop into Xcode to the target app’s folder. Now, if you delete the file on your desktop, it won’t get deleted from your project because your project has its own copy. It’s generally good practice to do this unless, for specific reasons, you decide not to (and I’ve experienced many of these reasons myself). After you drag and drop the file, the file AlanSugar.png is in the project’s main bundle and you can retrieve its path in this way:

- (BOOL)            application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

  NSString *alanSugarFilePath =
  [[NSBundle mainBundle] pathForResource:@"AlanSugar"
                                  ofType:@"png"];

  if ([alanSugarFilePath length] > 0){
    UIImage *image = [UIImage imageWithContentsOfFile:alanSugarFilePath];
    if (image != nil){
      NSLog(@"Successfully loaded the file as an image.");
    } else {
      NSLog(@"Failed to load the file as an image.");
    }

  } else {
    NSLog(@"Could not find this file in the main bundle.");
  }

  self.window = [[UIWindow alloc] initWithFrame:
                 [[UIScreen mainScreen] bounds]];

  self.window.backgroundColor = [UIColor whiteColor];
  [self.window makeKeyAndVisible];
  return YES;
}

The output of the pathForResource:ofType: method of NSBundle will be either a valid path or nil if the specified resource cannot be found in the target bundle. So after you call this method, it is best to check whether the path could actually be retrieved. If so, the code shown passes the path of the file to the UIImage class in order to load the AlanSugar.png file into memory as an image.

Similarly, if you wanted to load the data of that file into memory, instead of retrieving this image as an image object, you could use the NSData class:

- (BOOL)          application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

  NSString *alanSugarFilePath =
  [[NSBundle mainBundle] pathForResource:@"AlanSugar"
                                  ofType:@"png"];

  if ([alanSugarFilePath length] > 0){

    NSError *readError = nil;

    NSData *dataForFile =
    [[NSData alloc] initWithContentsOfFile:alanSugarFilePath
                                   options:NSMappedRead
                                     error:&readError];

    if (readError == nil &&
        dataForFile != nil){
      NSLog(@"Successfully loaded the data.");
    } else if (readError == nil &&
               dataForFile == nil){
      NSLog(@"No data could be loaded.");
    } else {
      NSLog(@"An error occured while loading data. Error = %@", readError);
    }

  } else {
    NSLog(@"Could not find this file in the main bundle.");
  }

  self.window = [[UIWindow alloc] initWithFrame:
                 [[UIScreen mainScreen] bounds]];

  self.window.backgroundColor = [UIColor whiteColor];
  [self.window makeKeyAndVisible];
  return YES;
}

See Also

Recipe 1.26

Get iOS 6 Programming Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.