After my first apps are now available on the Mac App Store, I wanted to prepare trials versions of these apps (something I should have done before launch, I know). Especially, I wanted to have some code ready to drop in any app to create a trial version and I surely didn’t want to maintain two separate code versions (or targets) for the Mac App Store version and the trial version. The following is a how-to for myself and might be useful also for you, fellow developers.

First: Create and Edit Trial Version Build Configurations

Yes, I’m working with Xcode 4 and my existing body of code is the code of the Mac App Store version of my app. To this code I will add the trial version code. To start, create new build configurations. So far, there should be a debug and a release build configuration. Open the project info window by clicking on the blue icon in the navigator area, then on the entry under the heading ‘Project’ and finally on the info tab. In the ‘configurations’ section there is a ‘+’ button to manage the build configurations. Duplicate both of the existing configurations and rename the duplicates as ‘Trial Debug’ and ‘Trial Release’.

Duplicate Configurations

These new build configurations will get some tweaks. Click on the build settings tab and change the ‘product name’ in the packaging section to indicate that the trial version build is a trial. In the same section, set the ‘Info.plist Preprocessor Definitions’ for these build configurations to something, let’s say ‘TRIAL’. This allows Xcode to know when you’re building the Mac App Store version and when you’re building the trial version, which is useful in order to tweak the Info.plist file. Finally, in the ‘LLVM GCC 4.2 – Preprocessing’ section define ‘Preprocessor Macros’ for the new build configurations and also set them to ‘TRIAL’. By doing this you can use conditional compiling in the source code.

Prepare Configurations

Second: Create a Trial Version Scheme

Now create a new scheme (this is new to Xcode 4, consider it like a summary of build options, read Martin Pilkington on this) by clicking Product/Edit Scheme and then click on the ‘Duplicate Scheme’ button. Name this new scheme ‘trial version’ or something like that. Tell Xcode to use the modified build configurations (debug and/or release) with this scheme, and, if you also change the product name, also the modified (i.e. renamed) executable, as otherwise running the app from within Xcode won’t work.

Edit Schemes

Now, if you want to build the Mac App Store version of my app, select the original scheme in the Xcode main toolbar. If you want to build the trial version, select the duplicated scheme referring to the modified build configuration.

Third: Edit Info.plist file

So far for the preparatory configuration, now to the true changes between the two build versions, which will be contained in the Info.plist file and in the source code. Right-click on the info.plist file in the navigation area and open it (“Open As”) in source code view (instead of the usual plist view). Then add the following key-value combination somewhere at the end of the file:

#ifdef TRIAL
 CFBundleIdentifier
 com.yourcompany.yourapptrial
#endif

This is just an example on how to modify the contents of the Info.plist file [Update: Even better probably to have two different Info.plist files, set their respective names in the packaging build settings]. The code example above is only put in the build product if you build the trial version (by having selected the newly created trial build scheme, see above). To have a different bundle identifier for the trial version is useful as the Mac App Store source code (compiled only into the Mac App Store version) can then check for the trial version and ask if the user wants to delete it. Another possibility is that the trial version checks for the Mac App Store version present and ask the user the same (read Craig Hockenberry on this, who prefers to create a separate target for each version).

Fourth: Add Trial Version Code

In the source code, you now introduce conditional trial version code, which I currently do like this in the AppDelegate [Update April 21, 2012: Previously, I recommended having the following code snippet in the -awakeFromNib method. It turned out that there are issues with the application menu if you do so, therefore I moved the code to the -applicationDidFinishLaunching: method and everything works as it should]:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{
    #ifdef TRIAL
        if ([self checkIfExpired]) {
        // expired
        [self showExpiredMessage];
    } else {
        // not expired
        [self showRemainingTrialMessage];
    }
    #endif
}

This code is compiled into the target only if the trial version is built, not if the Mac App Store version is built. It simply checks if the trial is expired or not. If it is, showExpiredMessage shows a notice window and quits, if it isn’t, showRemainingTrialMessage shows a short notice with the remaining trial period. These notices also contain links to open the Mac App Store website in case the user wants to buy the full version. Some more internal methods (not visible in the snippet above) handle storing and retrieving information on the trial period.

I have put all the methods mentioned above in an AppDelegate category so I can drop the category in any of my projects and it will just compile. My way to store the information about the trial period and its expiration is pretty straightforward and by far not bullet-proof. People who want to crack it will likely (and easily) succeed, especially after studying the source code. However, I trust that there are enough honest people out there.