Practice Safe Subclassing: NSTableView Row Highlighting

Even though hand crafting source lists are a lost art on Leopard (since the look is now offered natively in AppKit), for cross-developed applications or other styling needs, I’d to demonstrate a safe and sturdy technique for custom drawn NSTableView row highlights.

One seemingly always overlooked fact about NSTableView subclassing is that custom row highlighting can be achieved solely by overriding public, documented methods on 10.4 and above. A decent number of NSTableView subclasses I’ve come across, mainly source list implementations, accomplish customized row highlighting by stepping in the way of the superclass’s normal highlight drawing process by overriding a private and undocumented method called “-_highlightColorForCell.”

As you can imagine, depending on the existence and behavior of a private method is a dangerous line to walk your application along. Such methods have no documented or expected behavior, and more importantly, Apple reserves the right to change or remove parts of the class’s internal implementation as part of any future AppKit framework release.

What I’m focused on is the technique used to circumvent the NSTableView’s normal (superclass defined) row highlighting process. This task is an essential component of any source list style subclass, as we’d like to replace Apple’s highlight with something completely different. The process is actually quite simple; what’s difficult is knowing another approach exists and which methods should be overridden.

So, without further ado, the safe way to bypass the standard highlighting is to introduce an NSCell subclass to accompany your custom NSTableView. Just about all NSTableView row highlighting customizations are source lists, which already necessitate an NSCell derivative anyway.

Here’s how it works:

In your NSCell subclass:

- (NSColor *)highlightColorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
// Returning nil circumvents the standard row highlighting.
  return nil;
}

In your NSTableView subclass, the following method can be overridden to perform all custom row highlight drawing:

- (void)highlightSelectionInClipRect:(NSRect)clipRect;

One of the reasons you’d want to put your custom row highlight drawing in the table and not the cell is drawing a contiguous gradient for multiple selections.

That’s it! No private method overriding necessary, so now you can highlight away and sleep better at night. This is tested to work on Tiger and above, but since the above methods have been around since 10.1, the process will likely work on older systems as well. I plan to release a reusable and customizable source list view based on this safer approach in the coming week or so; stay tuned to the feed if you’re interested.

Continue reading » · Rating: · Written on: 04-20-08 · No Comments »

Make Your Own Abstract Factory Class Cluster in Objective-C

Crafting software is more than just mastering the syntax of any given language - the overall architecture, relationships and interaction among an application’s source code weigh in heavily to define true engineering elegance. A winning design is often harder to achieve than superior syntax, since the solutions are not as clear cut and mistakes are not immediately obvious. No compiler can warn us of poor architectural and design decisions, we instead realize such problems maintaining our code in the future. Thankfully, design principles and patterns exist to help us write elegant software.

One design template relied upon heavily by Cocoa’s Foundation framework is the class cluster, which is implemented in a manner consistent with the Abstract Factory design pattern. I’d like to explain how you’d actually go about rolling one of these yourself, and also touch on some of the motivating factors for introducing such a pattern into your own application. Later, I’ll relate to a real-world example of a class-cluster I designed for use in Camino to support the flexible parsing of search engine plugins.

First of all, what exactly is a class cluster?

In a nutshell, it’s a design that allows you to incorporate a family of functionally-related of objects into your application while keeping the code interacting with those objects loosely coupled, flexible, and easy to maintain or update.

A class cluster conforms this set of common objects to behave according to a single interface and, furthermore, channels all creation of them through that interface. The construction is made up of two key parts: a) one public, abstract interface serving as the “face” of the cluster which advertises the supported API, and b) many private, concrete subclasses of this interface responsible for actually implementing the advertised behavior of the superclass in their own specific way. The abstract superclass does implement a few methods itself, the most significant being a factory method to vend instances of the private subclasses; other common functionality shared across all subclasses, such as accessors, can also be defined here and shared as well.

Users of the cluster only ever see the one public superclass, are unaware that it is actually abstract, and know nothing about the existence of any private concrete subclasses. The superclass offers a factory creational method which is responsible for determining which subclass is appropriate for any given situation and transparently returning an instance of it. Since this returned object implements and behaves according to the public superclass’ interface, users can simply assume they have obtained a direct instance of this superclass.

What makes this a good idea?

Overview of a class cluster’s advantages:

  • Code is shared among a class hierarchy
  • Users of the collection are loosely coupled: They depend on only one new object
  • Change is isolated: the cluster is free to expand without affecting external code
  • Avoid duplicating code and logic: Determining which subclass is appropriate for any given situation is not repeated in multiple places.
  • Pain-free maintenance: New behavior can be added in only one place, the cluster itself.
  • External code is kept simple; Complexity is hidden inside the cluster
  • Your application can automatically use new behavior added to the cluster with no (or minimal) change to external code.

An Abstract Factory-based class cluster offers the benefits of shading code among a class hierarchy without subjecting users to depend upon the entire collection of classes. By encapsulating the awareness and creation of private subclasses inside the cluster, external code is loosely coupled to the hierarchy and clients end up programming according to (and depending upon) one interface, not various implementations.

"Depend upon Abstractions. Do not depend upon concretions"

A cluster hides the actual determination of which unique subclass to use for a certain situation into one area: the superclass’ init method. Leaving creational logic spread (and repeated) throughout the application is a maintenance problem because each time a new object is introduced all locations in code where one of these subclasses are created would require modification. The cluster can then use arguments to its initWith…: method as clues to determine which subclass can handle the desired behavior.

External code is thus dependent upon only one class. External users of your class cluster can be shielded from the fact that a different object (a subclass of the cluster) is returned rather than a direct instance of the cluster itself. The way to hide this fact is by ensuring the object responds to all methods declared in the cluster’s public interface. This strategy increases your code’s maintainability by allowing you to transparently introduce a new subclass to handle additional specific behavior flexibility by programming to an abstraction and not a concrete implementation.

"Identify the aspects of your application that vary and separate them from what stays the same."

Much of complexity and extra work behind designing a class cluster lies in the initial creation. Afterward, when that inevitable aspect of software engineering known as change comes around, you will immediately realize the importance and beauty of this pattern. Our application will be easier to maintain, nimble to change, and highly flexible.

A real example: Camino’s Search Plugin Parsing.

In Camino, I worked on support for the ability to parse and install plugins. Search engines can describe themselves (their name, query URL structure, etc) in a plugin file, which we can then parse and use that information in our built-in toolbar search field. I wanted to avoid locking us down to any one particular plugin format and at the same time make it quick and painless to introduce support for a new type, should another become widely used in the future.

Here’s a diagram of the Abstract Factory based class cluster I came up with:

Search Plugin Parsing Class Cluster

Following this design, introducing support for another plugin file format means all client code would just “automatically” start using it. Camino just supplies the MIME type of a plugin, and if the parsing cluster knows how to deal with that format an object is successfully created and returned. External code using the cluster would not require any modifications whatsoever to support a newly introduced format parser. External code does not need to be aware of each and every plugin parser.

External users of the parsing cluster have no knowledge of which SearchPluginParser subclasses should be used for each particular plugin format (or even that any such subclasses exist in the first place). It should appear, on the surface, that the SearchPluginParser itself actually implements all behavior.

Here’s some simplified sample code to demonstrate how to go about this:

SearchPluginParser.h

@interface SearchPluginParser
// Our class cluster’s public API:

- (id)initWithPluginMIMEType:(NSString *)mimeType;

- (BOOL)parseSearchPluginAtURL:(NSURL *)searchPluginURL;

- (NSURL *)searchPluginURL;

- (NSString *)searchPluginName;

@end

#pragma mark -

@interface SearchPluginParser (AbstractMethods)
// Abstract methods which should be implemented by subclasses:

-(BOOL)parsePluginData:(NSData *)searchPluginData;

@end

#pragma mark -

@interface SearchPluginParser (SubclassUseOnly)

// Private, concrete methods which should only be used by subclasses:

- (void)setSearchEngineName:(NSString *)newSearchEngineName;

- (void)setSearchEngineURL:(NSString *)newSearchEngineURL;

@end

SearchPluginParser.m

@implementation SearchPluginParser

// Returns nil if type is not supported.

- (id)initWithPluginMIMEType:(NSString *)mimeType
{
  // Since we’re an abstract object, transparently

  // return one of our concrete subclasses.
  [self release];

  // This is where you’d determine which concrete
  // subclass can best handle the desired behavior.

  if ([mimeType isEqualToString:kOpenSearchType])
    self = [[OpenSearchParser alloc] init];
  else

    self = nil;

  return self;
}

// Define any other default implementations
// for other methods, such as the accessors…


@end

OpenSearchParser.{h,m}

// Private subclass, known only to SearchPluginParser,
// which it uses to parse a certain file format:

@interface OpenSearchParser : SearchPluginParser

@end

@implementation OpenSearchParser


-(BOOL)parsePluginAtURL:(NSURL *)pluginURL
{
  // Implement this abstract method with the specific
  // knowledge of how to parse OpenSearch plugins.

}

@end

External Code in the Browser

- (void)detectedSearchPlugin:(NSDictionary *)searchPluginInfoDict
{
  SearchPluginParser *pluginParser =      [[SearchPluginParser alloc] initWithMIMEType:      [searchPluginInfoDict objectForKey:kSearchPluginMIMETypeKey]];

  // Check return value and then call methods declared publicly

  // in SearchPluginParser.h.

  // We’re essentially treating the returned object
  // as a SearchPluginParser, even though it’s actually
  // a subclass of it.  We know nothing about OpenSearchParser.
}

The cluster’s init method should have the knowledge to determine which private, concrete subclass to actually implement the required behavior. Now, if we decide to add another plugin format, the client code shouldn’t need to change at all - the cluster would introduce the new private subclass and handle the new behavior.

A note about over-engineering: Whenever such an advanced and complex design is introduced into an application one could convincingly argue that the initial work was unnecessary and over the top. To that I would caution that you should not use a design pattern simply because it exists and seems cool. Weigh your options to carefully determine if spending some initial development time up-front will definitely offer benefit to the long term future of your source code.

Continue reading » · Rating: · Written on: 04-20-08 · No Comments »

The First Post

Well, it’s been a goal of mine for quite some time to start up a weblog, mainly to express the many Cocoa development related thoughts flying around inside of my head.  I thoroughly enjoy reading the rich diversity of developer blogs in the Mac OS X community, especially when I come across an awesome and in-depth technically focused article.  I hope to contribute a few drops of my own into this vast ocean of information.

A little background about who I am, and my experience:

Programming in Cocoa and Objective-C has been my main focus (and eventually, job) since 2004.  I’m extremely passionate about the Mac platform, foremost as a developer, but also as a user.  Currently, my main source of fame in the Cocoa community is my position as a member of Camino’s programming team.  Elsewhere, I’m active on the Cocoa-Dev mailing list trying to help out when I can.

When I’m not writing code I’m usually outside, hanging around on a horse farm where I’m lucky enough to be the best friend (owner) of a quarter horse named Dude (that red guy with me, above).  I’m also found hiking in the awesome Pennsylvania mountains with my golden-retriever Casey, or giving out “lap time” to a few cat friends I fell in love with and brought home from the barn.  I work part-time taking care of the 18 horses on the farm,  and often wonder if perhaps I’m the only horse farmer / Cocoa developer that exists.  Anyway, my passions in life are divvied among software development, animals, nature, environmentalism, healthy eating, and hockey.  That’s me in a paragraph!

I should give a shout and admit that I was further motivated by Daniel Jalkut’s No More Excuses post.  The rest of my inspiration came from the fact that there’s a shortage of fellow Cocoa developers to share my thoughts with in Northeast PA, and I can only bore my friends and family with so much ;)

Anyway, on my home page you’ll find out more about me, including photos and a resume.   Thanks for taking time to stop by and listen to what I have to say.

Upcoming post ideas include:

  • Designing your own Abstract Factory Class Cluster in Obj-C
  • Custom NSTableView row highlighting without the use of private methods
  • Protecting your code from change with the use of Interfaces and isolation.
  • The why and how of using constant NSStrings, efficiently and elegantly.
Continue reading » · Rating: · Written on: 04-20-08 · No Comments »