A bit over a month ago [1], I decided it was time to create a “lite” version of Flower Garden [2]. I really thought it was going to be a pretty easy task: Disable a few pots, remove some seeds, and repackage it. Sounds simple enough. But before that, I had to decide how to structure the project for the lite version.
#defining The Problem
The first approach that came to mind was to use #ifdefs. Hopefully, if I did things well, it wouldn’t require too many of them. As soon as I gave it some more thinking, it was clear this was a pretty flawed approach.
I like to be able to create a build with zero human interaction: Press a button, wait, and the build is ready. I don’t want to have to do any manual (read, error-prone) steps. So I couldn’t just have an #ifdef that I turned on and off depending on whether I was building the lite version or the full one. I can get around that by creating a new configuration in XCode, except that it wouldn’t be one, it would be one for every existing configuration I already have: Debug, Release, Distribution Ad Hoc, and Distribution App Store. So this would make a total of 8 configurations. That’s rather ugly!
In case the explosion of configurations wasn’t ugly enough, there was one other, even bigger problem: An #ifdef wouldn’t allow me to change the assets included in each version of Flower Garden, and, most importantly, I couldn’t change the Info.plist file to have a different bundle id (although maybe I could change the Info.plist for each of the new configurations and point it to the new one).
Not an attractive option. So how else can we do it?
New XCode Project
The idea is straightforward: Make a copy of the existing project for the full version of the game, and change it in any way you want: Remove resources, add new ones, change Info.plist, etc. You can still share common source files and define a preprocessor macro LITE in the new project to #ifdef parts of the code.
This approach sounds more promising, until you realize that changes made to one project don’t carry over to the other one. So if you’re done developing the full version, and you create the lite version at the very end, maybe that won’t be a problem (although, how can you be sure you’re done?) And making changes to two separate projects is never fun.
You could improve things a bit by starting out the lite version as a branch in source control. That way, changes made to one of the branches can (hopefully) be merged back into the main line or another branch (have fun merging xcodeproj files). I think it’s because I’ve had the displeasure of working in projects that went totally overboard branching, that just the word branch makes me recoil in disgust. Yes, this method might work, but it seemed cumbersome and error prone.
Any other solutions?
Stay On Target
It turns out I kept thinking in terms of a solution I could have implemented in Visual Studio (that’s what I get for spending so many years working with it). But fortunately we’re not in Windows-land anymore. This is XCode, and XCode totally rocks.
XCode’s build system, once you wrap your head around it, is much more flexible and natural than Visual Studio’s. It’s much more like a make file (and I say that in a good way–I do like make files!). You have files, and then you have targets. And there’s nothing that says a file can’t be in multiple targets (and the file only appears in the project once). Once you have multiple target, whenever you add a new file you have the option to add it to both targets the same time if you want. Perfect!
My Flower Garden project contains about 15 targets: 7 “engine” libraries, 7 test executables for each of the libraries, and one main executable target for the game itself. So the (now obvious) solution, was to simply create a new target for the lite version, in that same project, with the same files. It turns out this solution had all the advantages of the previous approaches with none of the drawbacks.
Lite Target Details
Once I landed on the target idea, it really was quite straightforward to create the Lite version by following a few, simple steps:
- Make a duplicate of the target for the full version. Just right click on the full target, and select duplicate.
- Rename new target to something else (Flower Garden Lite–I’m highly imaginative when it comes to naming things like this! 🙂
- Make a duplicate of your Info.plist (InfoLite.plist)
- Change the info.plist file setting for the lite target to point to InfoLite.plist
- Edit InfoLite.plist to have a different bundle identifier and maybe a different bundle display name (the name that’s going to show up under the icon on the iPhone).
- Add a compiler define to the lite target called LITE or something like that. That way you can #ifdef a few parts of the code depending on the version you’re compiling.
And that’s almost it. There’s one more trick that made my life a lot easier. It turns out that you will definitely want to change not just some code, but some assets depending on which version you’re using. So maybe you’ll have a Wall.png texture in the full version, but you’ll want to have a different version of that texture in the Lite version. I started out creating a new file called WallLite.png, but then it quickly cascaded into a bunch of code changes loading either Wall.png or WallLite.png depending on the version and it was ugly and time consuming.
A much better approach is to rely on the resource copy step of XCode. By default, XCode will grab the resources in your project and copy them all in the same folder, flattening the hierarchy completely. This can be a pain if you have resources with the same name organized in different folders because they will clash. But here we can use it to our advantage to supply different versions of the resource for the full and lite versions.
Just create a Lite directory and put any variations of your resources there. Then, in the Resources Copy step of the target, remove the resources you’re replacing and add the ones from the Lite directory. Whenever you build, XCode will put those new resources in the same place where the full resources were and your code won’t have to change one bit. This trick works even with xib files, which is great because I had to modify one of them to reduce the number of pots in the garden screen.
Once you have that set up, the rest is just a matter of deciding what functionality goes in the lite version and how it is presented. It can be as simple or as complex as you want.
In the case of Flower Garden, it was very straightforward. I limited the number of pots to two, and the number of different seeds to three. So I just had to change a few resources and add a couple of #ifdefs with the number of total pots and seeds. I also removed the resources that weren’t used in the lite version (the seed data for all the unavailable seeds for example).
Flower Garden lite was finally approved and it has been available on the App Store for a few days. So far it has been downloaded several thousand times, it has been very well received, and has definitely bumped up the sales of the full version. Definitely a few hours well spent!
[1] The lite version took a couple of hours to create and a month to approve by Apple.
[2] It was something I wanted to have created from the beginning, but my hands were tied because email bouquets were being sent through my server and I had a maximum number of emails I was allowed to send per day because of spam limits. Fortunately, as soon as the 3.0 OS was released, apps were able to use in-app email and that restriction went away.
Thanks Noel, that was really useful info. When I first started using Xcode I balked a little at the targets idea. It seemed confusing and superfluous… until, that is, I needed it for a situation not unlike this one. Now it seems very natural to produce multiple outputs with shared dependencies.
Thanks for your helpful articles.
Because you mentioned test targets: I wonder if you could write an article about unit testing with XCode. Are you still developing/using UnitTest++?
I find unit testing in XCode quite a pain because you need to run the tests as part of the build process to be able to double-click on the error messages.
Hi Claus,
Yes, I’m using UnitTest++ quite extensively with XCode and the iPhone and an article describing that is pretty high up in my priority list. Now I just need to find the time 🙂
I’m surprised you list running the unit tests as part of the build a disadvantage though. That’s how I run all of them. How’s your ideal set up? Do you run them as a separate step afterwards?
I planned on leaving a comment here about UnitTest++, but since I see Claus already has, I’ll just be moving along. 😉
Nice detailed explanation.
Myself I went with the SCM approach. Luckily git makes branching and merging fairly easy. Since I didn’t just have a lite version but multiple apps sharing the same core codebase, I think that was the best approach to take for me.
However I will admit that merging the xcodeproj files can be a bit of a pain at times.
After following the instructions at http://code.google.com/p/google-toolbox-for-mac/wiki/iPhoneUnitTesting, I had troubles setting breakpoints in my code when running the unit tests as part of my build (the breakpoints wouldn’t trigger).
I also prefer running my unit tests separately before checking in my code rather than every time during build – some of my tests involve file reading/writing and network stuff that slows down the execution.
Nice article Noel.
Just a tip. You don’t need to modify the ‘Copy Bundle Resources’ build phase of your target to maintain separation of artwork between different targets.
An alternative method is to create subdirectories in your project root, then drag that folder into the Xcode ‘Groups & Files’ pane underneath the ‘Resources’ group.
Make sure ‘Copy items into destination’ is unchecked and the reference type is ‘Relative to Project’.
Then you can open the Info panel for each of these newly created groups and change the target membership to the correct target. Also ensure any resources inside each group have the correct target membership.
This way just feels cleaner to me.
Amazing!  Just what I was looking for.  The only thing missing from this (unless I missed it myself) was an explanation on how to define the constants which the #ifdef would use in the code.  I asked on stackoverflow and got the answer here:  http://stackoverflow.com/questions/8269711/how-do-i-create-an-ifdef-command-to-differentiate-between-my-two-targets-in-xco
Thanks so much!  You da man!ÂÂ