OCUnit is the standard unit testing framework that is built into current versions of XCode. The OCUnit SDK is called SenTestingKit, which is a little confusing, but whatever. The biggest drawback to using OCUnit (or SenTestingKit) is you cannot get a call stack (i.e. back trace) when tests fail.
Hey Call Stack
Getting a hold of the NSException object is key to printing the call stack. After a bit of poking around in the headers, I found a notification called SenTestCaseDidFailNotification, which gives you an array of NSExceptions that occurred during test failure.
OCUnit is a bit obscure, but it’s integration into XCode makes it very fast to use—the ⌘U shortcut in XCode is fantastic. This is about the only advantage over GHUnit (another great testing framework), and it is the biggest reason I keep using it.
OCUnit is open-source, and I’ve put this on my list of “code to read”.
Pivotal Tracker is pretty easy to use, but like any powerful tool, it will only work as well as you use it. The hardest part of using Pivotal Tracker is learning how to rank stories. I’m going to describe how I use the point system, while it’s certainly not the only way, it works quite well for me.
Basics
First, some basics. My Pivotal Tracker workflow is straightforward: create a bunch of tasks, assign points, and begin working. Throughout the day I will change points and add tasks to reflect what is actually happening–giving me immediate insight into my project.
If you do things right, Tracker is designed to answer questions like these:
How much time is stuff taking?
How soon will this feature be finished?
Are bugs affecting upcoming deadlines?
Who’s working on what?
Sounds Easy
The big “but” is that Pivotal Tracker only works if your point ranking is consistent. So what the heck are points? How do you start?
Points are Time, Sort Of
Points are essentially equal to some unit of time, but don’t dwell on the time values, they are intentionally vague on purpose. The purpose of a point ranking system is to keep estimates and tracking at a high level. The ambiguity is a feature, and this can make it a little hard to know where to begin.
Beginner’s Guide to Points
Here is a simple approach to start using points effectively. Remember, being consistent is the most important part.
Start with 15 points allocated for the week.
Each 8 hour day gets 3 points.
Also, keep pivotal tracker on the 1-3 point scale until you become more comfortable with ranking. Starting is easy: simply begin ranking your tasks from 1-3 points. Anything greater than 1 day should be broken into smaller subtasks; anything smaller than a couple hours shouldn’t be a task.
Task ranking becomes easy if you think like this:
1 point tasks are 2 hours or so.
2 point tasks are 4 hours or so.
3 point tasks are 8 hours or so.
The Process
When you start “tracking” a project, it doesn’t mean you need to know all the tasks and points in the beginning. It’s perfectly normal to continually add new tasks, change points on existing tasks, or delete tasks entirely. Many tasks will end up with different points than they started with. (Tasks can have zero points too.)
The goal is to have Tracker represent reality as best as possible. It should reflect only what you know about a project, and it’s OK (and normal) if you don’t know much.
The magic of using Pivotal Tracker happens after a few weeks into a project. You get a visual representation of what was accomplished in each iteration (or week), and as long as you are diligent about keeping new tasks entered and rated, you will have a fairly accurate outlook on future work.
The point system really shines when you can say: “Feature X took 5 points, so feature Y will probably take the same”. The vague quality of points keeps you from fiddling with hours and estimates. Having 3 points for the day keeps things flexible. For instance, if you finish a 2 point task in 4 or 5 hours and spend the rest of the day on a 1 point task, it ends up being OK.
Icing on the Cake
A few other super handy features are epics and task lists.
Epics allow you to group many stories into a unit. The Tracker interface gives a size graph for each epic, giving you instant size comparisons between all the features of a project.
Another feature is that each story can have a task list. It’s extremely helpful to be able to create lists and reorder items inside a story, I use this feature heavily on complicated tasks. The lists work as both an organizational tool and to track progress.
Smooth Sailing
Pivotal Tracker is an amazing tool if used to it’s full potential. It has a lot of power, but remains one of the simplest bug trackers to use. Because of it’s simplicity it can be adapted for use on any kind of project, whether on a large team or even single person projects.
Inspired by the 7 sins of objective-c, here is my unorganized list of random dos and dont’s for iOS development.
1. Don’t kitchen Sink the AppDelegate
Any code in here should relate to UIApplicationDelegate tasks: launching, quiting, backgrounding, and foregrounding the app.
I know, it’s tempting to use the AppDelegate instance as your “global” singleton instance, but don’t do this. Create specific and seperate singletons if that is what you need. Things I’ve seen in AppDelegate:
CoreLocation code
Analytics code
Database access code
etc..
Don’t get me wrong, AppDelegate is the place to initialize these guys, but making AppDelegate the primary class that will contain the logic for these services is bad idea.
UIView has a property called “tag” which is intended for tracking a view for later use. The tag property might as well be called “lazyHackUsedHere”. The biggest problem with the tag property is that its use always starts out simple, but over time the logic (or lack thereof) evolves into a fat unreadable mess.
Instead of tags, put UIViews into collections (array or set). If you just need a single view, then rely upon instance variables.
Tags are not obvious; they make figuring out heirarchies difficult. The key with collections and instance variables is they have names, which are incredibly useful in figuring out intent and purpose of the views being tracked. Debuging is much easier as well: as it’s much easier to introspect variables in lldb then it is to query the heirarchy for tags.
3. Don’t check-in Commented-Out Code
Don’t leave commented out code in production builds. (Also, please don’t make redundent comments on code that could simply explain itself.)
4. Don’t over complicate it
Don’t make methods that are many 100’s of lines long. Code should be a bunch of short single purpose commands.
5. Do use UIViewControllers
I’ve worked on a two projects that for some reason did not use UIViewControllers. The previous enginner built the entire UI with only UIViews—this is very bad! View controllers are useful for many reasons (MVC comes to mind), but ultimately they serve three concrete purposes:
Provide rotation events.
Provide appearance events.
Provide system memory warnings.
You could implement each of these behaviors in your views, but why reinvent something that is given to you for free. Not only is this wasteful, but UIViews shouldn’t ever know about rotation and appearance, that’s what controllers are for!
6. Do Learn How to Create Containment View Controllers
You know the classic view controllers: UITabBarViewController and UINavigationController, well you can make your own containment controllers, these puppies are the key to creating custom navigation in your apps. Once you crack this task the possibilities are endless. Containment view controllers allow you to abstract complex navigations (and animations) while using memory efficiently.
7. Do use the delegate pattern
The delegate pattern is pervasive across UIKit, and once you learn how it leads to simpler code you’ll see why it’s used so heavily. Avoid passing messages through NSNotificationCenter whenever possible, it does have its purpose, but I can almost gaurantee you if that is your first choice then you’re doing it wrong.
8. Do use singletons where they make sense
Singletons are quite the controversy, but they definitely serve valid purpose in iOS apps. As a side note, the best way to implement singletons these days is with GDC.
Here are some valid use cases where I’ve used singletons (it’s easy to tell what they do based on the name alone):
CoreLocationService
DatabaseService
CachingService
MasterNetworkRequestQueue
etc..
9. Do use a standard code style
Using a standard code style is all about consistency, and this is something that will most likely benefit others more than yourself. (That doesn’t mean this is optional.)
Also, don’t just make up your own style, unless you are that “cool”—then by all means, do what you want. Probably would be best to pick K&R Style or 1TB Style. See wikipedia for details on each of these.
I have failed at this one over the years, but recently have settled on using K&R style.
10. Do abstract complex views
Abstracting complex views as much as possible by subclassing UIView. This seems trivial, but to the guy who inherits your project it will make his life so much easier!
11. Do use latest and greatest language features
Use ARC and modern property syntax. Retain, release, and @synthesize are so 2012.
12. Do explicitly reference Nibs
If you must use Nibs, always explicity reference them in your view controllers or views. For UIViewControllers, UIKit will automatically load correctly named nibs, but I’ve been bitten by this feature a few times, as the nib file was hidden far away from the view controller.
This solves to probems: 1) simplies public interface, and 2) explicity shows that nib is being used and shows what the filename is.
12345678910111213141516171819
// instead of this:-(id)initWithNibNamed:(NSString*)namebundle:(NSBundle*)bundle{if(self=[superinit]){}returnself;}// explicity load the nib, like this:-(id)init{if(self=[superinitWithNibNamed:@"NibNameGoesHere"bundle:nil]){}returnself}
Developing on the iOS platform is unique is many ways; one particularily annoying item is the way problems show themselves as a sudden crash of your app. The two most common causes of crashes of an iOS app are: 1) performing incorrect memory management, and 2) accessing NSArray outside it’s bounds. Automatic reference counting solved the first problem. That leaves us battling the unforgiving NSArray.
NSArray likes to crash
Accessing an NSArray with an index that does not exist instantly crashes your app. The NSDictionary collection, which uses keys, will return nil if you ask for element with an non-existant key. They are very different collections with totally separate use cases. For one, NSArray isn’t allowed to be sparsely filled—it must contain data in sequential format. So they are different, but this doesn’t explain why NSArray can’t simply return a nil value on non-existant indexes.
Why can’t we be friends
I’ve been plotting to create a NSArray subclass, called BFSparseArray, that will always return nil on an invalid index. It sounded like a wonderful idea until I realized why this would be very bad. Having code return unexpected values is harder to debug than a crash. The unexpected value will cause all sorts of weird behaviors, things that end up being incredibly hard to track down. The crash on the other hand halts the execution immediately, and (if you use the NSSetUncaughtExceptionHandler() trick) you get a full stack trace that directs you right to the problem.
No coddling here
So in the words of Daniel Jalkut, “Don’t coddle your code”. Let those crashes happen and keep on trucking until they’re all fixed. While you’re writing code and libraries of your own, don’t return default values if input is unexpected. It is usually better to crash the app immediately instead of obsurcing the buggy condition even further.
(By the way, NSAssert() is a nice, obvious way to check for valid input, and it will crash with a nice Assertion failure exception.)
Most simple apps will never deal with slow Core Data data reads, but with a complex data model, or where the data largely defines the UI, getting super fast queries can be a rewarding challenge.
Multi-threading CoreData will certainly give the appearance of speed, as it won’t block the main thread and this won’t speed up a slow query. There is a subtle trick which will massively speed up queries. This technique isn’t obvious, and is probably the biggest secret about core data performance!
Many Requests == Lots of Overhead
From the documentation:
Each round trip to the persistent store (each fetch) incurs an overhead, both in accessing the store and in merging the returned objects into the persistence stack. You should avoid executing multiple requests if you can instead combine them into a single request that will return all the objects you require.
Filter those arrays
This tiny blurb doesn’t do the concept justice. The “overhead” is very large; especially if you’re making many requests with the intent to query the datastore with different NSPredicates. It can be 2x or 3x faster to make one fetch request and filter the resulting array using filteredArrayUsingPredicate:.
An Example
For example, say you have two entities in your sqlite-backed data store: “Meeting” and “Type”. Your goal is to display “meetings” grouped by “Type”.
The most obvious procedure would be:
Fetch all “Type” objects into an array.
Loop through the array.
Fetch each set of “Meeting” objects using NSFetchRequest coupled with a NSPredicate.
Hand off the results.
This seems correct, as it offloads the predicate query onto the sqlite datastore, but this is actually the slowest way to fetch data.
Fast method (the difference is subtle):
Fetch all “Type” objects into an array.
Fetch all the “Meeting” objects into an array.
Loop through the array.
Filter the “Meeting” objects using the NSPredicate and filteredArrayUsingPredicate:
Hand off the results.
The difference is your making one NSFetchRequest(the slow part), and many filteredArrayUsingPredicate: calls on the array. You’re essentially querying “in memory”.
Conclusion
It’s counterintuitive, but “filtering arrays” is much much faster than “querying the datastore”. The overhead of making multiple fetch requests far outweighs any benefit the database can give you.