Copyright ©1995 by NeXT Computer, Inc.  All Rights Reserved.

NXBundle



Inherits From: Object
Declared In: objc/NXBundle.h



Class Description

An NXBundle is an object that corresponds to a directory where program resources are stored.  The directory, in essence, "bundles" a set of resources used by an application, and the NXBundle object makes those resources available to the application.  It's able to find requested resources in the directory and, in some cases, dynamically load executable code.  The term "bundle" is used both for the object and for the directory it represents.

Bundled resources might include such things as:

Images
Sounds
Character strings
Nib files--files with a ".nib" extension--archived by Interface Builder
Executable code

Each resource resides in a separate file.



Localized Resources

If an application is to be used in more than one part of the world, its resources may need to be customized--localized--for language, country, or cultural region.  It may need, for example, to have separate Japanese, English, French, Hindi, and Swedish versions of the character strings that label menu commands.  Its nib files might similarly need to be localized, as well as any images or sounds it uses.

The resource files specific to a particular language are grouped together in a subdirectory of the bundle directory.  The subdirectory has the name of the language (in English) followed by a ".lproj" extension (for "language project").  The application mentioned above, for example, would have Japanese.lproj, English.lproj, French.lproj, Hindi.lproj, and Swedish.lproj subdirectories.

Each ".lproj" subdirectory in a bundle has the same set of files; all versions of a resource file must have the same name.  Thus, myIcon.tiff in French.lproj should be the French counterpart to the Swedish myIcon.tiff in Swedish.lproj, and so on.

If two or more languages share the same localized version of a file, the file can be stored in just one of the ".lproj" subdirectories, while the other subdirectories keep (hard or soft) links to it.  If a resource doesn't need to be localized at all, it's stored in the bundle directory itself, not in the ".lproj" subdirectores.

The user determines which set of localized resources will actually be used by the application.  NXBundle objects rely on the language preferences set by the user in the Preferences application.  Preferences lets users order a list of available languages so that the most preferred language is first, the second most preferred language is second, and so on.

When an NXBundle is asked for a resource file, it provides the path to the resource that best matches the user's language preferences.  In the following code, for example, the application sends a getPath:forResource:ofType: message to ask for the path to the myIcon.tiff file.  With the path in hand, it can use other facilities (here NXImage's initFromFile: method) to access the resource.

char        buf[MAXPATHLEN + 1];
NXBundle   *bundle;
NXImage    *image;

bundle = [NXBundle bundleForClass:[self class]];
if ( [bundle getPath:buf forResource:"myIcon" ofType:"tiff"] ) {
image = [[NXImage alloc] initFromFile:buf];
. . .
}


The Main Bundle

Every application is considered to have at least one bundle--its main bundle, the directory where its executable file is located. If the application is organized into a file package marked by a ".app" extension, the file package is the main bundle.

Note:  A file package is a directory that the Workspace Manager presents to users as if it were a simple file; the contents of the directory are hidden.  A file package for an application includes the application executable plus other files required by the application as it runs.  It bears the same name as the executable file but adds a ".app" extension that identifies it to the Workspace Manager.  For example, if you develop a Rutabaga application and place it in a Rutabaga.app directory with various ".nib" and TIFF files that the application will use, the Rutabaga.app directory is its file package and its main bundle.



Other Bundles

An application can be organized into any number of other bundles in addition to the main bundle.  These other bundles usually reside inside the file package, but they can be located anywhere in the file system.  Each bundle directory is represented in the application by a separate NXBundle object.

By convention, bundle directories other than the main bundle end in a ".bundle" extension, which instructs the Workspace Manager to hide the contents of the directory just as it hides the contents of a file package.  The extension isn't required, but it's a good idea, especially if the bundle isn't already hidden by virtue of being inside a file package.



Dynamically Loadable Classes

Any bundle directory can contain a file with executable code.  For the main bundle, that file is the application executable that's loaded into memory when the application is launched.  The executable in the main bundle includes the main() function and other code necessary to start up the application.

Executable files in other bundle directories hold class (and category) definitions that the NXBundle object can dynamically load while the application runs.  When asked, the NXBundle returns class objects for the classes (and categories) stored in the file. It waits to load the file until those classes are needed.

In the example below, the first line of code creates an instance of a class provided by an NXBundle object.  If the class had not already been loaded into memory, asking for the class would cause it to be loaded.

id foo = [[[myBundle classNamed:"Reporter"] alloc] init];
if ( foo ) {
[foo doSomething];
. . .
}

By using a number of separate bundles in this way, you can split an application into smaller, more manageable pieces.  Each piece is loaded into memory only when the code being executed requires it, so the application can start up faster than it otherwise would.  And, assuming that only the rare user will exercise every part of the application, it will also consume less memory as it runs.

The file that contains dynamically loadable code must have the same name as the bundle directory, but without the ".bundle" extension.

Since each bundle can have only one executable file, that file should be kept free of localizable content.  Anything that needs to be localized should be segregated into separate resource files and stored in ".lproj" subdirectories.



Instance Variables

None declared in this class.



Method Types

Initializing a new NXBundle object
initForDirectory:
Getting and freeing an NXBundle
+ mainBundle
+ bundleForClass:
free
Getting a bundled class principalClass
classNamed:
Setting which resources to use + setSystemLanguages:
Finding a resource getPath:forResource:ofType:
+ getPath:forResource:ofType:inDirectory:withVersion:
Getting the bundle directory directory
Setting the version setVersion:
version



Class Methods

bundleForClass:
+ bundleForClass:classObject

Returns the NXBundle object that dynamically loaded classObject, or the main bundle object if classObject was not dynamically loaded.

See also:  + mainBundle



getPath:forResource:ofType:inDirectory:withVersion:
+ (BOOL)getPath:(char *)path
forResource:(const char *)filename
ofType:(const char *)extension
inDirectory:(const char *)directory
withVersion:(int)version

Returns YES if the specified resource file is available within the directory, and NO if it's not.  If path is not NULL, a full pathname to the file is copied into the buffer it points to.  To accommodate all possible pathnames, the path buffer should be at least MAXPATHLEN + 1 characters long.  MAXPATHLEN is defined in the sys/param.h header file.

This method works just like the getPath:forResource:ofType: instance method, except that it searches for the resource in directory (rather than in the directory associated with the instance) and it tests against version (rather than the version last set by setVersion:).  Therefore, if you only occasionally search for a resource in directory and don't need to dynamically load code from it, you can use this method instead of getPath:forResource:ofType: and avoid creating an NXBundle instance.

See also:  getPath:forResource:ofType:, setVersion:, + setSystemLanguages:



mainBundle
+ mainBundle

Returns the NXBundle object that corresponds to the directory where the application executable (the file that's loaded into memory to start up the application) is located.  This method allocates and initializes the NXBundle object, if it doesn't already exist.

In general, the main bundle corresponds to an application file package, a directory that bears the name of the application and is marked by a ".app" extension.

See also:  + bundleForClass:



setSystemLanguages:
+ setSystemLanguages:(const char * const *)languageArray

Informs the NXBundle class of the user's language preferences, and returns self.  The argument, languageArray, is a pointer to an ordered list of null-terminated character strings.  Each string is the name of a language.

Language names used for ".lproj" subdirectories should match those set by this method.  By convention, the names are in English.  These are among the names currently in use:

English
French
German
Japanese
Spanish
Swedish

This method responds to a message sent by the Application Kit when the application first starts up; it's not necessary for your application to set the system languages.



Instance Methods

classNamed:
classNamed:(const char *)classname

Returns the class object for the classname class, or nil if classname isn't one of the classes associated with the receiving NXBundle.

Before returning, this method ensures that any code in the bundle directory has been loaded into memory, so the classname class will be part of the executable image, if it's available to the NXBundle object.

See also:  principalClass



directory
(const char *)directory

Returns a pointer to the full pathname of the receiver's bundle directory.

See also:  initForDirectory:



free
free

Frees the receiving NXBundle, and returns nil.  However, the main bundle can't be freed, and neither can any NXBundle with dynamically loaded code.  If it can't free the object, this method returns self.



getPath:forResource:ofType:
(BOOL)getPath:(char *)path
forResource:(const char *)filename
ofType:(const char *)extension

Returns YES if the specified resource file is available within the bundle, and NO if it's not.  If path is not NULL, a full pathname to the file is copied into the buffer it points to.  To accommodate all possible pathnames, the path buffer should be at least MAXPATHLEN + 1 characters long.  MAXPATHLEN is defined in the sys/param.h header file.

To find the resource file, this method first looks inside the bundle directory for ".lproj" subdirectories that match the user's language preferences (as specified in the Preferences application).  It searches for subdirectories in the order of user preference.

When it finds a ".lproj" subdirectory for a preferred language, the NXBundle first makes sure that the subdirectory version (as specified in a version file) matches the version last set by the setVersion: method.  If the versions don't match or if the subdirectory doesn't contain the requested resource file, the NXBundle continues the search by looking for the ".lproj" subdirectory for the next most preferred language.

The search stops, and this method returns, as soon as the resource file is found.  If the file can't be found in any ".lproj" subdirectory, the NXBundle looks for a nonlocalized version of it in the bundle directory.

If the extension doesn't repeat an extension already specified in the filename, it's added to filename before the search begins. The extension can be NULL, but filename can't be.

See also:  + getPath:forResource:ofType:inDirectory:withVersion:, + setSystemLanguages:, setVersion:



initForDirectory:
initForDirectory:(const char *)fullPath

Initializes a newly allocated NXBundle object to make it the NXBundle for the fullPath directory.  fullPath must be a full pathname for a directory.

If the directory doesn't exist or the user doesn't have access to it, the NXBundle is freed and this method returns nil.  If the application already has an NXBundle object for the fullPath directory, this method frees the receiver and returns the existing object.

It's not necessary to allocate and initialize an object for the main bundle; the mainBundle method provides it.

See also:  + mainBundle



principalClass
principalClass

Returns the class object for a class that's dynamically loaded by the NXBundle, or nil if the NXBundle can't dynamically load any classes.  Classes can be loaded from just one file within the bundle directory, a file that has the same name as the directory (but without the ".bundle" extension).  If that file contains a single class, this method returns it.  If the file contains more than one loadable class, this method returns the first one it encounters--that is, the first one listed on the ld command line that created the file.  In the following example, Reporter would be the principal class:

ld -o myBundle -r Reporter.o NotePad.o QueryList.o

In general, the principal class should be the one that controls all the other classes that are dynamically loaded with it.

Before returning, this method ensures that any loadable code in the bundle directory has in fact been loaded into memory.  If the NXBundle can load any classes at all, the principal class will be part of the executable image.

If the receiver is the main bundle object, this method returns nil.  The main bundle doesn't have a principal class.

See also:  classNamed:



setVersion:
setVersion:(int)version

Sets the version that the NXBundle will use when searching ".lproj" subdirectories for resource files, and returns self.  The default version is 0.

See also:  getPath:forResource:ofType:, version



version
(int)version

Returns the version last set by the setVersion: method, or 0 if no version has been set.

See also:  setVersion: