Source code modifications
In the previous installment of Preparing for Mac App Store Submission, I provided some guidelines for application data and resources that should be followed (where applicable) as you transform an existing Mac OS X game project into a project that can be successfully submitted to the Mac App Store (MAS).
In this fourth part, I will describe a number of modifications to the source code of your project that you may want to make to avoid unnecessary MAS rejections and, perhaps, improve your customer support and project maintenance. These recommendations build on the theme of earlier posts, as most of these changes are due to Apple restrictions against anything having to do with downloading or external sales, including the use of registration codes.
As before, these suggestions are based solely on our experience with our products, so it could help to understand the basics of our sales system. We have two separate downloadable builds for each Mac OS X product: one is a trial version, with limitations, and the other is a full version, which is unrestricted but requires a customer-specific registration code (as well as the hidden download location). In order to better understand our customers, each web link in the program adds a very basic tracking code, so we know which version is being used; when online high scores are submitted, we include a hash of the registration code so the scores can be applied to the correct player account.
For this process, we have created a new store version, which is unrestricted (like the full version), includes available extra data (avoiding downloads), and cannot require a registration code. (Instead, the Mac App Store automatically includes a “receipt” which should be checked, as discussed in the next installment.) Here are the steps we took…
1. Store game data within user library folder
For the Mac App Store, game data that is not directly created by the user must be stored within the user library (not documents) folder. Originally, we stored our saved games and statistics in ‘~/Documents/<project name>/<player>’, which made them easily accessible for our customers, but MAS guidelines (and an explicit rejection) required us to relocate such files to ‘~/Library/<project name>/<player>’, where they are less discoverable by users, especially since ‘~/Library’ is now hidden in Finder. In our case, we actually relocated the files for all SKUs, including a function in the downloadable versions to automatically copy this game data to the new location, to make future customer support more manageable.
Note that, with application sandboxing (as discussed in the final installment), the actual location of the (sandboxed) user library is a significantly longer path name, and the main user documents folder is not accessible by default. Obviously, the paths given above are illustrative only; one should never hard-code a path in project source code.
2. Remove all registration code via conditionals
Since any sort of registration code is forbidden, you should remove all registration code functionality via conditional compilation. As part of our Project modifications earlier, we added an APPSTORE preprocessor variable to the store version target (only), which now allows us to remove sections of code with “#ifndef APPSTORE”/”#endif” sections.
In our case, every place in the code where the trial and full versions were different were already denoted with the preprocessor variable ‘DEMO’, so we only needed to look at each such section and decide if it should be like the full version (i.e., no limitations), which was the case most of the time, or like the trial version (i.e., no registration codes).
3. Add fixed internal MAS registration code
Because we used registration codes to differentiate between customers, we needed to add a fixed internal MAS registration code for all store version customers. In order to replicate the previous functionality for online high score submissions, we needed a way to distinguish among different users without accessing any individually identifiable user information. Alas, in the end, we had to provide a hash of some system information that was reasonably unique, and simply accept the possibility of a certain number of collisions. (Thus far, MAS sales have not been high enough to get to levels where collisions are likely, though.)
Honestly, there is a possibility that information from the included MAS receipt could have provided a better (unique) customer identifier, but our implementation (above) was completed before receipt checking was considered. In this case, that is left as an exercise for the reader (but comments on implementation are encouraged).
4. Update version tracking codes
For the store version, you should update any version tracking codes in your project, using conditional compilation (APPSTORE) where necessary. This will allow any usage statistics to differentiate between downloadable and MAS customers, as well as from trial version users (i.e., potential customers).
In our case, we simply add a character to the end of link URLs (i.e., as part of a single GET variable) that makes this distinction, so we just added another legal value for MAS purchases. Additionally, our shortened version numbers are slightly different so (savvy) players can identify the product played on the online high score pages (for example, on this FreeCell scores page, see the ‘Product’ column).
5. Remove or modify marketing screens
Consider whether you should modify or remove any marketing screens in your product. If you have dialog boxes which have the occasional link to ordering pages or downloadable content, you will need to remove those links (and any referencing text); if you have dialogs that are explicitly for the purpose of marketing, it may make sense to remove them entirely instead.
In the case of our products, we include a fairly self-explanatory ‘Help Center‘ screen which not only contains links to the help files for the product, but also serves as a portal to all manner of support services, including sales (verboten), registration code entry (not allowed), customer support email (probably OK), and suggestions for other games (who knows?). We found that removing the disallowed and questionable content from these pages made them almost pointless, so we instead replaced the entire dialog with a direct link to the help file contents page (within the bundle).
6. Remove links to any downloadable content
You must remove any links to downloadable content or risk the rejection of your product. Note that these links (and, in truth, a number of other “offenses”) may not always be obvious on initial review, so your product can certainly get accepted once and then later rejected for something that was there in the first version; it has happened to us more than once. (All Apple reviewers are not created equal.)
In the case of Pretty Good Solitaire, we have card sets which customers can freely download, plus a feature to check for and download updates, both of which are forbidden in the Mac App Store. The following subsections describe the issues that we had to address to prevent suggesting that there was anything outside the MAS playground.
6a. Remove download links from main menus
We had to remove our ‘Download Additional Card Sets‘ menu option from both the main and game menus.
6b. Remove version checking from main menus
We had to remove our ‘Download Latest Version‘ menu option, which includes an implicit version check, from the both the main and game menus.
6c. Remove download links from submenus
We had to also remove a ‘Download Additional Card Sets‘ menu option from our submenu that players use to select the desired card set (a logical and convenient place to find it).
6d. Remove download links from buttons
Finally, we had a ‘Download Additional Card Sets‘ button within the program preferences, which also had to be removed (actually, hidden).
Note: In all of these cases, rather than modify our user interface (.nib) files, and thus have to manage different sets of files, we added conditional code that simply removed the offending menu option or hid the button, keeping the source code (including .nib files) identical between downloadable and store versions (distinguished solely by the presence/absence of APPSTORE).
7. Review any use of the Application Support folder
Before submitting your application to MAS, carefully review your use of the Application Support folder (if any). Specifically, you are allowed to create and (appropriately) use a folder at ‘~/Library/Application Support/<app-identifier>’, where <app-identifier> “can be your application’s bundle identifier, its name, or your company’s name.”
However, there is a twist: “These strings must match what you provided through iTunes Connect for this application.” This particular requirement is somewhat buried in the guidelines, and it is not always checked.
In our case, we inadvertently created but did not use (in the store version) a folder, ‘~/Library/Application Support/Goodsol’, which seems to fit the criteria. However, it was supposed to end in ‘Goodsol Development’ to meet the letter of this requirement. This “violation” was not discovered until our third or fourth submission, and our downloadable versions already used the folder extensively as named. Fortunately, the store version did not actually use it (by virtue of including all downloadable content), so we just removed the spurious creation of an empty folder. Problem solved (albeit with yet another rejection and delay).
Conclusion
With the above modifications to source code, plus perhaps a little detective work finding similar issues with any particular application, your project should be tentatively ready for submission to the Mac App Store. In the next installment, Part 5: Mac App Store receipt validation, I will discuss the reasons that you should probably validate receipts in your shipping product and provide a few links to useful code and resources.