Categories
Uncategorized

Expanding Outline Views in Cocoa

Thanks to a lengthy commute last week, I’ve been making a toy in Cocoa. It reads an URL, parses some XML and displays it in an NSOutlineView. Simple stuff, but it will hopefully make my life better at work, where I need to do this a fair bit.

By default, when I load the XML into the NSOutlineView, everything is closed up. So all you’re presented with is the root element. I’d like to expand that so it automatically includes all children of the root element. Nice and simple—there’s an expandItem: method.

Except that when I call it from the action that puts the XML into the NSOutlineView, it doesn’t work. Bugger.

After instrumenting my XML data source, I can see that nothing is really happening until after my action. My suspicion is that the NSOutlineView isn’t realising that it has any data until after the first call to display.

I tested this by hooking up the call to expandItem into a secondary action (on another button). And it works great.

So, I need a way to say “call this code back in the next idle period”. And this is where I start to get upset that Objective-C doesn’t have closures.

How to execute code soon isn’t easily determined from the apple docs. My guess is that you use an NSTimer with a very small NSTimeInterval . Let’s try that…

  NSTimer *idle = [NSTimer scheduledTimerWithTimeInterval:0.0
                                                   target:self
                                                 selector:@selector(expandRoot)
                                                 userInfo:nil
                                                  repeats:NO];
  [[NSRunLoop currentRunLoop] addTimer:idle forMode:NSDefaultRunLoopMode];

Well, it works. But! Further investigation finally reveals Deferring the Execution of Operations. This suggests that I should use an NSNotification instead, but posted with a NSPostWhenIdle flag. This means getting involved with the cocoa notifications system

The code now ends up looking like this:

  - (void)awakeFromNib
  {
    // Listen out for notification's we've posted to ourselves.
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(expandRoot:)
                                                 name:@"expandRoot"
                                               object:nil];
  }

  -(IBAction)fetch: (id)sender
  {
    // …
    NSNotification* todo = [NSNotification notificationWithName:@"expandRoot"
                                                         object:self];
    [[NSNotificationQueue defaultQueue] enqueueNotification:todo
                                               postingStyle:NSPostWhenIdle];
  }

  - (void)expandRoot:(NSNotification *)notification
  {
    [outlineView expandItem:[[outlineViewDataSource doc] rootElement]];
  }

Which is quite a bit more code. But it feels more robust doing it this way.

The big take away from all this is how difficult it is to use a non-Open-Source framework. If I had the source to Cocoa, I’d be able to look inside and see what I needed to do simply and quickly. Instead, it took me three train journeys. But there’s still enough to like in Cocoa that I intend to carry on.

Categories
Uncategorized

Unit Testing falsehoods

My latest fad is looking at developing Mac apps (in Cocoa). Since the Leopard upgrade, Objective-C 2.0 has Garbage Collection. Congratulations Apple, you’ve lifted it out of the dark ages.

So, I’ve been trying to read up bits in ADC, but my time is limited. This means I turn to podcasts like Late Night Cocoa which I can listen to during my commute.

Today, I was listening to the latest Mac Developer Roundtable, Testing Testing Testing. Neat, I wondered how to do unit testing in Cocoa.

Sadly, the discussion was like asking blind people to critique a painting. None of the participants (save the host, Scotty) actually knew what Unit Testing was, or had ever done any. It was the most ill informed discussion I’ve had the misfortune to hear in a long while.

“asserts are just another form of unit testing.”

“Well, I know my code, so I don’t have to bother with unit testing.”

“Unit testing doesn’t give any value.”

(all paraphrased, but it’s the gist of things).

It’s weird, sort of like jumping back 10 years in time. Like the whole agile thing never happened.

Now, I completely accept that XCode might have terrible support for unit testing. It’s possible, I have’t got that far yet. Likewise, OCUnit might be hard to use. But that doesn’t negate the value of knowing that you’ve fixed something and don’t have to go back to it.

This is particularly surprising, given the high quality of what I’ve seen in Cocoa so far. Apple is big on design patterns. In particular, MVC plays a huge rôle in Cocoa apps. Controllers and Views might well be hard to test, because they’re part of the GUI. But your Model (which is the core of your application) should really be amenable to testing.

I really hope that the rest of the Mac development community isn’t so stuck in the dark ages.