Blog

Ml Growl

Using Growl in a sandboxed app

4

I am working on a new version of ShelfMenu and, to be able to release it on the Mac App Store, I had to adapt it to a sandboxed environment. ShelfMenu uses Growl for notifications, and I do not want to change this, especially since the new Growl framework works with OS X 10.6 onwards, gracefully falling back to the Notification Center on 10.8, if you do not have Growl running.

Growl expects its development framework, included in your application, to communicate with the Growl service using local networking. This is not allowed by default in a sandboxed application, unless you request specific entitlements (making it more challenging to get the app approved by Apple). So, if you don’t need to add networking to your application, it’s better to use an XPC service, as recommended in the Growl documentation. This is explained in the Growl documentation in a synthetic way, here is what you need to do in some more details.

As explained in the documentation, the best way to use an XPC service to communicate with Growl, is to take the XPC bundle provided in the Growl framework (which is already compiled), rename it, sign it with your developer certificate, and copy it into your app bundle. This XPC already includes the necessary entitlements.

If you download the latest Growl SDK (2.0 at the time of writing), you will need two things:

  • XPC Client/com.company.application.GNTPClientService.xpc
  • XPC Client/xpc-rename-move.rb

I have stored these items at the root of my application (one level above the Xcode project), where I keep all application assets (icons master files, graphics, and anything not needed by Xcode to build my project). This is the root level I commit to my SVC (git in case you wonder what I use).

In the screenshot, com.cemacsoft.ShelfMenu.GNTPClientService.xpc is created during the build process by a script (see below). You need not to worry about this.

Next you need to add a step to the compile process, in Xcode. Easier seen than explained, have a look at the following screenshot.

In step 4, the script comes from the Growl documentation, with a change to the location of the original XPC (com.company.application.GNTPClientService.xpc) to match my config; setting it to “..” as in the screenshot will work if you store the XPC one level above your project, as I did.

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

Hope this saves you some time.

  1. Oleg
    Oleg05-09-2013

    Great post, thanks! Growl’s own documentation is very confusing, so your post was of much help to me. There were however a few stumbling points not covered:

    - not quite clear whether I should include the com.company.application.GNTPClientService.xpc bundle to my Xcode project so it gets copied as a resource, or make an additional “Run Script” build phase that will copy it before running Growl’s ruby script. (Answer: no, the ruby script copies the renamed and signed XPC bundle to the app bundle)

    - the ruby script doesn’t support spaces in paths, so make sure your configuration names do not contain spaces

    - codesign failed to sign the XPC bundle until I used Xcode Preferences->Updates to download updated command-line tools and restart Xcode and clean the project.

    - One question that is completely uncovered though : ok, now the XPC service is running. But how to use it? How to actually send messages to Growl via XPC? I don’t seem to find any docs at all on Growl’s own site.

  2. Oleg
    Oleg05-09-2013

    Oops, it seems that normal calls to Growl just works after the XPC is up and running :) Well, they should have made that clear explicitly in their docs.

  3. Dmitry
    Dmitry11-22-2013

    Hi! I think I’m missed something. Can you provide me some more info about XPC integration? For now I have Shell Script Invocation Error:

    /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:1262:in `copy’: unknown file type: ../com.mycomp.App.GNTPClientService.xpc (RuntimeError)
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:452:in `copy_entry’
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:1331:in `traverse’
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:449:in `copy_entry’
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:424:in `cp_r’
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:1402:in `fu_each_src_dest’
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:1418:in `fu_each_src_dest0′
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:1400:in `fu_each_src_dest’
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:423:in `cp_r’
    from /Volumes/Storage/Work/Deveopment/Groovetape/Dev/../XPC/xpc-rename-move.rb:124:in `main’
    from /Volumes/Storage/Work/Deveopment/Groovetape/Dev/../XPC/xpc-rename-move.rb:128
    No bundle named com.company.application.GNTPClientService.xpc found
    Command /bin/sh failed with exit code 1

    What I do.
    1. Rename com.company.application.GNTPClientService.xpc to com.mycomp.App.GNTPClientService.xpc and copy both files to Application’s folder

    2. Then, I code sign this folder with codesign -s “MyName” path to com.mycomp.App.GNTPClientService.xpc

    Then I use your tutorial and add script to Build Phase.

    Thank you!

    • Dmitry
      Dmitry11-22-2013

      Opps! Sorry for previous comment. It’s just some misunderstanding. If both xpc folder and ruby script laying in folder named FOLDER I need XPC_START = “../FOLDER/” and XPC_SCRIPT = “XPC_SCRIPT=”$SRCROOT/../FOLDER/xpc-rename-move.rb”. Also no need to rename com.company to something — the script do it by self. For now I have another problem with it:

      /Volumes/Storage/Work/Deveopment/App/Dev/../XPC/xpc-rename-move.rb:73:in `+’: can’t convert nil into String (TypeError)
      from /Volumes/Storage/Work/Deveopment/App/Dev/../XPC/xpc-rename-move.rb:73:in `resign’
      from /Volumes/Storage/Work/Deveopment/App/Dev/../XPC/xpc-rename-move.rb:111:in `main’
      from /Volumes/Storage/Work/Deveopment/App/Dev/../XPC/xpc-rename-move.rb:128
      Creating com.comp.App.GNTPClientService.xpc from com.company.application.GNTPClientService.xpc
      Command /bin/sh failed with exit code 1

      Thank you

    Leave a Reply

    Thanks for the visit, please come back again