If you’re developing an iPhone application that uses WiFi networking or the new Gaming Kit (Bluetooth) at some point you’ll want to test the code on two devices side-by-side. At this point you will realize that:
a) You can only run one instance of XCode on your machine at a time.
b) Debugging one device at a time is a royal PITA.
c) Your only option is to beg, borrow, or steal another Mac then install all of XCode and your code onto it just so you can do some debugging.
I’m here to tell you there’s a better way.
The solution is to use VMWare Fusion with Leopard or Snow Leopard server as a “guest OS.” (In VMWare-speak your regular machine is a host OS while each virtual OS instance running on it is a guest OS.)
The reason this works is because the latest version of VMWare allows you to run OS X Server as a guest OS on top of another OS X host OS. VMWare also allows you to assign specific USB devices to each VM instance so you can tell it that one iPhone/Touch belongs to the guest and the other to the host OS. Now I’ve only tried this with Snow Leopard Server so I’ll stick with that but VMWare says it can also install Leopard Server but I have’t tried that. Since Snow Leopard is still under NDA I’m not going to go into any specifics of the OS itself. Instead, I’ll show you how to set up VMWare so it can pull this off.
To start you’ll want the latest VMWare version (
2.0.4 2.0.5 as of this writing). Earlier versions may not support OS X as a guest OS. To get access to the Snow Leopard releases you’ll need to be part of the Apple Mac Developer program. You’ll want the Server release. During WWDC 2009 some people were reporting having trouble installing Snow Leopard under VMWare from a disk image file so you might have to sacrifice a blank DVD and burn it to disc before installing it.
With the disc all you have to do is pop it in and it does the right thing. Or you can walk through and manually choose which versions you want to load.
Technically VMWare is only supposed to run Leopard/Snow Leopard Server which installs a lot of extra background services, etc. and needs a pretty hefty chunk of memory. If you Google around there are ways to patch the VMWare settings files to let it install Client. Far be it from me to suggest you do this, since it’s not certified and is not recommended for production use. All I’m saying is it might be less resource intensive and prevent your MacBook Pro from catching fire after the fans run on full-bore trying to cool down 100% loaded dual-core CPUs. But then again, YMMV.
So once you have VMWare and Leopard/Snow Leopard installed, you’ll want to install XCode (which comes on the OS install disk under “Extras,” or it can be downloaded separately from the Developer Connection website). Once that’s done you’ll also need the iPhone SDK. If you’re a member of the iPhone Developer Program (and really, who isn’t nowadays?) you’ll want to download and install the latest SDK (note that there are different downloads for Leopard and Snow Leopard).
Inside VMWare you’ll want to set up directory sharing. I won’t walk you through that here but if you have trouble getting the VMWare “folder-mapping” feature to work (like I did) one workaround is to turn on file-sharing in the host OS and mount the networked filesystem from the guest OS. If you do this, make sure you restrict access to only your own account on the shared volumes and turn it off for everyone else, otherwise all your pals at the coffee shop sitting on the WiFi and running Bonjour will have access to your source files.
Next you’ll want to end up with two separate projects, one for the host and the other for the guest OS. The reason for this is because each instance of XCode makes changes to the project settings each time you do a build. If you share the projects and load them from two XCode instances, you’ll end up with an endless litany of XCode warnings asking you if it should reload the project from disk. Save yourself a lot of headache. Just create separate projects but keep all your source and media files in the same shared folder. This way if you do actually make changes to the source code during debugging you only have to do it once from either side, otherwise it’s a nightmare keeping track of deltas.
(Handy tip: if you have dual monitors, install VMWare tools and put the guest OS on the second monitor then have it go full-screen. It’s like having two actual systems in one.)
So now comes the fun part: plugging in the actual devices. Make sure you’re not running XCode or any other app that accesses the USB devices behind the scenes. If you have iTunes set up to auto-run and sync when a device is plugged in you either haven’t done much real iPhone device debugging or you have a much higher tolerance for Annoyingly Intrusive Software. I turn off all auto-sync, auto-run, auto-whatever features that iTunes throws in to make itself the consumer-friendly app that it is. But that convenience gets in the way of debugging, so do yourself a favor and turn it all off. Manual sync mode is your friend.
iTunes also installs an iTunesHelper app that runs in the background. I personally take that out too by going into System Preferences > Accounts
then clicking on the “Login Items” tab button, finding “iTunesHelper” and hitting the ‘-‘ button (or delete) to take it out. The point is, if iTunes snags your USB port before VMWare gets to it you won’t be able to switch it over.
So now we have the two devices plugged in. Do NOT give in to temptation and start XCode just yet. In VMWare bring up the “Virtual Machine Library” which lists all your VM instances (if it’s not already showing, go under Windows > Virtual Machine Library). Select your Leopard/Snow Leopard instance then push the “Settings” button.
Now go into the USB devices section. Note that you can only make changes to the guest OS once it’s powered up and running. So if the guest OS isn’t already running, boot it up. You’ll be faced with a list of all the USB devices on your host machine, something like this:
Unfortunately VMWare doesn’t actually show the device names as assigned through iTunes so there’s no way to tell which device is which. If you have two iPhones plugged in they both show up as Apple iPhone. You can always test an iPhone against a 2G touch (the Touch shows up as Apple iPod). Then again, if both devices are running the same code it may not really matter.
Turn on the checkbox next to the device you want assigned to the guest OS. If it doesn’t stick then it’s likely you’ve got some other app locking up your USB ports. Go back and re-check (might have to do a clean reboot). I should note that this was the most frustrating part of the whole exercise. The trick that made it work for me was to make sure XCode is not running until AFTER you’ve done your USB port assignments.
Once it’s checked, *now* start up XCode on both sides. The host OS should be seeing one device and the guest OS the other. Verify by loading up the XCode Organizer and seeing that each device shows up with a little green dot next it. If you haven’t done it yet, you’ll want to tell the XCode Organizer that the devices are to be used for Development.
Getting excited? Almost there. Now choose iPhone Device from your XCode Active SDK pop-up.
Ready to hit Build and Run?
Not so fast.
Your iPhone development certificate is only installed on your host OS. The guest OS is a whole other machine and it needs to be set up as if it was a brand new machine. Fortunately, it’s fairly straightforward to get the keys moved over.
Launch the Keychain Access application (in Applications > Utilities) on your host OS. If you followed the iPhone SDK instructions and created a provisioning profile for your application on the iTunes Connect site, you should have a private key certificate installed in your keychain. If you’re like me and have done this a number of times, you’ll have lots of similar looking keys floating around. The trick is to look for the private key with your name and a drop-down disclosure arrow next to it. Open those up and verify that they say iPhone Developer and/or iPhone Distribution next to them. If you’ve followed Apple guidelines you should have one for development and another for app-store or ad-hoc distribution.
While you’re there, it’s a good time to make sure the expiration date is in the future. I got bit once and boy, was it a pain to diagnose. Stuff that was working one minute magically stopped the next. So stay on top of your certificate expiration dates. If the date is pretty close or already gone by go back to iPhone developer site and follow instructions to regenerate new keys, then install them and come back when you’re done.
Command-select all the private certificates you want moved and export the lot of them (File menu > Export Items…) Make sure they go out as .p12 (Personal Information Exchange) files. You will be prompted for passwords. Pick something pithy but memorable and save the exported file to a shared folder you can get at from the guest OS. While you’re at it, you may also want to move over your app’s provisioning profiles just in case you need it later.
Back in the guest OS, double-click the exported certificate file, enter the password, and install it in your keychain. You’ll probably have to restart XCode so it picks up the changes. To verify that it worked, bring up the XCode project in the guest OS, do a Get Info on the target and make sure the Code Signing Identity is selected and is the same as the private key you just installed.
So NOW you’re ready to go. Select iPhone Device from the Active SDK on both sides, hit Build and Run, go grab a cup of coffee or an adult beverage, and wait until both instances come up side-by-side.
That wasn’t too bad, was it?
A few final thoughts: I ended up running Snow Leopard as both host and guest OS since I wanted to run XCode 3.2 with the latest goodies on both sides. You’ll definitely want separate project files if you want to run Leopard in host and Snow Leopard in guest OS. At this point it’s not clear if app-store submissions can be built using unreleased versions of XCode. So you might want to check on the developer forums before doing the crazy thing I did and upgrading everything everywhere to the new shiny version.
Also, there’s no reason this shouldn’t work for three or more devices (for group peer-to-peer or mass WiFi testing) but you’ll need a beefy Mac with a lot of CPU and memory. You’ll also be needing a USB hub (I ended up with this Belkin Ultra Mini model based on advice from @schwa on Twitter) if you run short on USB ports. All it takes is another VMWare instance and assigning the right device to the right OS instance. Lather, rinse, repeat.
Update: Make sure you read VMWare update release notes. In the latest release there are two known issues that directly impact this way of using VMWare:
You’ll definitely want to turn off Sleep mode as soon as you’ve installed the guest OS and before you walk away from the system for an extended length of time otherwise you’ll have to hard-reboot the VM instance. I even turn off the screen-saver. If the system is idle for any length of time the host OS should be the one doing the sleeping and screen blanking.
The first issue is also easily solved. You can manually download and install MacFUSE. It allows you to use not only third-party file systems on top of MacOS, but file systems on local disk, across the network etc. There are some issues with folder sharing across guest/host applications that clean install of MacFUSE might clear up.