We now have a public interface available to interact with the ImageApp plug-in system. With that in place, the next step is to learn how to package
IAGrayscaleFilter into a bundle that can be loaded by ImageApp at runtime. Rather than creating a new project for our plug-in, which would be what a third-party ImageApp plug-in developer would have to do, we will create a new target in our existing project.
A target in Project Builder is a set of rules that tells Project Builder how to build an application, a framework, a bundle, and more. When you create a project, the default target created for you is a target to build whatever you choose the project to be (i.e. Cocoa application, Cocoa bundle, Foundation tool, etc). We will add a target to the ImageApp project to build our filter plug-in.
From the Project menu select New Target, which will bring up a dialog asking you to select the type of target. Select Bundle from the top of the list, and name the target Grayscale Filter.
We now have two targets: one for the ImageApp application, and one for the Grayscale Filter plug-in bundle. If you click on the target tab, you will see a list of the targets that are a part of your project.
Selecting a target will bring up a view with an outline that lets you set various options about included files, the build process, and other settings. It would be worth your while to take some time to explore the different settings available. We will see quite a few today.
If you look again at the Targets list, you will notice that each target has a disclosure triangle to the left of the name. What these disclosure triangles do is to reveal target dependencies. If target A needs needs to use the product of target B in its build process, then target A is said to be dependent on target B. We will make ImageApp dependent on the Grayscale Filter plug-in. In practice, what this does for us is automatically build Grayscale Filter when we build ImageApp. To create a dependency, all you have to do is drag the Grayscale Filter target name underneath the ImageApp (Itís really ImageAppTest) target name.
The next thing we must do is add some files to our bundle. In Project Builder's toolbar you should see a pop-up button that contains a list of all of the targets in our project. Selecting a target from that list will make it the active target, which is the one that gets built or debugged when we initiate those actions. You can also select the active target by clicking on the radio buttons to the left of the target names in the targets list.
When switching between targets in this menu, you should notice that the little checkboxes to the left of each file in the Groups & Files list change depending on which target is selected. At this point they should all be selected when ImageApp is the active target, and they should all be disabled when Grayscale Filter is the active target. What these checkboxes indicate is what targets use what files. We need to change things so that the class files for
IAGrayscaleFilter are used by the Grayscale Filter bundle target and not the ImageApp target.
To do this, select the ImageApp target, and uncheck the checkboxes for IAGrayscaleFilter.h and IAGrayscaleFilter.m. This removes the
IAGrayscaleFilter class from ImageApp's build process. Now, select the Grayscale Filter target, and check the checkboxes for these two same files. Since
IAGrayscaleFilter will soon be modified to inherit from
IAFilter, we need to add IAFilter.h and IAFilter.m to this same target as well (It is perfectly acceptable for a file to be used by multiple targets in the same project).
Another way you can change which files for a target to use is in the target settings tab in the Build Phases section. Here you will see sections for Headers, Sources, and more. If you click on one of the sections of Build Phases, you will see a list of all files that fall in that category. To add files to a phase, you can drag any of those files from the Groups & Files list. To delete, simply select the file and hit the delete key.
Now that we have a handle on how Project Builder handles multiple targets, let's fine-tune our Grayscale Filter bundle settings. We will make changes to the following areas:
- Principal Class
- Installation Location
Every bundle in Mac OS X, whether it be an application or a framework, contains a file called
Info.plist. This file contains information about the bundle needed by the operating system and other applications that may use that bundle. The plug-in loader that we build for ImageApp will require the existence of two entries in this file: the principal class and a string that will be used as the filter plug-ins menu item title.
Like all property lists, Info.plist's entries are made up of key-value pairs. The key for the bundle's principal class already exists. We just have to give it a value. The key for the filter menu item title, however, is something that we must define ourselves.
To set the principal class of the bundle, select from the Targets tab Grayscale Filter and navigate to
Info.plist Entries > Simple View > Cocoa-Specific, and you will see the
Principal class field. Enter IAGrayscaleFilter here. We will see in a moment how ImageApp can ask the bundle to return the class object with this class name, which we will use to create an instance of IAGrayscaleFilter. You will also notice another field in this view, Main nib file. If you were building a plug-in with an interface built in Interface Builder, this is where you would specify the nib file containing that interface. An application can then use methods of
NSBundle to load this nib. We won't be doing that here.
To define a custom key, click on Info.plist Entries > Expert View. Here you will see a table of all the keys and values that will be built into the bundle's Info.plist file. At the bottom of the list you should see the key
NSPrincipalClass with the class name we just entered as the value. To add a new key, click on the New Sibling button at the top of the view. For the key name enter
IAFilterMenuItemTitle, and for the value enter Grayscale. This is the string that will appear in the filter menu of ImageApp at runtime.
That's all we have to do as far as
Info.plist is concerned. Now we have to change some settings related to how the bundle is built, and to build phases of ImageApp so the plug-in is copied to the ImageApp bundle.
By default bundles take on the extension
.bundle. We want to change this to
.plug-in to indicate that the bundle has a more specific purpose as that of a bit of code that extends the functionality of an app. If you click on Settings > Expert View, you will see a table similar to the one we were just dealing with. At the bottom of this list is the
WRAPPER_EXTENSION setting. We want to change the value of
WRAPPER_EXTENSION from bundle to plug-in. Do that now by double-clicking on bundle to select the existing text.
Don't worry if you don't see this change updated in the Groups & Files list where Grayscale Filter is listed under Products. It may still be listed as Grayscale Filter.bundle. The change won't be reflected in the list unless you edit the bundle name or build the bundle.
The next thing we have to do is specify the bundle's installation location. This is found under Settings > Simple View > Installation Settings. Select the Path radio button, and enter the path @executable_path/../Plug-ins.
One last thing we have to do that will prove most helpful is to provide the Cocoa frameworks for the Grayscale Filter bundle to link against. To do this, drag the item from the Groups & Files list Frameworks/Linked Frameworks/Cocoa.framework, and drop it on the Frameworks and Libraries build phase of Grayscale Filter.
Now, that's about all we have to do for the Grayscale Filter bundle. The next thing on the docket is to fix up ImageApp to incorporate this bundle into its build process.