Archive for February, 2011

Conditionally Building Mac App Store Applications to Exclude Sparkle

Wednesday, February 9th, 2011

A lot of developers are in the process of rebuilding their apps for the Mac App Store. One of the guidelines is not using any update mechanism and leaving the updating to the App Store. Like most developers we also use the Sparkle framework to do our non-App Store updates and figuring out how to build a version of your application without Sparkle can be frustrating. Some developers have already solved it in clever ways – our favorite being Gus Mueller from Flying Meat who uses a script to run a find and replace:

Then when my build script is run for App Store builds (-s) it’ll use /usr/bin/sed against Acorn’s project.pbxproj and replace all instances of Sparkle.framework with Noop.framework. Then the automated build takes place, and Noop.framework gets copied in instead of Sparkle.

But we feel there’s still space for another solution on how to conditionally include Sparkle:

1. You should have a configuration for the App Store. It can be created under the project settings “Configurations” tab. You should duplicate your Release configuration for these new settings.

2. Remove Sparkle from being automatically included in all linker calls. To do this remove it from under the Targets–>Link Binaries With Libraries.

3. Under all your regular configurations (Debug, Release, …) add Sparkle to the linker flags. Open the build setting and set “other linker flags” to “-framework Sparkle”.

OTHER_LDFLAGS = -framework Sparkle

4. You now need to remove Sparkle from the Mac App Store application folder after building it. Create a new custom script build phase to run at the end of your build (Custom Package Processing above) and make the script the following (do make sure that the value of $CONFIGURATION matches the name you set  your new configuration for Mac App Store releases):

#!/bin/sh
# Remove Sparkle Framework for MacAppStore
if [ $CONFIGURATION == MacAppStore ]
then
rm -rf "$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/Sparkle.framework"
fi

The above takes care of the physical framework and linking but you still need to remove any code that calls the Sparkle framework. To do so set a variable in the Mac App Store build that you can use in your code to conditionally include Sparkle calls.

5. In your Mac App Store build setting set a flag, we use:

OTHER_CFLAGS = $(OTHER_CFLAGS) -DMacAppStore

6. Encapsulate all  your sparkle code with #ifdef preprocessor directives.

#ifndef MacAppStore
[[SUUpdater sharedUpdater] checkForUpdatesInBackground];
#endif

7. Don’t forget to update your interface if necessary. Remove any menus and check for update preferences. Include the menus programmatically for non-Mac App Store builds at applicationWillFinishLaunching:

#ifndef MacAppStore

// Insert Check For Updates menu
NSMenu *mainMenu = [NSApp mainMenu];
NSMenu *appMenu = [[mainMenu itemAtIndex:0] submenu];
NSMenuItem *uploadMenu = [[[NSMenuItem alloc] init] autorelease];
[uploadMenu setTitle:NSLocalizedStringWithDefaultValue(@"Check for Updates", nil, frameworkBundle, nil, nil)];
[uploadMenu setTarget:self];
[uploadMenu setAction:@selector(checkForUpdate:)];
[appMenu insertItem:uploadMenu atIndex:1];

#endif

The same method can be used for the eSellerate registration engine. The linker flags would be, “-lEWS -lValidateUniversal” and you would remove both the libEWS.a library and the compressed .zip version of the engine. Hoping the following proves useful to a few developers. Code on.