6. Frameworks

“You don't know what I know!”

—said the Framework to the Application

Frameworks provide a convenient way to distribute a library along with headers, documentation, and other resources. Frameworks are most analogous to libraries in C. If you're a C programmer, you have undoubtedly created or used a library. You might not have known it, but you have.

You might be familiar with libraries that come with a .lib file (the library itself) and a separate .h file (the header, or interface, file). The library file contains all the compiled source code to perform certain tasks, whereas the header file defines the interface to that code. By adding the library to your project and including the header file in your source files, you can access the functionality of the library without ever seeing its source code. Frameworks are very similar, but are much more advanced in structure and form.

Note

I know it's early in the chapter, but this is just a quick note to say that although libraries ending in .lib are popular, there are plenty of others. Dynamic libraries end in .dylib and static libraries end in .a.

Similar to libraries, frameworks allow you to easily hide the implementation of classes or functions while providing an interface for others to access the hidden functionality. For example, you might have a highly secure encryption algorithm that someone else in your company needs to use, but you don't want the source code getting out. You can easily compile this function in a framework, release it with an interface file, and not have to worry. You can provide updates as needed and not be concerned about someone having old copies of your source code lying around. Frameworks can automatically handle versioning so that the latest version executes even if multiple versions are installed.

In addition, frameworks are bundles. Therefore, they can contain compiled code, plists, resources, nib files, documentation, and so on. You can place just about anything you like (including other frameworks) in a framework's bundle. Framework bundle names end in .framework. Figure 6.1 shows what a framework looks like in the Finder.

Figure 6.1. Apple's AudioUnit framework.

image

Note that Apple's AudioUnit framework looks just like a folder in the Finder. You can open it up and browse the contents the same as you would any folder you create. The framework stores specific versions of its content in a Versions directory; you can use the Current alias (actually a symbolic link) to find the framework's current version. The Headers directory contains the public interface to the framework. To use this framework, you would simply add it to your project and include the required header files in your source code. The framework will work in either a Cocoa or Carbon project, as you will see shortly. Listing 6.1 shows that the AudioUnit.h header includes all the other headers defined in the framework. Therefore, in many cases, you only need to be concerned about including the single file. Your project now has access to all the published functions in the framework.

Listing 6.1. AudioUnit.h



#ifndef __AUDIOUNIT__
#define __AUDIOUNIT__

// This is the main AudioUnit specification
#include <AudioUnit/AUComponent.h>
#include <AudioUnit/AudioOutputUnit.h>
#include <AudioUnit/MusicDevice.h>

#include <AudioUnit/AudioUnitProperties.h>
#include <AudioUnit/AudioUnitParameters.h>

#include <AudioUnit/AudioUnitCarbonView.h>

#include <AudioUnit/AudioCodec.h>

// This file relies on AUComponent.h
// and contains the differences of Version 1 API
#include <AudioUnit/AUNTComponent.h>

#endif /* __AUDIOUNIT__ */


Note

If you've read any books in the past that discussed code fragments, you are already familiar with the capabilities of the Code Fragment Manager. One of the nice features of this manager was versioning. Code fragments would automatically run the most recent version of themselves if multiple versions were installed. Frameworks are designed in such a way that multiple versions of a framework can be made available and accessed accordingly. You can check to see what the current version of a Framework is by using the CFBundleGetVersionNumber function in CFBundle.h. This can be useful if you want to check for features or implement workarounds.

Normally your application will link against a specific version of a framework: “Link against version A of AppKit,” for example. Using this mechanism, you can run older programs on newer computers since the older version of AppKit will still exist on the new machine. This topic is discussed in more detail in Apple's Mac OS X Overview documentation.

Apple Frameworks

Much of the functionality in Mac OS X is implemented via frameworks. The AudioUnits framework is one such framework that implements audio-related functionality in the OS. There are plenty of others as well. Browse the /System/Library/Frameworks directory structure for a whole bunch of frameworks available for your use (see Figure 6.2). You can usually find documentation on Apple's Web site for each framework.

Figure 6.2. A partial look at the /System/Library/Frameworks directory.

image

As you can see, there are plenty of frameworks to keep you busy exploring and new ones are being made available regularly. Most of them are documented, but every once in awhile, you might find one that has no documentation—be careful with these. Sometimes Apple uses a framework to implement something internally to the OS with out releasing the documentation right away, although the interface might exist. Eventually these interfaces usually become public, but if they are not officially documented, be wary of using them in any application you release to the public. You might find yourself with a headache later on when the interface changes.

Other frameworks exist in the /System/Library/PrivateFrameworks directory. These are truly private to Apple and can (and will) change at anytime without warning or concern for binary compatibility. Generally, anything in /System/Library/Frameworks is fair game for third-party developers such as yourself. Just because it isn't documented doesn't mean that it isn't supported. However, if you use anything in /System/Library/PrivateFrameworks, you can be guaranteed the headache that was only a possibility in the previous paragraph.

Third-Party Frameworks

Some very popular third-party frameworks are also available for your use. One in particular that is quite robust is made available by The Omni Group at http://www.omnigroup.com/developer/sourcecode/. The Omni Group provides multiple frameworks that provide varying functionality. All is available for your use under the Omni Source License (available on their Web site), and some of it is even cross-platform. Let's look at what it has to offer as of this writing. The following descriptions are directly from the Web site with only a few edits.

OmniBase

OmniBase is the lowest-level framework in the Omni framework suite. It is used by virtually every consumer product and consulting application we've written. OmniBase provides a series of debugging aids for class allocation and initialization, an alternative assertions mechanism, several Objective-C runtime manipulation aids, and a very reliable, cross-platform implementation of +load: (called +didLoad:).

OmniFoundation

OmniFoundation is our extension to Apple's Foundation framework. Beside several extremely useful extensions to Apple Foundation classes, OmniFoundation provides a horde of unique and powerful classes. Among the more interesting are

OFStringScannerA blindingly fast Unicode-safe alternative to NSStringScanner for when you really need to burn through those character streams.

OFRegularExpressionPowerful regular expression processing wrapped in an Objective-C shell. Combine with OFStringScanner for extra fun.

OFTrieImplementation of the popular trie data structure. Interfaces to OFStringScanner for rapid scanning of “longest” token elements.

OFMessageQueue and OFQueueProcessorWriting stable multithreaded applications is an error prone process. These classes help de-fang the savage beast.

OmniFoundation is a veritable treasure chest of programming goodies. Look for yourself.

OmniNetworking

OmniNetworking provides a simple and extensible Objective-C wrapper to a multitude of complex networking constructs. Communication over several Internet standard protocols is supported, including TCP, UDP, and Multicast. Writing a simple FTP client or custom TCP/IP-based server program becomes a trivial task using OmniNetworking.

OmniAppKit

OmniAppKit is our set of extensions to Apple's AppKit.framework—full of cool stuff to make Mac OS X application development even easier. Some highlights are

AppleScriptOAOSAScript makes it super easy to run AppleScripts from within a Cocoa app, and companion classes introduce features such as automatically populated script menus, scripts on toolbars, and extensions to the Text suite allowing scripters to manipulate rich text.

OAPreferencesAn architecture for building multipane Preferences windows. All you need to do is write the individual preference panes (which is quite easy) and OAPreferences will automatically generate an appropriate Preferences window—one such as System Preferences if you have a lot of panes, or one such as Mail's Preferences window if you only have a few.

OAFindPanelApple has a standardized architecture for Find in applications, but implementing it is entirely up to developers. OmniAppKit provides a powerful Find and Replace panel and a protocol for easily making widgets and documents in your app searchable.

OmniAppKit also includes a lot of extensions to Apple's classes to make creating rich user interfaces much easier.

OWF

Perhaps the most powerful framework in our entire framework suite, OWF, otherwise known as the Omni Web Framework, provides an advanced architecture within which to write multithreaded, Internet applications. OWF is the work horse in OmniWeb controlling all the content fetching, HTML/SGML parsing, FTP server manipulation, and so on. If OmniFoundation is the Objective-C programmer's Swiss Army Knife, OWF is their double barrel, rotary laser cannon.

Want to write a quick app to fetch stock prices from www.fakestocksite.com? OWF practically already does it. Need to fetch and parse a credit report from the credit bureau in your e-commerce site? Sprinkle in some custom logic, and the OWF fairy does the rest. In the next version, we plan on integrating back scratching and toast making.

OmniHTML

OmniHTML is the part of OmniWeb's architecture that actually renders parsed HTML and decoded images for display in a view. It also provides some conveniences such as URL dragging and file downloading that come in handy if you're writing something such as a Web browser.

As you can see, The Omni Group makes some powerful frameworks available. Be sure to check them out if you need any of the functionality they provide. This is proven code that you wouldn't want to rewrite if you didn't have to.

Now let's look at how to create our own framework!

The Result

Figure 6.3 shows the resulting application in action. Our application is written in Cocoa and calls through to both a Carbon and a Cocoa framework, depending on which button you press. The two integer values from the fields above the buttons are passed to the framework, and the result is returned and displayed in the alert. The application handles displaying all user interface components including the alert. In our case, the framework has no user interface at all and simply performs the calculation.

Figure 6.3. MyCocoaApp calling a framework.

image

Figure 6.4 shows what the Cocoa application's package looks like. Note that there is a Frameworks directory containing both the Carbon and Cocoa frameworks. Much like how plug-ins are copied into an application, we copy the frameworks into our application here. This is discussed in more detail later. We could have placed the frameworks in the ~/Library/Application Support/Our Application directory if we rather, but in this case it's easier to build them right into the application itself—no installer required! Mind you, by putting the frameworks directly in the application, they cannot be shared by other applications that might also use them. This is our trade-off at this point. /Library/Frameworks is also a popular place to store frameworks that need to be shared among multiple applications.

Figure 6.4. MyCocoaApp package contents.

image

Note

How do you decide if you should put a framework inside your application package or in a friendlier place to allow sharing between other applications? If your framework is private and no other applications (including your own) will use it, build it in to your application package. However, if you are working with a framework that will be used in multiple applications, save your users some drive space (and memory) and place them in a universal location to be shared among all the applications that will use them.

Taking this one step further, frameworks can be made available as application-specific (as noted previously), user-specific, machine-wide or across an entire local area network. Placing your framework in ~/Library/Frameworks makes it available to only that specific user. Placing your framework in /Library/Frameworks makes it available to all users on the machine. Finally, if you want to use your framework over an entire network, you would place it in /Network/Library/Frameworks.

In this chapter, we will create three projects. One is the Cocoa application that will call the frameworks and the other two are the frameworks themselves. One of the frameworks will be implemented as a Carbon framework; the other will be a Cocoa framework. Let's look at the projects!

The Carbon Framework Project

This project (see Figure 6.5) was started with the Carbon Framework template in Project Builder. Other than creating the MyCarbonFramework.c and MyCarbonFramework.h files as shown, there really is nothing more to do to have a working Carbon framework. Simply build the project, and MyCarbonFramework. framework will be created for you.

Figure 6.5. The MyCarbonFramework project in Project Builder.

image

Listings 6.2 and 6.3 show that this framework has one function, doAddition. It takes two integer parameters, a and b, and returns a single integer result. We could have added as many functions and source files as needed to this project. We could even add resources and nibs. As long as it all compiles, we're all set. Let's look at the Cocoa equivalent, and then we'll see how the Cocoa application makes use of these frameworks.

Listing 6.2. MyCarbonFramework.h



#include <Carbon/Carbon.h>

int doAddition(int a, int b);


Listing 6.3. MyCarbonFramework.c



#include "MyCarbonFramework.h"

int doAddition(int a, int b)
{
    return a+b;
}


The Cocoa Framework Project

This project (see Figure 6.6) was started with the Cocoa Framework template in Project Builder. Other than creating the MyCocoaFramework.m and MyCocoaFramework.h files as shown, there really is nothing more to do to have a working Cocoa framework. Simply build the project, and MyCocoaFramework.framework will be created for you.

Figure 6.6. The MyCocoaFramework project in Project Builder.

image

Listings 6.4 and 6.5 show that this framework has one class that has a single class method, +doAddition:plus:. It takes two integer parameters, a and b, and returns a single integer result. We could have added as many classes, methods, and source files as needed to this project. We could even add resources and nibs. As long as it all compiles, we're all set. See how similar the Carbon and Cocoa frameworks are? They both accomplish the same task with only slight differences. On disk, they both pretty much look the same as well.

Listing 6.4. MyCocoaFramework.h



#import <Cocoa/Cocoa.h>

@interface MyCocoaObject : NSObject
{
}
+ (int)doAddition:(int)a plus:(int)b;
@end


Listing 6.5. MyCocoaFramework.m



#import "MyCocoaFramework.h"

@implementation MyCocoaObject

+ (int)doAddition:(int)a plus:(int)b
{
    return a+b;
}

@end


Note

Question:

Why did we make this a class method?

Answer:

Because no object state was required to satisfy the result.

This method could have easily been implemented as a C-style function as in the Carbon example. However, I wanted to show a method as part of an object. Any framework you implement might be more complex than this example, and you might need to implement class and instance methods.

The Cocoa Application Project

Our Cocoa application is straightforward as well. We created a basic application project (see Figure 6.7) using our AppController to both implement the -applicationShouldTerminateAfterLastWindowClosed: and function as our window controller by managing the NSTextFields in the application window and the -doCarbon: and -doCocoa: action methods.

Figure 6.7. The MyCocoaApp project in Project Builder.

image

Note

Remember, in order for the AppController to receive the -applicationShouldTerminateAfterLastWindowClosed: delegate message, it must be registered as the application's delegate. One way to do this is to hook your AppController object up to the File's Owner's (NSApplication's) delegate outlet in MainMenu.nib. Another way is to send [NSApp setDelegate:self]; in AppController's -awakeFromNib method.

In AppController.h, the AppController object is defined as shown in Listing 6.6. Note that it has two IBOutlets for the text fields and two IBActions for the buttons. These are connected in the standard way using Interface Builder.

Listing 6.6. AppController Interface in AppController.h



@interface AppController : NSObject
{
    IBOutlet NSTextField *m_a;
    IBOutlet NSTextField *m_b;
}
-(IBAction)doCarbon:(id)sender;
-(IBAction)doCocoa:(id)sender;
@end


Listing 6.7 shows the AppController implementation itself. Notice that we import the header files from the two frameworks in order to have access to the functions/methods within them. In addition, at the top of this file, in awakeFromNib: we initialize the values of the two NSTextFields to 7 and 3. They are numbers chosen arbitrarily—they could have just as easily been 39 and 43. Jumping to the bottom of the file, the applicationShouldTerminateAfterLastWindowClosed: method is implemented as we've done in the past, for convenience.

The -doCarbon: and -doCocoa: methods are the meat of this example. When the user clicks the Carbon button, after giving her the chance to cancel, the -doCarbon: method calls the doAddition function in the Carbon framework. It passes the integer values from the text fields and displays the result in an alert. The -doCocoa: method works the same way with the exception that the framework that it calls contains a Cocoa class with +doAddition:plus: implemented as a class method.

Listing 6.7. AppController Implementation in AppController.m



#import "AppController.h"
#import "../MyCocoaFramework/MyCocoaFramework.h"
#import "../MyCarbonFramework/MyCarbonFramework.h"

@implementation AppController

- (void)awakeFromNib
{
    [m_a setIntValue:7];
    [m_b setIntValue:3];
}

-(IBAction)doCarbon:(id)sender
{
    if (NSRunAlertPanel(@"Carbon",
                @"Call the Carbon framework?", @"OK", @"Cancel", @"")
                == NSAlertDefaultReturn) {
        int result = doAddition([m_a intValue], [m_b intValue]);
        NSRunAlertPanel(@"Carbon Result",
                          @"The result of the computation is %d",
                          @"OK", @"", @"", result);
    }
}

-(IBAction)doCocoa:(id)sender
{
    if (NSRunAlertPanel(@"Cocoa",
                @"Call the Cocoa framework?", @"OK", @"Cancel", @"")
                == NSAlertDefaultReturn) {
        int result = [MyCocoaObject doAddition:[m_a intValue]
                          plus:[m_b intValue]];
        NSRunAlertPanel(@"Cocoa Result",
                          @"The result of the computation is %d",
                          @"OK", @"", @"", result);
    }
}

// Causes the application to quit when the one and only window is closed
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
{
    return TRUE;
}

@end


One last thing to remember is that the frameworks themselves must be copied into the application bundle. Figure 6.8 shows the Copy Files Build Phase, as you've seen used in previous chapters. Note that we are copying the files to the Frameworks directory as opposed to the Plug-Ins directory that we've used in the past. If you forget this step, your application will simply quit as soon as it launches. You might see a few bounces in the dock, but then it will fade away.

Figure 6.8. MyCocoaApp Copy Files build phase in Project Builder.

image

That is how you call a Cocoa or Carbon framework from within a Cocoa application.

Try This

Here are some ideas to expand the framework project in this chapter; try these on your own.

Add another method to the Cocoa framework, but make it an instance method instead of a class method. How would you call this method?

Create a doSubtraction implementation to each framework. Change the user interface to support addition or subtraction of the same numbers. You might consider using a pop-up button or radio buttons to allow the user to choose.

Instead of putting the frameworks in the application package itself, place them in a location that can be used to share the frameworks with other applications.

Conclusion

As you can see, frameworks are a great way to manage your source code. Even if you don't need to hide the implementation from others, packaging your tested modules as frameworks has many advantages for code reuse and distribution—even within your organization. Think about how you might factor your application to make it leverage the power of frameworks.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.81.33