Multi-Target: The Guide
In a previous blog entry I had mentioned that I combined my Full and Lite versions of each of my apps into one XCode project using targets. This was something that pre-XCode 4 was pretty hard to do, thus my decision to keep them as separate projects until recently.
Another reason to create multiple targets would be to keep your iPhone and iPad versions in the same project. This probably becomes even more important nowadays if you don’t wish to go the Universal App route and prevent retina assets app bloat. Plus it gives you the option of pricing your iPad app differently than your iPhone app if you choose to do so.
I didn’t go into much detail the previous time around other than to give a few quick pointers. This time I’ve created a sample project and will guide you through step-by-step with screen captures where appropriate. The project can be found here (you’ll need it because I’m not going to go through every single step in this tutorial).
The first thing I did was create a new “Single View” project.
This created a target with the same name as the project.
I really wanted to create 2 targets with different names than the project. So I created 2 new “Single View Application” targets (File->New->Target). The first one I called MultiTargetIphone and the second MultiTargetIpad, making sure to select the right “Device Family” for both.
I then removed the original MultiTarget target, scheme, and directory. You do this by selecting Product->Manage Schemes. Click the minus sign to remove MultiTarget. Then select the target in the TARGET view, right click, and delete. Also remove it from the files view and the physical directory in the project (show in finder). If anyone has a better way to remove a target and all it’s files, let me know. It seems like a lot of areas to remove.
Here’s what my project looked like after this step
And in Finder
To prove that these are indeed 2 different targets and binaries I’m going to add some shared code and assets as well as specific target code and assets. When we’re done we’ll examine the resulting bundles and make sure only the necessary assets are included. When adding files and assets make sure you specify what target they should be included in.
or you can do this after the fact in the File Inspector panel
In this example I’m using a separate XIB file per target. I’m going to put 2 buttons in each. One button will call a target specific IBAction and the other will call an IBAction that will use a shared class to perform it’s behavior. Both targets include SharedStuff.m and shared.png, however they each have their own ViewController that handles the specific behaviors for that target as well as their own button images (ipad.png and iphone.png).
The one thing I did notice is that Interface Builder doesn’t seem to honor the “Target Membership” settings. That is, when I’m editing the XIB for the iPad it allows me to select iphone.png as the background button image even though I only added that file to the iPhone target. The button DOES end up empty as expected when you run the executable (because the png file is not included in the bundle), but just be aware interface builder may be showing you things it shouldn’t (let me know if you have a different understanding of this).
I’m not going to go into the details of wiring up the IBActions, building the UI, or writing the code in the action. You have the source and can play around with that. Here’s what my project structure now looks like.
My directory structure in Finder looks like this
You’ll notice I also added icon.png and icon-72.png for both the iPhone and iPad. You can select the icon to use in the target’s summary panel
Ok, now let’s run the 2 targets. First select the iPad one and run it on the iPad simulator.
You’ll see the following
The top button is using ipad.png to display it’s contents. The bottom button is using shared.png. Clicking on each will call the appropriate IBAction in the ViewController and display an alert dialog. The shared one will use SharedStuff which is common code (and asset) shared by both targets.
Now let’s run the iPhone one in the iPad simulator to prove that they are indeed 2 different binaries. You’ll know this because the iPhone version will run in 1x mode and not take up the full screen.
You’ll also notice two different icons on the SpringBoard.
Ok, but how do you know the bundles only include what is necessary for each platform? One way is to look at the “Build Phases”.
Notice that the iPad target includes it’s version of main.m, AppDelegate.m, and ViewController.m, and includes the shared SharedStuff.m. But it doesn’t reference any of the code files for the iPhone target. In the resources it has it’s own InfoPlist.strings, ViewController.xib, and ipad.png, and shares shared.png, but doesn’t reference iphone.png or it’s interface builder files.
Another option is to build for the device and use PhoneView to inspect the package contents.
You don’t see any of the iPhone assets in the iPad bundle.
Whew… I hope that helps understanding how to build an iOS app that can target multiple devices separately and yet share common assets. Or you might use it to provide full and lite versions of your app. You’d just add a LITE_VERSION compiler flag to the build settings for your lite version and ifdef your code accordingly.
There really is no reason not to try and combine different versions of your app into one project now that Apple has made it easy. While this use to be hard before XCode 4 it’s obvious Apple has given this considerable thought since then.
UPDATE: Last weekend I submitted Jiggle Balls Studio to Apple. It’s the update to Jiggle Balls HD that is now FREE and supports In-App Purchases. I’m still waiting for it to be approved, but in the meantime you can go get the previous version for FREE and get yourself ready for the update. Enjoy!