2

I'm working on a small wrapper for the Growl 1.3.1 SDK. More specifically, I'd like to package Growl in my application so that even if the user doesn't have Growl, they will still be able to get notifications. I previously had Growl installed and my code would fire a notification. I have since uninstalled Growl and am using just the framework; Mist, I believe it is called. However, when I launch the code now (that Growl is uninstalled), no notification is fired! Below is the code I am currently working with:

#import "growlwrapper.h"

void showGrowlMessage(std::string title, std::string desc) {
    std::cout << "[Growl] showGrowlMessage() called." << std::endl;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [GrowlApplicationBridge setGrowlDelegate: @""];
    [GrowlApplicationBridge
        notifyWithTitle: [NSString stringWithUTF8String:title.c_str()]
        description: [NSString stringWithUTF8String:desc.c_str()]
        notificationName: @"Upload"
        iconData: nil
        priority: 0
        isSticky: NO
        clickContext: nil
    ];
    [pool drain];
}

int main() {
    showGrowlMessage("Hello World!", "This is a test of the growl system");
    return 0;
}

I also have the appropriate Growl Registration dictionary, and am compiling with:

g++ growlwrapper.mm -framework Growl -framework Foundation -o growltest

Is there anything wrong with this code? Any ideas why it wouldn't be firing?


Edit: Seems the code above is working just fine. Just needed to be in a run loop, with the appropriate Growl dictionary stuff.

Julio
  • 2,261
  • 4
  • 30
  • 56
  • I'm not sure, but when I was using Growl, the Growl bridge would only TRY to communicate with Growl service to show notification. If it didn't find it, it won't show notification by itself. So if you don't have Growl installed no notifications at all would be shown. – Kamil Klimek Jan 16 '12 at 10:23
  • Ok, I can see that it is 1.3SDK feature to show notification without Growl installed – Kamil Klimek Jan 16 '12 at 10:25

1 Answers1

2

I'm not an authority on Growl, but I have a pretty good hunch: When the Growl app is installed, a one-shot notification like this has a prayer to work, because the running app has a run loop and can drive UI from it. In the example you have here, there's no run loop, so there's no way for this one-shot app to ever draw any notifications -- it's dead before it even has a chance. I would guess if you made a boilerplate Cocoa app, and then called showGrowlMessage from applicationDidFinishLaunching:, but before you terminated/quit the app, I bet it would work. At the very least you should give that a try.

EDIT: If you create a new Cocoa non-document application, and add the following methods to the appDelegate class, it will successfully display a notification using the Mist (i.e. in-app) Growl.

@implementation SOAppDelegate

@synthesize window = _window;

- (void)showGrowlMessageTitled: (NSString*)title description:(NSString*) desc
{
    NSLog(@"[Growl] showGrowlMessage() called.");
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [GrowlApplicationBridge notifyWithTitle: title
                                description: desc
                           notificationName: @"Upload"
                                   iconData: nil
                                   priority: 0
                                   isSticky: NO
                               clickContext: nil];
    [pool drain];
}


- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    [GrowlApplicationBridge setGrowlDelegate: (NSObject<GrowlApplicationBridgeDelegate>*)self];
    [self showGrowlMessageTitled: @"Foo" description: @"Bar"];    
}

- (NSDictionary *) registrationDictionaryForGrowl
{
    return [NSDictionary dictionaryWithObjectsAndKeys: 
            [NSArray arrayWithObject: @"Upload"], GROWL_NOTIFICATIONS_ALL,
            [NSArray arrayWithObject: @"Upload"], GROWL_NOTIFICATIONS_DEFAULT,
            nil];
}

@end

So, in short, the problem with the original code was not only the runLoop problem, but that it was not passing a real delegate (i.e. object that implements the delegate methods described in the headers as required) to the GrowlApplicationBridge (it passes an empty string). You definitely still need a runLoop, but that's not all -- there's additional, non-optional setup for using this framework.

ipmcc
  • 29,581
  • 5
  • 84
  • 147
  • No, this code should work, but in my opinion it's that autoreleasepool autoreleasing before the message is broadcasted. – Aditya Vaidyam Jan 16 '12 at 04:21
  • @Galaxas0 could you elaborate? The code won't compile without the pool . . . I'll look into the applicationDidFinishLaunching and see if that sorts things out as well! – Julio Jan 16 '12 at 04:54
  • It has to do with Objective-C memory management. I suggest using -performSelectorInBackground: and write a simple cocoa method (assuming you're in Objective-C++ and with a .mm filename) and have your C++ method call the cocoa method. The Pool is releasing the object faster than you're sending it to Growl is my assumption. – Aditya Vaidyam Jan 16 '12 at 04:57
  • @Galaxas0 I've been googling for a bit, but can't really find any good docs or examples on performSelectorInBackground. Mind pointing me in the right direction as far as code goes? – Julio Jan 16 '12 at 06:28
  • Sure! http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html It's in the list there. – Aditya Vaidyam Jan 16 '12 at 06:30
  • Note: this could also be an error with Mist.framework's code for locating the growl libraries. It is also worth noting that Growl's website explicitly says to not package the app with a third party app, and to allow the user to install it themselves. – Aditya Vaidyam Jan 16 '12 at 06:31
  • It looks like mist requires run loop as ipmcc said: http://code.google.com/p/growl/source/browse/Framework/Source/GrowlApplicationBridge.m#291 as mist is dispatched async – Kamil Klimek Jan 16 '12 at 11:22
  • Yup. dispatch_async to the "main queue" requires there to be a main thread run loop. @KamilKlimek's link is the smoking gun. – ipmcc Jan 16 '12 at 11:59
  • Hm, does it matter that I am using this in a GUI (which I presume has a run loop) and it's still not firing? – Julio Jan 16 '12 at 14:48
  • What does "in a GUI" mean? The example you posted here shows your app consisting of `main()` entry point calling this function and then immediately exiting. That's not a "in a GUI" by any measure, and does not have a run loop. – ipmcc Jan 16 '12 at 16:23
  • I coded this up, outside of the GUI, to get an idea of how Growl worked and to ensure that my code was correct. Point being, this code also does not run in my GUI, which I understand has a run loop and thus should sidestep the premature termination I believe? – Julio Jan 16 '12 at 16:47
  • I have edited my answer to provide a trivial example that successfully displays a notification. – ipmcc Jan 17 '12 at 01:01