OSX Uninstallers the Easy Way

Back in the old days at LimeWire one of the many tasks I took on was building the installers for all platforms, and I’ve carried on my installer hacking with LittleShoot. While installers are anything but glamorous, I’m oddly obsessed with them I think because they’re your users’ first introduction to your program. They need to be simple, and they need to work.

One of Mac’s quirks has always been a lack of uninstallers, leading to widespread global frustration, decreased productivity, and some say a leading cause of the recent financial crisis (a small minority).

Salvation is at hand.  LittleShoot’s uninstaller is ridiculously simple. It’s a little snippet of AppleScript that just runs a bash file loaded into it’s application bundle using “do shell script” to run an external script file. Within this framework, your entire uninstaller is basically a bash script. Here are the steps to get this working:

  1. Copy the following AppleScript into a file called ‘uninstaller.scpt’ and open it in Script Editor. You can also save the binary script from our SVN here and open it in Script Editor directly (easier).
  2. on onConfirmUninstall()
    set applicationName to “LittleShoot”
    try
    display dialog “Are you sure you want to uninstall ” & applicationName & “?”
    set uninstallScript to quoted form of POSIX path of (path to resource “uninstall.bash”)
    do shell script “bash ” & uninstallScript with administrator privileges
    display dialog “Successfully Uninstalled ” & applicationName buttons {“OK”} default button “OK”

    on error err
    if err contains “User canceled” then
    display dialog “Canceled ” & applicationName & ” Uninstall” buttons {“OK”} default button “OK”
    else
    display dialog “We’re sorry, but there was an error uninstalling ” & applicationName & ” described as: ” & err buttons {“OK”} default button “OK”
    end if
    end try
    end onConfirmUninstall

    onConfirmUninstall()

  3. Change the line ‘set applicationName to “LittleShoot”‘ to ‘set applicationName to “[your application name]”‘
  4. Choose File->Save As… in AppleScript Editor and save this script as an application bundle that’s run only with no startup screen (the options at the bottom when you choose Save As…). 
  5. Navigate via the Terminal to where you saved the bundle and cd into the “Contents/Resources” directory. In our case that’s “LittleShootUninstaller.app/Contents/Resources,” so it’s Contents/Resources within the app bundle.
  6. Create an uninstall.bash file in Contents/Resources. As you can see in the AppleScript above, that’s the file the AppleScript looks for and executes.
  7. Put whatever you need in uninstall.bash to uninstall your application. The script will run with administrator privileges, so you can really do whatever you want here. Here’s the LittleShoot uninstall script to get you going, although this is a little quirky because we use things like launchd that most applications don’t use. You can also grab this directly from our SVN here.
  8. #!/usr/bin/env bash 

    function die()
    {
      echo $*
      exit 1
    }

    function cleanAndDie()
    {
      clean
      die “LittleShoot is already uninstalled”
    }

    function clean()
    {
      local plist=~/Library/LaunchAgents/org.lastbamboo.littleshoot.plist
      test -f $plist && launchctl unload $plist
      rm -rf ~/Applications/LittleShoot.app
      rm -f ~/Library/LaunchAgents/org.lastbamboo.littleshoot.plist
      rm -rf /Library/Receipts/littleshoot.pkg
      rm -rf ~/Library/Receipts/littleshoot.pkg
      rm -rf ~/.littleshoot
      rm -rf ~/Applications/LittleShootUninstaller.app
    }

    function remove()
    {
      rm -rf $1 || die “Could not remove file: $1”
    }

    # If it looks like we’ve already uninstalled, just make sure to remove everything again and die.
    test -e ~/Applications/LittleShoot.app || cleanAndDie

    launchctl stop org.lastbamboo.littleshoot || die “Could not stop LittleShoot”
    launchctl unload ~/Library/LaunchAgents/org.lastbamboo.littleshoot.plist || die “Could not unload”
    rm -rf ~/Applications/LittleShoot.app || die “Could not remove LittleShoot”
    rm -f ~/Library/LaunchAgents/org.lastbamboo.littleshoot.plist || die “Could not remove plist”

    # We go through all this because the package file is placed differently on Tiger, Leopard, etc.
    globalReceipt=/Library/Receipts/littleshoot.pkg
    userReceipt=~/Library/Receipts/littleshoot.pkg
    test -e $globalReceipt && remove $globalReceipt
    test -e $localReceipt && remove $localReceipt
    rm -rf ~/.littleshoot || die “Could not remove LittleShoot config folder”
    rm -rf ~/Applications/LittleShootUninstaller.app || die “Could not remove LittleShoot uninstaller”

 

So that’s about all she wrote. You basically just have to set up the uninstaller AppleScript application bundle, and then you can just edit your bash script from then on. The application bundle will use whatever script is stored in its Contents/Resources/uninstall.bash file. I’ve played around with a lot of different options for doing this, and the straight script file approach kills the other options in terms of maintainability and flexibility. Note that if the uninstaller encounters an error, the AppleScript will display a dialog to the user with anything your script has echoed, so make those errors informative. You also of course have to include the uninstaller application bundle in your installer.

In the end, your users have a simple uninstaller they can just double click on, and you have a really easy way to write and maintain your uninstaller code.

Back to allowing you to post LittleShoot files to Twitter…

Advertisements

One Response to OSX Uninstallers the Easy Way

  1. Nice try. I would really like to think there’s now an Applescript way of uninstalling, but there’s an error on second line …

    Syntax error
    “Expected expression but found unknown token.”

    Anyone else have this problem?
    Adrian

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: