Beware the Cruftmonster
At a recent class, I was asked a couple of innocuous questions that ended up having the same root problem. “My app runs fine on my phone, but can’t find some resources when run from the Simulator.” Another was “I’m getting iPad graphics even though I removed the file.” I personally have encountered a problem with “It runs great on my machine and my device, but breaks when someone installs a beta build because it can’t find some images.” The three of us got bit by the cruftmonster.
When you build your app, Xcode does as little work as possible to get your app copied and running on your device or in the simulator. It sees if you have any new or modified files and copies them up. Everything else it leaves alone. Ordinarily this is a good thing – you don’t want to wait while Xcode uploads the 500 megabytes of graphics assets for your really spiffy flashlight app every time you take it for a spin on the device.
The problems happen when you start removing things. Removing the file from the Xcode project does not cause the corresponding files to be removed from the application bundle that lives in the simulator or on the device, leading to an accumulation of old files in thre. This happens when renaming a file too, since a rename is fundamentally adding a new file and removing an old one.
OK, so how can this cause problems? In my case, I had a bunch of images and masks for drawing an animation of a bucket filling up and then overflowing. I made a some changes to the organization and names of the files on my Mac and in Xcode. Unfortunately I botched the changes to the code that referenced them, so they ended up using the old names. The cruftmonster left the old files in their old location in the app bundle, so the buggy code happily found the old images. “Yay, it works! Ship it!” I cut a beta version of the app a couple of weeks later and shipped it to my boss. He let me know nearly immediately that the buckets were gone. “But… But… But… It works on my device! Look!” A couple of hours of debugging later (and some personal embarrassment) I discovered the file name discrepancy and fixed the code.
That class where I was asked those questions? The problems were similar. Someone had done a refactoring, a xib file got renamed, but the string name of the xib file didn’t get updated. The app ran fine on the device because it still had the old xib file, but the Simulator was running on a loaner machine, and so had a very fresh build that didn’t include the old xib file. Another student had added some ~iPad@2x.png graphics to the project, but decided they weren’t needed. Out of the project they came. Re-running the app, the stale graphics were still there, being picked up by UIImage.
I always tell people that I’m not smart, I’ve just accumulated a lot of scar tissue. I had encountered the bucket problem a couple of months before the class, so the leftover-cruft problem was still pretty fresh in my brain, allowing me to address their questions disturbing rapidity.
Now that the problem is known, is there really any way to prevent it? Not really. I filed radar 11562130 – feel free to file your own and dupe this one. (As soon as I get my dupe report, I’ll update this number)
When faced with problems of the form “I know I nuked that pony but it keeps coming back from the dead like a Zombie Fluttershy”, check to see if you’ve been visited by the cruftmonster. How to fix it? One of the standard parts of the “wave dead chickens over stuff and hope it magically fixes it self” litany is “remove the app from your phone or the simulator and try again.” This has the side effect of removing all the cruft, starting you over from the same slate as if you had made a new application archive.
A lot of times you might not want to do that. Perhaps you have a number of existing documents you use for testing. You might actually be using your app for Real Stuff and don’t want to lose everything. Or you don’t want to wait for those 500 megs of flashlight images to upload. That means you’ll need to go into your application bundle and clean things out yourself. I usually just go in and remove all the stuff inside the app bundle, but not remove the app bundle directory itself. The next Xcode build will then copy everything over there.
The hardest part when cleaning out your app bundle in the simulator is just finding it. I use the find command to grovel through my ~/Library looking for stuff:
% find ~/Library -name Encruftinator.app ~/Library/Application Support/iPhone Simulator/5.1/Applications/D116B125-...- 053BA32E73F2/Encruftinator.app
And then visit that directory in the terminal or in the Finder to remove the cruft.
Edit: Rancher Alex Silverman reminded me about the Simulator’s nuke and pave button (“Reset Content and Settings”) that’ll do a nice clean-out of everything.
Cleaning things off the device requires a utility like iExplorer on the Mac (or just plug it into a Windows box), and then navigate around until you find your app, and clean it out from there. These aren’t pretty solutions, but they work. “Crude, but effective.”
Wanting to get down? The Avanced Mac OS X Bootcamp covers a lot of the lower-level details of Mac OS X and iOS, covering architecture, libraries, tools and techniques.