Collection Extensions

Brown is Ugly

Well it’s been a long, long time since I posted anything here. If you’re reading this it’s either because we knock back drinks together at Mac events or you’re someone I should be buying a drink for at a Mac event. The basic idea I initially had for this blog was that I’d post a series of Weird & Wonderful Cocoa tricks. There’s tons of great Cocoa content out there ranging from introductory to pretty advanced but it seemed like there was a hole in the Semi-Abusive segment. I’ve had a number of ideas I’ve wanted to put up but with the pressures of the oppressive corporate world (I work at a subsidiary of an umbrella company held by a Rogue Amoeba interest. We’re hiring.) and various other things on my plate nothing ever gets written. The thing about the Weird & Wonderful is that while it’s easy to know if something is weird it’s something else to know if it is indeed Wonderful and it’s something even more else to be able to explain why it’s Wonderful. So in the interest of trying to keep my feed from turning brown in NetNewsWire I think I’ll start to mix it up a little. That said, let’s get Weird & Wonderful.

Loopy Problems

You’re too smart to be writing yet another loop. Nine times out of ten it’s mostly boiler plate – the operation you’re doing can be easily explained in a couple of words but expressing it in code is cumbersome. Even with Objective-C 2.0’s new for in iteration loops are still basically boring beasts. Let’s see what we can do to avoid them.

At the mega-giant corporation where I slave under the ever watchful eye of a hawkish manager we often need to iterate over Employees to create the endless reports required by our strict oversight committee. We’d like to be able to say, “Give me the names of all Employees who’s computers are idle for more than 5 minutes”. Here’s how we’d do it in Cocoa:

NSMutableArray *namesOfIdleEmployees = [NSMutableArray array];
for ( Employee *employee in allEmployees )
{
    if ( employee.idleTime > 5 )
    {
        [namesOfIdleEmployees addObject: employee.name];
    }
}

Now, sure, that doesn’t look like much code and it’s not really arduous to write but you’ve got better things to do with your time than crank out code like that. Like make more money for your overlords. They love that.

So what are we actually doing in that loop? How would we express it in words? Maybe “collect the names of employees who are idle for more than five minutes”. That sounds like a nice concise order, one you could pretty easily turn into a method on the collection. But if you did that in this case there’s a billion other cases you’d have to do too. And that gets more tiring that just doing the loops when you need them. It would be nice if we could just ask the collection though. Kind of like how we can ask for a @distinctUnionOfObjects. However, to do that we’d need to add our own array operators or override valueForKeyPath: or something weird.

Weird & Wonderful

NSArray *names = [allEmployees valueForKeyPath: @"[collect].{idleTime>5}.name"];

Kind of nifty, isn’t it? What we’re doing here is saying we want to filter based on idle time and any objects that pass the filter we collect their name. Easy as pie.

To do this you do indeed have to override valueForKeyPath: – there’s no way to add your own @style array operators. That’s ok though because once we’ve overridden valueForKeyPath: we can get fancy with how we handle things and add some pretty powerful functionality.

  • method calls

    NSArray *results = [myCollection valueForKeyPath: @"[collect].name"];
    

    Method calls go between []’s. In this case since there’s no parameter given an implicit parameter is assumed. The result of this expression is basically: [myCollection collect: @"name"] – the collect method iterates over the objects in the collection and gathers all the values for the key ‘name’.

    You can have more complicated method calls:

    NSArray *results = 
    [myCollection valueForKeyPath: @"[collect].name.[componentsSeparatedByString: ' ']"];
    

    The result of this expression is that componentsSeparatedByString: @" " will be called for the value of ‘name’ in each object in the collection. The resulting array of components will then be gathered by the ‘collect’ call. The end result is an array of subarrays containing the words of the name.

  • inline predicates

    NSArray *results = 
    [myCollection valueForKeyPath: @"[collect].{salary>100}.jobTitle"];
    

    Predicates can be specified between {}’s. The predicate string is used to create an NSPredicate which is then used to evaluate the object. If the object matches the predicate then it returns the value of the remainder of the keypath otherwise it returns nil. In this case we use the predicate to filter the collection based on a salary. Each object is checked if it’s salary property is greater than 100. If it is then the value of it’s jobTitle property is returned. If it’s not nil is returned. Collect gathers the results ignoring any that are nil. The end effect of this is that you’ll get an array of all the job titles where salary is more than 100.

  • inline value transformers

     NSArray *results =
     [myCollection valueForKeyPath:
        @"[collect].<NSUnarchiveFromDataTransformerName>.imageData"];
    

    You may specify the name of a value transformer between <>’s. The value transformer is handed the value of the remainder of the keypath. In this case we use the unarchive from data value transformer and hand it some imageData. The result of the transformer is then collected by ‘collect’. The resulting array would contain unarchived NSImage instances. You may specify any of the build in value transformers by their constants or you can use your own value transformers names.

Some Examples

NSArray *waitsAlbumCovers =
[myRecordCollection valueForKeyPath:
@"[collect].{artist like 'Tom Waits'}.<NSUnarchiveFromDataTransformerName>.albumCoverImageData"];

waitsAlbumCovers now contains NSImage instances for each of the albums in my collection where ‘Tom Waits’ is the artist.

NSString *albumsTitles =
[myRecordCollection valueForKeyPath:
@"[concatenate: * withSeparator: ', '].{artist=='Tom Waits'}.albumTitle"];

albumTitles contains a string of all Tom Wait’s album titles separated with ‘, ‘. This example shows the use of a special place holder symbol. The ‘*’ expands during evaluation to the remainder of the keypath. In this case the resulting call on the collection would look like: [myRecordCollection concatentate: @"{artist=='Tom Waits'}.albumTitle" withSeparator: @", "]; The concatenate:withSeparator: method would then iterate over the contents of the collection and concatenate the value of {artist like 'Tom Waits'}.albumTitle placing the separator in between.

Implementation

The code for all this is actually surprisingly small. It’s one file, one class which isn’t actually really used and a couple of categories. Objective-C & Cocoa are just ridiculously awesome at times.

First up we need to override valueForKeyPath:. That’s totally possible to do – we make our own NSObject subclass then inherit everything from that and we’re good to go. Except that’s a lame solution because then we’d not be able to use these calls on stock Cocoa collections. What we want is to replace NSObject‘s implementation with our own. On Leopard there’s a new way of doing that: class_replaceMethod. We use that early in the life time of the process (in main.m before you actually kick off the AppKit) and we’re good to go.

The code for our version of valueForKeyPath: is also pretty basic. We’re given a “key path” which is a string with components separated by ‘.’ characters. We just use componentsSeparatedByString: to grab each component then iterate over each one. If we detect that the component starts with one of our special characters ( [, { or < ) then we handle it. Otherwise we just recurse down the component list.

collect:, concatenate: and other methods need to be implemented by each of the collection classes you’d want to call them on. Rather than code them for each kind of collection I hung them off NSObject in a category and, better, I also made NSObject be able to act as a collection by implementing NSFastEnumeration for it. So now this is valid code:

    NSString *exampleString = @"Example string";
    for ( NSString *string in exampleString ) NSLog( @"%@", string );

That’ll print out “Example string”. As far as I’m concerned single instances should behave as collections of 1 item. Adding this to NSObject doesn’t effect NSArray, NSSet, or NSDictionary enumerations at all. The real benefit of NSObject being a collection is that this, sort of contrived example, works:

    NSString *jobTitle =
        [person valueForKeyPath: @"[collect].{hasNotBeedFiredYet==YES}.jobTitle"];

You’ll get nil or the jobTitle depending on the person not being canned yet.

The best way to check out the implementation though is to grab the source code. It should be pointed out that this code should really be taken as a proof of concept. There’s so much room for optimization and general clean up it’s not even funny. It does, however, work, which is always nice, and it shows off what you can do with this kind of approach. Also as a word of warning: I’ve yet to use this in any sizable project. It may also be a terribly bad idea for some reason that’s escaped me but will no doubt be pointed out in the first comment.

All the warnings out of the way I think what you can do with this stuff is pretty powerful and will save a ton of boring loop iteration code. It lets you more easily express what you want rather than the iterative steps needed to get it done. And that’s always a good thing.

Bug Responder

When the user takes an action in a Cocoa app the framework looks for the first object in the responder chain that can handle that action. Basically each object in the responder chain is asked if it implements the given action selector and if not passes the buck on to the next object in the chain. This works great since you can put code to handle user actions in the higher levels of your applications controller classes without having to worry about calling into them yourself from button handlers. The trade off here is that the further removed from the action you put the code the less contextual information you may have with which to make your decisions.

Generally this isn’t a problem. Your NSWindowController will handle an action for the relevant document or you handle it higher up for the application in general. Things become a little more complicated though when you have view controllers. These are very much like window controllers except they are responsible for only a sub-portion of the view hierarchy. As an example consider a view controller in charge of a date picker. The date picker may have several subviews, one for the day, month, year, maybe some more for the time. The idea of a view controller is that it can create a view hierarchy and manage it in a way thats opaque to the rest of the app. The date picker is a trivial example – you can imagine view controllers that manage a complex amount of state and touch their model area in a non-trivial way.

Often you’ll want to give the user the ability to report bugs in your software, sort of like what Safari does with it’s Bug Button. Ideally we’d click the bug button or chose an item from a menu and we’d get a nice bug report with contextual information as to what the user was looking at. There were a couple of ways to approach this. First is to add code in each controller that’ll handle a ‘reportBug:’ action, gather it’s information and send it to the centralized bug reporting class. This fails though because if I implement ‘reportBug:’ in my date picker then I lose the context in which I was trying to pick a date – and the bug report loses some of it’s usefulness. If I implement ‘reportBug:’ in my window controller then I can know the big picture but I don’t know what’s going on with the view controllers below me – they may have pertinent information that hasn’t been committed to the model layer yet. So it looks like we’re screwed either way.

My solution to the problem was to implement my ‘reportBug:’ action at the highest level in my application. It looks like this:


- (IBAction) reportBug: (id) sender
{
    KBBugReportController *bugReport = [KBBugReportController sharedBugReportController];
    [bugReport gatherBugReportInformation];
    [bugReport sendBugReport];
}

Nice and simple, isn’t it? Except how can ‘gatherBugReportInformation’ work? The trick is we walk the responder chain ourselves and ask each responder to contribute some context information. By the time we’ve walked the entire chain we’ll have travelled from the most specific information to the most general.

Here’s what that’d look like:

- (void) gatherBugReportInformation
{
    KBCoalescingDictionary *bugReportInformation = [KBCoalescingDictionary dictionary];
    NSResponder *responder = [[NSApp mainWindow] firstResponder];
    while ( responder != nil ) 
    {
        if ( [responder respondsToSelector: @selector( submitBugReportInformation: )] )
        {
            [(id)responder submitBugReportInformation: bugReportInformation];
        }
        responder = [responder nextResponder];
    }

    NSArray *titles = [bugReportInformation objectsForKey: kKBBugReportTitleKey];
    [self setTitle: [titles componentsJoinedByString: @" "]];

    NSArray *contents = [bugReportInformation objectsForKey: kKBBugReportContentKey];
    [self setContent: [contents componentsJoinedByString: @" "]];

    [bugReportInformation setObject: [NSDate date] forKey: kKBBugReportTimeStampKey];

    [self setBugReportInformation: bugReportInformation];
}

The method I use here is that responders implement an informal protocol or one method – ‘submitBugReportInformation:’. The method takes one argument a special NSMutableDictionary into which they can write whatever bug report info they want. I’ve got a few special keys defined for the bug report title, contents and time stamp. If you send the bug report in an email the title and contents come in handy. There is one trick here and that’s using a coalescing dictionary. All that does is that when it gets asked to set and object for a key that already exists it instead makes and array and sticks both objects into the key slot. Asking it objectForKey will return the last object set and asking it for objectsForKey (note the ‘s’) will return the array of all objects set for that key. This makes it easy to just write whatever you want into it without worrying about blowing away other objects data.

By the end of ‘gatherBugReportInformation’ we’ve travelled the chain and given each responder the opportunity to help us out with some context info. All we need to do now is send our dictionary off to our server somehow. We can either upload it with an HTTP POST request or send it off in an email. Either way I suggest letting the user have a look at what you’re going to send; you can get quite a backlash for not being polite about this kind of thing.

Shader Source Code

I’ve posted the source code to a basic implementation of the Cocoa Shaders I talked about here. There’s code for conditional shaders, shader lists, clip shaders, and affine transform shaders. As far as shaders that actually draw there’s a solid color shader and one for drawing an image. The image shader lets you tweak the compositing operation, source rect, and set a drawing scale.

Included is a sample app to show you how you could use this stuff in practice. The app has a custom view which draws using a shader. The demo shader will change its background from blue to red when the view is clicked. Its implemented using only the simple shaders provided.

Fancier gradient fill shaders, CoreImage based shaders and that kind of thing are left, for now, as an exercise for the reader. I mentioned it in the original post but if you’re looking to do gradients you’d do yourself a favour by looking at CTGradient.