Implementing Growl support in your Cocoa Application

Table of Contents: Implementing Growl Support

  1. Let's Begin
  2. Communicating with Growl
  3. Compatibility Notes
  4. Registering your application with Growl
  5. The Growl Application Bridge delegate
  6. Notification
  7. Including and linking against the Growl framework

Table of Contents for SDK updates

2.0 SDK changes

1.3.1 SDK changes

1.3 SDK changes

Let's Begin

This document is for Cocoa developers planning to implement Growl support in an application. In order to do this, you will need the following:

This document assumes that you have a working knowledge of Xcode, and that you have an already working application to which you would like to add Growl support. As of the 1.3 framework your application will not require Growl to display a notification, we will cover that further in this document.

Communicating with Growl

All your communication with Growl takes place through the Growl Application Bridge, using class methods declared in GrowlApplicationBridge.h. This does not just mean application-to-Growl communication (i.e., notifications); it also means Growl-to-application communication, such as click callbacks. We describe both later in this document.

The process is simple:

  1. Write a dictionary that tells Growl what notifications your application will be able to post, and put it in a plist file in your application's Resources folder.
  2. Add code to post the notifications.
  3. Optional: Implement a Growl Application Bridge delegate to respond to click callbacks.

That's all there is to it!

Compatibility Notes

The Growl 1.2.3 and 1.3 frameworks both deprecate the isInstalled method. Please discontinue use of this method as soon as possible in your application if you are using it. Using this method is unreliable for detection of Growl, and was never recommended. We recommend using the isRunning method instead.

Different versions of the framework have different capabilities, and work with different versions of OS X. The table at this page shows the differences, and also show which versions provide the Mist display built-in.

Registering your application with Growl

In order for Growl to know what notifications your application is able to post, you must register with Growl. This is very easy: simply write a registration dictionary, and place it in a file named “Growl Registration Ticket.growlRegDict” in your application bundle's Resources folder. This file must be in a property-list format, with a dictionary (obviously) as its root element.

When you provide your registration dictionary this way, Growl will detect it automatically when your program launches, by looking in your application's bundle. We call this auto-registration, in contrast to the manual registration that you can do either with a delegate (see next section) or by directly messaging the Growl Application Bridge.

Important: The search for the registration dictionary file is case-sensitive, even on case-insensitive file-systems. You must name the file exactly as shown above, or Growl will not notice it.

These are the keys that you must have in the dictionary:

TicketVersion

The version of the registration-dictionary format you're using. As of Growl 0.7 and 1.1, the current version is 1. The value should be expressed as an integer.

AllNotifications

An array containing the names of all the notifications that your application is able to post.

Other properties allow you to exert finer control over your registration with Growl. The most useful one is:

DefaultNotifications

An array containing the names of all the notifications that are enabled by default.

The Growl Application Bridge delegate

Bug note: As of 1.1, the Growl Application Bridge requires a delegate to be set before you can use it—even if the delegate does not implement any delegate methods. As a workaround, use [GrowlApplicationBridge setGrowlDelegate:@""] to satisfy it.

You can designate an object within your application as responsible for providing Growl with the information it needs to register your application, display notifications, and pass information back to you if the user interacts with a Growl notification. This object must conform to the GrowlApplicationBridgeDelegate protocol.

@interface YourObjectController : NSObject <GrowlApplicationBridgeDelegate>

It may also implement one or more of the methods specified by the GrowlApplicationBridgeDelegate_InformalProtocol informal protocol.

To register, call

[GrowlApplicationBridge setGrowlDelegate:delegateObject];

delegateObject is the object that you want to be your Growl delegate. For basic usage (no click callbacks), you don't need a Growl delegate [see bug note above] you can simply set @"" as your delegate. If you do need a real delegate (e.g., for click callbacks), then in most implementations, you'll probably do this:

[GrowlApplicationBridge setGrowlDelegate:self];

Delegate methods

Since version 0.7 of the Growl frameworks, all of these methods are optional. They will only be called if implemented.

- (NSDictionary *) registrationDictionaryForGrowl;

The returned dictionary gives Growl the complete list of notifications this application will ever send, and it also specifies which notifications should be enabled by default. For most applications, these two arrays can be the same (if all sent notifications should be displayed by default). The NSString objects in these arrays are notification names, and thus will correspond to the notificationName: parameter passed into the +[GrowlApplicationBridge notifyWithTitle::::::::] calls.

The dictionary should have at least 2 key object pairs:

GROWL_NOTIFICATIONS_ALL
An NSArray of all possible names of notifications.
GROWL_NOTIFICATIONS_DEFAULT
An NSArray of notifications enabled by default (either by name, or by index into the GROWL_NOTIFICATIONS_ALL array).

These correspond to the AllNotifications and DefaultNotifications keys described above in “Registration”. All of the keys for registration dictionaries are defined in GrowlDefines.h.

Compatibility note: In versions of Growl before 0.7, this method is required, not optional.

- (NSString *) applicationNameForGrowl;

The name of your application. This name is used both for user display and for internal bookkeeping, so it should clearly identify your application (but it should not be your bundle identifier, because it will be displayed to the user) and it should not change between versions and incarnations (so don't include a version number or "Lite", "Pro", etc.). If this method is not implemented, the executable name specified by your application's Info.plist will be used. It is recommended that you implement this method.

- (NSData *) applicationIconDataForGrowl;

The delegate may return an NSData object to use as the application icon; if this is not implemented or returns nil, the application's own icon is used. This method is not generally needed.

- (void) growlIsReady;

Informs the delegate that Growl (specifically, the GrowlHelperApp) was launched successfully. The application can then take actions with the knowledge that Growl is installed and functional.

- (void) growlNotificationWasClicked:(id)clickContext;

Informs the delegate that a Growl notification was clicked. It is only sent for notifications sent with a non-nil clickContext, so if you want to receive a message when a notification is clicked, clickContext must not be nil when posting the notification. clickContext must be a property list: it must be a dictionary, array, string, data, or number object. Not all displays support click feedback.

- (void) growlNotificationTimedOut:(id)clickContext;

Informs the delegate that a Growl notification timed out. It is only sent for notifications sent with a non-nil clickContext, so if you want to receive a message when a notification timed out, clickContext must not be nil when posting the notification. clickContext must be a property list: it must be a dictionary, array, string, data, or number object. This selector was added in Growl 0.7.

Notification

Posting a notification with Growl is easy. One way is to use the following:

+[GrowlApplicationBridge
notifyWithTitle:(NSString *)title
description:(NSString *)description
notificationName:(NSString *)notificationName
iconData:(NSData *)iconData
priority:(signed int)priority
isSticky:(BOOL)isSticky
clickContext:(id)clickContext]

The arguments are as follows:

title

A title for this notification, such as “File transferred”. Required.

description

A description of the notification, which should provide more detail but should not flood the user with information. Here are some examples of good and bad descriptions:

Good description
“importantFile.zip (4.5 MB) transferred to Fortress.local in less than one second”
Bad description
“The file /Users/thisUser/desktop/importantFile.zip (4718592 bytes) was successfully transferred to Fortress.local in 0.00000143051147460938 seconds at an average of 3298534883328 bytes per second”

Required.

notificationName

A name for this notification. This must match a name which was in the GROWL_NOTIFICATIONS_ALL array in the dictionary returned by your delegate's registrationDictionaryForGrowl method. This name must be human readable, as it will be displayed in the Growl preferences pane. It will not, however, be displayed to the user when the notification is displayed. Required.

iconData

An NSData object which is a representation of an NSImage to be shown with this notification. If it is nil, the default icon for your application will be used. If you want the notification to have no icon, supply an empty NSData ([NSData data]). Optional.

priority

A signed integer value for the priority of this notification. The default value is 0; priority ranges from -2 (“Very Low”) to 2 (“Emergency”). The effect of different priority settings varies by Growl display plugin. Optional; pass 0 for no priority.

isSticky

If YES, the notification will remain on screen until clicked, if supported by the display plugin. The default behavior is for the notification to fade out after a delay. Optional; pass NO for the notification to behave normally, without “stickiness”.

clickContext

If the delegate implements the growlNotificationWasClicked: method (see above), this context will be passed back to the delegate if the notification is clicked. Optional; pass nil to not receive click notifications for this notification.

Another way is to use +[GrowlApplicationBridge notifyWithDictionary:] with an NSDictionary containing keys listed in Growl/GrowlDefines.h.

Other methods

Several other methods are made available by GrowlApplicationBridge, including methods for determining whether Growl is installed, determining whether Growl is running, and for re-registering with Growl if the notifications made available by your application change. These methods are not generally needed, so they are not explained in this tutorial. For more information, please see GrowlApplicationBridge.h.

Including and linking against the Growl framework

The recommended way to use the Growl framework is to link against it in your source folder, and copy it into your app bundle. This section tells you how to set up your Xcode project to do this.

Note: Inclusion of Growl.framework in your application is required.

  1. Download the frameworks from the Downloads page from there.
  2. Copy the Growl framework to your application's project folder (or any subdirectory of it).
  3. Add the Growl framework to your project, making sure that all the relevant target checkboxes are checked. The header files in the framework use UTF-8 encoding.
  4. Add a Copy Files phase to your application's target.
  5. Get Info on the Copy Files phase.
  6. Set the destination to “Frameworks”, with no subpath (clear the field).
  7. Drag the framework from the group tree into the Copy Files phase.

From now on, your application will compile and link using the Growl framework inside its bundle.

If you are using the Growl framework from a loadable bundle on Mac OS X before 10.4, you have to load the framework dynamically from the bundle's Frameworks directory. This is because the dynamic linker resolves @executable_path to the path the of application that hosts your bundle, not the bundle's own path.

Using Cocoa, you can use the following code snipped to load the framework in your bundle:

NSBundle *myBundle = [NSBundle bundleForClass:[MyMainClass class]];
NSString *growlPath = [[myBundle privateFrameworksPath]
	stringByAppendingPathComponent:@"Growl.framework"];
NSBundle *growlBundle = [NSBundle bundleWithPath:growlPath];
if (growlBundle && [growlBundle load]) {
	// Register ourselves as a Growl delegate
	[GrowlApplicationBridge setGrowlDelegate:self];
} else {
	NSLog(@"Could not load Growl.framework");
}

If your application targets Mac OS X 10.4 or higher, you can use @loader_path instead of loading the bundle dynamically.

A previous version of this document gave instructions to set up the project so that Xcode would copy the framework first, then link against the copy. Subsequent tests have shown that the executable resulting from that procedure is identical to the executable resulting from the simpler procedure shown above. Thus the change.

Changes introduced in the 2.0 SDK

The 2.0 SDK provides feature enhancements and bug fixes. The major change is support for Notification Center

Changes in the 2.0 SDK are listed below:

Support for Notification Center

The Growl 2.0 SDK implements support for Notification Center. The idea here is that it should very simple for you to add in support for Notification Center by updating the Framework, and the XPC if you have implemented that. The SDK includes more information about this in the readme

Mist position controller works better.

The Mist positioning controller works much better in the 2.0 framework.

Notification description is now supported by GNTP baked into the framework

Notification descriptions were added back into the framework.

Changes introduced in the 1.3.1 SDK

The 1.3.1 SDK has a few changes to address issues reported with the 1.3 SDK. Please review these changes and the changes in the 1.3 SDK when updating to the newer frameworks.

Changes in the 1.3.1 SDK are listed below:

Introducing MultiGrowl

The 1.3.1 SDK introduces MultiGrowl. MultiGrowl provides a source based example of how to load different versions of the framework based on which OS X version your application is run. MultiGrowl will show you how to for example load the 1.2.3 framework on 10.5, and the 1.3 framework on 10.6. This page lists which versions of the framework are compatible which different OS X versions.

Mist now respects the default notifications list.

In the 1.3 framework, Mist would notify about all notifications in the list. Now Mist will only notify about the default notifications in the list. Notifications which are registered as disabled will not fire with Mist.

The GCDAsyncSocket class has been prefixed to avoid namespace collisions.

In the 1.3 framework, the GCDAsyncSocket class was not prefixed. The 1.3.1 framework addresses this issue. This resolves issue #344

Fixes an issue on machines where the hostname is not set.

In the 1.3 framework, if the hostname was not set then the framework would crash. The 1.3.1 framework addresses this issue.

Changes introduced in the 1.3 SDK

The 1.3 SDK has multiple different changes which are listed below:

Click to go to the section you want to learn about.

Sandboxing in Your Application

The Growl 1.3 Framework helps with sandboxing in 2 ways. First, we have added GNTP support for sending notifications to the local Growl application. However, you will most likely need to justify this to Apple, as they want everyone to justify entitlements in their applications. If you either don't need networking in your core app, or you have already separated out your core app's networking into an XPC to make that justification easier, then we recommend using the XPC based version of GNTP. The XPC version in the new framework is available to make it that much more secure, and it is easier to justify to the reviewers in the app store.

Growl.framework 1.3 still supports sending notes and receiving feedback from Growl 1.2.2. However, in a sandboxed environment, these will not be supported (this support is for running on 10.6 where sandboxing entitlements are ignored, and Growl.app 1.3 will not be available).

If you choose to use the XPC, com.company.application.GNTPClientService.xpc will need to be renamed, and signed by you, it will not need to be recompiled. We will supply one named com.growl.appname.GNTPClientService.xpc which will need to be changed to your app's bundle ID. We provide a script which provides an example of this, gntp-rename-move.rb. This script is what we use internally, so it assumes we are building the XPC everytime, but for your purposes you will only need the rename and resign portion once per version of the XPC.

Example script phase on the main build target (from BeepHammer) for adding the XPC to the bundle:

XPC_START="$BUILT_PRODUCTS_DIR"
XPC_SCRIPT="$SRCROOT/../../scripts/xpc-rename-move.rb"
ruby "$XPC_SCRIPT" "$XPC_START" "$BUILT_PRODUCTS_DIR/$WRAPPER_NAME"
"$CODE_SIGN_IDENTITY"

The first argument is where the original XPC bundle (com.company.application…) lives, second is where its headed to (your application), third argument is your code sign identity.

There is one new delegate method:

- (BOOL) hasNetworkClientEntitlement;

This method is required if you are sandboxed, and going to be using the network client entitlement on the main application instead of the XPC. We could not find a way to detect this well in a sandboxed environment so we are simply are going to ask you. All you have to do here if you are using the network client entitlement is implement this, and return YES, no need for any logic, if we are on 10.6 and sandboxing is ignored, we won't care about this, Growl.app 1.3+ is 10.7+ only, so GNTP is out as a choice anyways.

Introducing Mist

Over the years we have come to the conclusion; thanks in part to a large group of developers separately contacting us, installing Growl without telling end users, or by using the Growl-WithInstaller.framework; that we needed to provide a way to have notifications fire even when Growl is not installed. Growl 1.3 addresses this concern by introducing a fire and forget notification that we are calling Mist.

Mist was developed with the feedback of many developers from different types of applications. Our goal here is to provide the end user with a more cohesive experience. In the past this would have been to prompt the user to install Growl. Growl-WithInstaller.framework was great for the intended purpose, but some developers abused it, some developers had issues with when the installation prompt would come up, and it was a large framework to ship for an optional feature in most of the situations that we encountered. We want to provide a great experience to both end users and developers. Hence the motivation for Mist.

If the end user does not have Growl installed, is on 10.6 or 10.7, and your application has the Growl 1.3 framework, then Mist will fire a notification. This is an opt out solution, so if your application needs to only fire a notification if Growl is running, then you can opt out of Mist entirely.

If you are a developer with an application which already includes/implements Growl support through Growl.framework, then here are the steps to get Mist.

  1. Replace the framework in your application with Growl.framework from at least the Growl 1.3 SDK, or newer if available.
  2. Stop and remove Growl from your system to test Mist.

Note: If your application uses Growl-WithInstaller.framework, then you will need to take more steps in your application. Please contact our Development google group if you have any questions

If your application would benefit from preferences for end users if Growl is not present, it is our recommendation that you provide these.

One last note here. If you need to disable Mist. Say for instance you already have a checkbox for notifications, and you want to be able to toggle them on or off, you can do that. This is how:

[GrowlApplicationBridge setShouldUseBuiltInNotifications:NO];

You can also disable Mist globally if you need to test it, or just do not like it. The following is how to do that:

defaults write -g com.growl.growlframework.mist.enabled -bool YES or NO

We have a ticket on this in case you would like to further review how it works here.

Compatibility with newer versions of Growl

Growl.framework 1.3 is designed to work with both Growl 1.2.2 and older, and Growl 1.3 and higher. The framework will attempt to communicate in multiple ways with Growl to ensure that the notification which your application is sending gets to where it is going.

The SDK removes the Growl-WithInstaller framework.

The 1.3 SDK removes the Growl-WithInstaller.framework due to a number of usability and technical issues. Going forward we recommend instead using the Mist built-in fire and forget display that comes with Growl.framework version 1.3.

The SDK adds a 1.2.3 framework

The 1.3 SDK also includes an update to the 1.2.2 framework. The 1.3 framework does not have PPC or 10.5 support, so we have included this framework as well for those who still need support for 10.5 and PPC. We have versioned this the 1.2.3 framework. It only has 2 changes:

The 1.2.3 framework is included in the 1.3 SDK, under the Legacy directory

The isInstalled method is deprecated.

The 1.3 and 1.2.3 frameworks both will always return YES for the isInstalled method. This method is deprecated going forward. Please discontinue use of this method as soon as possible.

There are multiple reasons for this change: