2.4 Bundles and Resource Management

A bundle is an abstraction that represents a collection of resources, such as image files, nib files, or loadable code, stored within a folder. Bundles are used pervasively in Mac OS Xapplications are themselves bundles, as are preference pane and screensaver modules and application plug-ins. Although bundles are directories, the Finder often presents them to the user as a single file. For a more in-depth discussion of bundles and their usage in Mac OS X, see Inside Mac OS X: System Overview (/Developer/Documentation/Essentials/SystemOverview/SystemOverview.pdf ).

NSBundle provides an interface to bundles in the filesystem. Every Cocoa application has at least one bundlethe main bundle, accessed using the mainBundle methodthat represents the application. To load other bundles, use the methods initWithPath: or bundleWithPath:. To access a bundle containing a given class, use bundleForClass:.

2.4.1 Loading Resources

Using NSBundle, you can obtain the paths to resources without knowledge of a bundle's internal directory structure or what localization is used. Methods that find a resource come in two flavors: those that retrieve individual resources, whose names are on the base method name pathForResource:ofType:, and those that return all resources of a type, which are based on the method name pathsForResourceOfType:. The paths returned by these methods are absolute paths in the filesystem. For example, consider the method pathForResource:ofType:. Given the name of the resource (resource file name sans extension) and, optionally, the type (the extension may pass nil or @"" here), this method will return the full path to the specified resource in the main resources directory, which is at BundleName/Contents/Resources. If the resource is not found there, then any .lproj folders are searched in order according to the user's Language setting in Preferences.

If you want to specify a directory to search in for the resource (as it may not be contained in the Resources directory), use the method pathForResource:ofType:inDirectory:. If the resource is not present in the specified directory, the method returns nil. If nil is passed for the parameter inDirectory:, a search is performed through a prioritized list of directories. See the NSBundle documentation for the search order.

In addition to containing resources, bundles may contain other bundles in the form of plug-ins, frameworks, and other applications. Plug-ins and frameworks are generally located in the /BundleName/Contents/PlugIns and /BundleName/Contents/Frameworks directories, respectively. You can discover these paths by using the builtInPlugInsPath, privateFrameworksPath and sharedFrameworksPath methods.

Example 2-29 shows how to use these methods to access resources contained within a bundle.

Example 2-29. Accessing bundle resources
NSString *imagePath;
NSImage *anImage;
NSBundle *bundle = [NSBundle mainBundle];

// Locate and load a resource
if ( imagePath = [bundle pathForResource:@"mug_shot"
        ofType:@"tiff"] ) {
    anImage = [[NSImage alloc] initWithContentsOfFile:imagePath];
    // Do something with anImage
}

// The path to the frameworks directory
NSString *fPath = [bundle sharedFrameworksPath];
fPath = [bundle privateFrameworksPath];

// Path of the bundle itself
NSString *bPath = [bundle bundlePath];

// Path to bundle executable
NSString *ePath = [bundle executablePath];

// Obtain the Info.plist dictionary for the bundle
NSDictioanry *iDict = [bundle infoDictionary];

2.4.2 Loading Code

Bundles also load new classes into the runtime system. Several methods that do this are provided: load, principalClass, and classNamed:. The load method loads a bundle's executable code into the runtime, if it has not been loaded previously. The method will return YES if loading is successful or if the executable has been loaded already, and NO otherwise.

The principalClass and classNamed: methods not only load a bundle's executable code, but they return a class object for the specified class. In classNamed:, the returned class object is specified by name in an NSString. If no class by the specified name can be located in the bundle, Nil will be returned (Nil is the equivalent of nil for class objects).

The principalClass method returns a class object that is determined by the bundle itself. The bundle's principal class is the class that is generally responsible for the other classes within a bundle. For example, the principal class of most standard Cocoa applications is NSApplication. A bundle's Info.plist file contains an entry that identifies the principal class with the key NSPrincipalClass. Bundle developers can specify a class name here for the principal class. If this entry is not present, principalClass returns the class object for the first class loaded in the bundle. Developers can specify the load order of classes by arranging class files in a project in the desired order. Like classNamed:, this method returns Nil if there is an error loading the code or if no executable is found within the bundle.

Example 2-30 shows how to load executable code with NSBundle.

Example 2-30. Loading code using NSBundle
Class exampleClass;
id newObject;

// The main bundle
NSBundle *bundle = [NSBundle mainBundle];

// Obtain and instantiate the principal class
if ( exampleClass = [bundle principalClass] ) {
    newObject = [[exampleClass alloc] init];
    // Do something with newObject
}

// Obtain and instantiate a class by name
if ( exampleClass = [bundle classNamed:@"MyClass"] ) {
    newObject = [[exampleClass alloc] init];
    // Do something with newObject
}


    Part II: API Quick Reference