I am doing a complete revamp of my second app – SweetTooth – a Match-three game. I know lots of people hate Bejeweled clones but it was fun to develop and a good learning experience. There are even some people who like it!
As I said, this was my second app. In other words, it was my second-worst app. I did not follow the MVC pattern to the letter. In part because I thought I could take shortcuts without any ramifications and in part I guess because I did not understand the concept fully. I did follow it where necessary, but I did stray from the right path more than I should have. I knew that I could have done better, but I was lazy and cocky. I also knew that it could come back to bite me in the ass if I ever had to update it or create another version of it.
The time for updates came and against all odds it went a lot smoother than thought. I just had to fix a few bugs that were unrelated to the poor design and easy to fix as well. But now that I am working on another version with new features and a different setting, it just doesn’t feel right any more and it also doesn’t reflect my programming skills the way I would like it to. If I wouldn’t have posted it here, probably no-one would have ever known, but since I want to deliver quality rather than crap, I did make a lot of changes and thus posted it here.
This brings me to the point of code cleanup. It comes in a variety of shapes and sizes. For this post I will restrain myself to some aspects of it.
The Slim-Fast Approach
Take a good look around and think about what is really needed. If you have analytics code embedded in your apps then that should be quite easy, but since I personally don’t like Flurry and the like – or rather the intrusive methods they employ – I do not use any analytics. Luckily my game does not have that many non-essential features. The only three being Game-Center, Facebook and Twitter integration and those are quite easy to monitor without analytics software.
Most of my players use Game Center so I can’t just remove it and it is way more fun to compare ones scores in an online tableau rather than holding your iPhones next to each other and compare them like in the olden days.
Twitter and Facebook Integration were so rarely used that they became negligible and I did not want to allow something like that to clutter up my code base, so I decided to remove them from my new app. Maybe now, with iOS 5, I will re-add Twitter because almost no work is required, but right now it just feels good to remove unused code and dependencies.
Another course I have taken is to aim for loose coupling and high cohesion in my class design. Recently a tool came to my attention which graphs the dependencies in my XCode projects. The tool itself is a small python script which uses a regular expression to find #import statements in all the .h and .m files of a project. Simply supply the python script with the path to your project and write the output into a file.
$ python objc_dep.py /path/to/your/project > project.dot
The produced .dot-files can be easily viewed in OmniGraffle or the free alternative GraphViz. Look for blue arrows in your graphs. Those are bi-directional imports and they are especially ugly and error-prone. My first output looked disastrous. That is what you get when you don’t make use of design patterns.
I should note that I changed the regular expression a bit so that it took my setup into account. In some classes i commented out some imports. Using the original regular expression those imports would still show up in the graph. My change is quite simple: Just look for the “#import” statement at the beginning of lines. This works for me because I use // style comments. It would not work if one would use the /**/ comment-style.
regex_import = re.compile("#import \"(?P\S*)\.h")
regex_import = re.compile("^#import \"(?P\S*)\.h")
After multiple iterations of dependency removal and moving *lots* of code from the UIView Subclasses to their respective UIViewController Classes, I finally arrived at the following graph.
As you can see, there are still a lot of dependencies on the App Delegate. This is because my Core Data code resides in there. But some of those dependencies could be removed by simply storing the settings in the NSUserDefaults rather than Core Data. This step is the next on my way to a clean(er) class structure. There are also some of those pesky blue bi-directional dependencies left which I vow to hunt down and eradicate after this post is published.
EDIT: Just removed one more bi-directional dependency by simply moving the Settings for my app from Core Data to NSUserDefaults. I wonder why I ever wanted to store Settings in Core Data because it is way more complicated than NSUserDefaults.
Well, the main lesson I have learned is that best practices and design patterns exist for a reason. I knew that before, but I never experienced it in my own projects. So what I take away from this is: Get it right the first time around – or at least aim for that. It will take considerately more time but it will definitely be worth it when the time for updates, bug-fixes and next versions comes. Any shortcuts taken in any stage of the project will come back and haunt you/cost you money later on.