Please join me at my new location bryankyle.com

Thursday, February 4, 2010

Trials and Tribulations with Growl

A few nights ago I had just finished getting the core piece of a new application I started working on. It occurred to me that my little application and its users would benefit from having Growl support. It was pretty late, midnight or so and I didn't want to be up much later so I just downloaded the framework and had a quick skim of the developer documentation. It seemed pretty simple, but it was late and I knew that if I started that late I'd be up few a few more hours getting no where and end up going to bed feeling defeated. "Tomorrow, " I thought, "I'll do this tomorrow. If its as easy as the docs say then I'll have enough time to add at least 2 other features!".

I got up the next morning feeling great, I knew that come that evening I'd have integrated Growl support. Evening rolled around and I sat down and got to work. I re-read the documentation...well, skimmed is more acurate, and started following the instructions. I created the plist file containing the information that the documentation said I needed. I added a build step to copy the plist to my application's Resources directory. I double checked the name of the file -- the documentation says it's case-sensitive. I linked Growl.framework to my application. I added a build step to copy the framework to my application's Frameworks directory. And lastly I added the code from the documentation to send a notification to Growl ensuring, of course, I was sending one of the notification names that was in my plist file.

I clicked the Build and Run button, and my application compiled and my application's Dock icon started to bounce. By this point I felt like a kid at Christmas. I'd written my letter to Santa, stuffed it in an envelope, and very carefully, in my tidiest printing I wrote the address: "North Pole." I placed my stamp and tossed it in the mailbox. My letter must have gotten there, it hadn't been returned to sender so I knew that good stuff was heading my way.

The Dock icon stopped bouncing and my application's window opened up. "This is it, " I thought to myself "the moment of truth!" I clicked the button to fire off the notification aannnddd...nothing.

Silence.

"That's ok," I thought "it's probably some silly little thing I did wrong."

I looked at my run logs and they were eerily quiet. No messages at all. Surely if I'd done something wrong the framework would tell me. That must mean that the framework didn't get copied to my application's bundle. But if that's the case, then shouldn't there have been an error in the log about my code trying to perform a selector on a class that didn't exist? So that must mean that my code that posts the notification was never called. This sounds like a job for Captain Breakpoint!

I placed a breakpoint on the first line of my method and started the application again. Application startup under the debugger always takes longer so I was caught off guard when my application's window opened up. I moved my mouse cursor over the button, knowing that I was one mouse click away from finding out that my code wasn't being called thereby giving me all the information I'd need to fix the problem. My eyes narrowed as I pressed the button.

Half a second later I was staring at the Xcode debugger, my line with the breakpoint highlighted and a stack trace showing me that execution was indeed stopped in my method. I felt totally disarmed like I'd just gathered up my courage to tell that guy what I really thought of him only to be knocked off my feet by an overpowering wall of cheap cologne.

"What...the...what?"

I gingerly stepped through the method stopping just shy of executing the line to post the notification. Pausing for a second to think about what could possibly happen next. If the debugger tosses me out of my method, I know that either the framework doesn't exist or my parameters are causing it to barf. If the debugger stops and the next statement in this method...well that doesn't sound easy to debug, so lets hope that doesn't happen. I stepped over the line and found myself looking back that beautiful line of code just after the call into the framework.

This doesn't make sense. The framework accepted my parameters without problem, ostensibly did something with them and succeeded. So what could it possibly be? The framework didn't log anything, so it must not be a problem with my code. But no notification showed up, so the problem must be that Growl just isn't running. At that moment notification showed up in Growl -- but not from my application.

"Well, if other apps can send notifications then it's gotta be something I'm doing wrong." Since the documentation told me that all I needed to do was create a plist file and make sure it was in my application's Resources folder I figured I'd double check, and sure enough it was there. I'd just been banished to developer's purgatory where I have to debug configuration..

I thought to myself, "If my application has to have one of these plists, then other apps must have to have them too!" The first few applications I found didn't have one of those plists and they were working just fine. Finally I found an application that had a plist. So I compared them to make sure I'd done it right and sure enough I found a problem with my configuration. I updated my file, saved, built and ran the app again. My application's window popped up and I clicked the button and this time...nothing happened...again.

Silence.

Not believeing that it was something that I'd done I cleaned, rebuilt, and ran the application a few times knowing that it shouldn't make any difference in the world but hoping that it would just magically start working. After several runs in vain I figured that maybe The Google would have an answer for me. Sure enough, after trying a few google-y incantations I found some source code buried on a page that showed how to initialize Growl implementing a delegate method. I modified my code in a similar way, saved and ran again. I clicked the button and was surprised to see my little notification appear on the screen - only an hour after I started.


Truth be told, the Growl documentation did mention that one could would have to implement a delegate if you were attempting to talk to Growl 0.7, but since my code was running against 1.2 I didn't think I'd need to. In any case I managed to get it to work, but not without some frustrations that could have very easily been avoided had the Growl team produced some other artifacts that were as easy to find as the developer's documentation.

The first of these is some sort of a quick-start guide that just provides the bare minimum steps that are needed to use the framework. The developer's guide was littered with documentation about old versions. I don't need to know that stuff. Just assume that I have the latest and tell me exactly what I need to do.

Another thing that would have really helped, especially for developers that like to get dirty is a downloadable example. I'm sure they have one somewhere, but I wasn't linked to from their documentation so it effectively didn't exist.

Last lastly, but certainly not least is some level of logging in the framework itself. This would have been immensely helpful since I'd be able to tell if the framework was finding my plist or not, or whether there was a problem with my parameters. Don't just leave me to figure it out on my own, give me some help!

I hope what I'm saying doesn't label me an Open Source Douchebag. Honestly, I'm really just trying to improve the project by making it easier to get people up to speed. And in the next few days (hopefully) I'm planning on uploading some sample code along with a quick-start blog post.