ObjectWare
Volume Number: 13
Issue Number: 6
Column Tag: Openstep
ObjectWare
by Michael Rutman, independent consultant
Selling your technology to developers as a drop-in
What is ObjectWare?
As functionality in different applications start to overlap, many programmers are
forced to reinvent existing technology. Most modern word processors have drawing
packages built into them, and most modern drawing packages have word processors
built into them. Normally, each developer must create both a word processor and a
drawing package. With ObjectWare, the philosophy is to create a great word processor
object, which can be dropped into any application and then sell it to application
developers.
Why is ObjectWare a philosophy rather than a technique? There are many techniques
to add technology to an application, ranging from shared libraries to publicly sold
source code. ObjectWare differs in the process. If an application is already object
oriented, and you can isolate the objects needed to use a technology, then those objects
can be easily bundled and resold. Not only can you sell your application to end users,
but you can sell your technology to other application developers. Using shared
libraries is a technique for doing ObjectWare.
OPENSTEP's support of ObjectWare is done through custom palettes for
InterfaceBuilder. A palette is a bundle of object files, interface files, and any other
resource that InterfaceBuilder would need to add the technology to an application. Once
loaded into InterfaceBuilder, all the functionality of the custom objects are accessable
as if NeXT had bundled them into InterfaceBuilder in the first place.
NeXT provides a wonderful tutorial, called CustomPalette.rtfd. I have been assured that
this documentation will become available to all Macintosh developers soon. Many of the
details, as well as source code, for implementing custom palettes, which are the user
interface to an ObjectWare package, are located in that tutorial.
The code in this article is built using NEXTSTEP 3.3 for the development, and all
coding is done in Objective-C. OPENSTEP is slightly different, and Rhapsody has not
been fully defined as of the writing of this article.
How do I use ObjectWare?
Under OPENSTEP, ObjectWare is easier than ever before. Adding a new technology to
your application can be as easy as a double-click and drag and drop. Bundled objects
are built with palettes and then can be loaded into InterfaceBuilder. Once loaded into
InterfaceBuilder, they can be dragged into your UI just like any existing OPENSTEP
object. At that point, there is no difference between adding a button, a graph or any
other object to your application. To load the libraries, just drag and drop the library
into your project, and it's ready for compilation.
Unfortunately, as a developer you will probably have to write some code. Not always,
though, as there are some Objects that just work and require no setup. A graphing
object, for example, will probably require some custom code to interact between the
user and the application. On the other hand, an ObjectWare package of pretty buttons
could be hooked up to your application using InterfaceBuilder without any code
changes.
Related to ObjectWare is loadable bundles. Loadable bundles are functionality that can
be added to existing applications at run time. Metrowerks and Adobe Plugins are very
similar to loadable bundles.
Creating an ObjectWare Palette
ObjectWare under OPENSTEP requires an InterfaceBuilder palette and a library, both
of which are created using ProjectBuilder. When ProjectBuilder creates a new
project, it will ask for a project type such as palette, bundle, library or application.
When creating a palette, ProjectBuilder will create a folder, a nib (InterfaceBuilder
file), and some starting source files.
The starting source files ProjectBuilder creates will be enough to load custom objects
into InterfaceBuilder. Unlike Constructor on the Macintosh, these objects are more
than placeholders. InterfaceBuilder allows user interfaces to be tested without adding
any code. An example of this is the Text Object. Many times OPENSTEP's power has
been demonstrated by placing a Text Object in a window and testing it from
InterfaceBuilder. Without adding a single line of code, the Text Object is able to spell
check, word wrap, place tabs, and many other features found only in more powerful
text editors. Likewise, any ObjectWare created will be as full-featured from
InterfaceBuilder as from any application they are used in.
The starting source files created by ProjectBuilder are mostly complete. If you are
adding only View elements, then you need not edit the starting source files. However, to
add non-View object you must first associate it with a View object so InterfaceBuilder
has a way to represent the object. This requires your overriding the
finishInstantiate() method and adding a line of code to associate each non-view
object with a custom view. In other words, if there is a non-graphical object, both the
non-graphical object and a custom view representing that object must be created.
When the developer grabs the custom view and drags it to where it belongs,
InterfaceBuilder will create the correct non-view object instead. The view object is
required to draw something to provide feedback in InterfaceBuilder.
When the project is created ProjectBuilder creates a default palette to represent your
objects in InterfaceBuilder. You must use InterfaceBuilder to modify this palette to
populate it with whatever interface elements are necessary for the ObjectWare
package. Palettes can contain custom views, menus, windows, or objects with no
interface element. Each element must have a subclass of view created and placed in the
palette window.
Basic palettes don't require any special code to make them work, but more
sophisticated palettes might. For example, your palette might have an Inspector panel
where various attributes, such as initial settings, can be set. These attributes should
probably be saved as the application is being built. Use of the Inspector and reading and
writing attributes require custom code. OPENSTEP provides objects to help perform
these tasks. One such object is typed streams, which are an interesting mechanism for
saving and restoring objects with disk files.
Typed Streams
Typed streams are a private data format for archiving objects. Each object declares the
types in the stream, then writes those types. An object can declare another object, and
write that object to the stream. In practice, it is usually easiest to write the document
object to a stream, and have the document object's write method declare all other
objects. Listing 1 shows sample code that archives some integers and subsidiary
objects. One of the powers of typed stream is not having to worry about big
endian/little endian issues. Typed streams work correctly with everything but bit
fields.
Listing 1:
- read:(NXTypedStream*)stream
{
[super read:stream];
NXReadTypes( stream, "ii", &total, &stepSize );
helperObject = NXReadObject( stream );
return self;
}
- write:(NXTypedStream*)stream
{
[super write:stream];
NXWriteTypes( stream, "ii", &total, &stepSize );
NXWriteObject( stream, helperObject );
return self;
}
Typed streams are very easy to implement, but they do have some drawbacks. Each
written stream must be read correctly. In the read method, the code has to specify that
it is reading integers. If the stream and the code don't match, then the read will raise
an exception. Typed streams support versioning, though, and each object can specify
that objects version. So, if only one object changes, then that object would up its
version number without any changes outside of that object.
Another drawback of typed streams is the lack of random access. Typed streams are
meant to be read in their entirety. Each object is created in turn. Deferred loading of
part of a document would be difficult.
The final problem I have had with typed streams is lack of portability. Despite
OPENSTEP running on Intel, HP, Sun, and now Macintosh hardware, some people still
need Windows applications. Some developers also need the file format to be the same
across their Windows and OPENSTEP applications. This just isn't going to work.
Inspectors
Most objects will require an inspector to examine and modify attributes of the object
in InterfaceBuilder. The inspector code is only used by InterfaceBuilder, but it is still
important. There are 4 inspectors for each object, Attribute, Connection, Size, and
Help. Default inspectors are provided automatically if custom inspectors are not
created. Custom Connection and Help inspectors are rarely used.
Custom palette objects are identified by adding one or more of the following factory
methods to the object:
getInspectorClassName
getConnectInspectorClassName
getSizeInspectorClassName
getHelpInspectorClassName
Factory methods are, for all intensive purposes, the same as C++ static methods.
There are some differences, but most of those are implementation details. Here,
factory methods are used to retrieve global objects.
Each custom inspector object must be added to the palette project. There is no
theoretical limit on the number of custom objects that can be placed in a palette of a
project, as each custom object identifies which custom objects apply. Likewise, a
custom inspector object can inspect multiple objects.
Each custom inspector is an object and a nib file. Interface Builder has a command for
creating the nib and object templates. Unlike the palette object, the inspector objects
require some code, but not much.
The inspector object inherits from IBInspector, which has outlets for connecting to the
inspector. Once these outlets are hooked up, then the object is able to communicate
with InterfaceBuilder.
There are four methods that need to be overridden. The first is the init method. The init
method is called automatically by InterfaceBuilder and is responsible for loading the
nib file. As well as the nib file, the init method can instantiate any helper objects it
will require.
The second method required is the wantsButtons method. InterfaceBuilder will call
each custom Inspectors wantsButtons to determine if an OK and Revert button are
needed. If the inspector is doing live updating, then there is no reason for the OK and
Revert buttons to be present. Most objects don't do live updating, but wait for the OK
button to be hit.
The last two methods are OK and Revert. These methods are automatically called when
the user clicks on the OK and Revert buttons. Both of these methods must access the API
created for the custom object to set and retrieve values. InterfaceBuilder saves
settings by writing objects into a nib file. When an application loads a nib file, the
retrieval method unarchives the objects. In reality, every object displayed in
InterfaceBuilder is an instantiation of the object that will appear in the application.
Library
The last step is to distribute the object files for application programmers to use.
Unfortunately, the palette is only usable in InterfaceBuilder. Fortunately, the same
objects used in the palette can be used in the library.
The best mechanism, but the least used, is to distribute the source code. Of course,
every programmer wants to be able to fix bugs in other programmer's libraries, but
giving away your source code is rarely used.
If there is only one custom object, then distributing the object file created by gcc will
work. The object file can be included in a project without any problems. If there are
many objects, then the objects can be linked into a library. ProjectBuilder can create
libraries as easily as creating palettes or applications. Each directory can only hold
one project, so a second directory will have to be created.
The standard way of creating the libraries is to bundle the objects with any nib files
needed. Again, ProjectBuilder can create Bundles automatically. Bundles are a
collection of objects and, nib files, and other resources that can either linked in or
loaded at run-time. Shipping as a bundle allows applications to load functionality as
needed.
Conclusion
With ObjectWare, not only can developers market their technologies for other
developers to use, but they can incorporate the latest technologies without
re-inventing the them. OPENSTEP's architecture and development tools make bundling
technology simple. There is no set source code language, any resources can be included,
any call made by an application can be made by the custom objects. Furthermore,
developers that are testing their interfaces in InterfaceBuilder using the custom
palettes will see the objects live instead of an abstract box that might work once the
application is done.
Overall, ObjectWare under OPENSTEP opens possibilities that other platforms have
long struggled to achieve.