Over the course of developing many iOS-based apps for a variety of customers such as Sports Authority, Navy Federal Credit Union, Fuzz Alert, Map My Fitness, and more, you begin to sense a pattern with how you use the iOS SDK and what your coding practices are in Objective-C. An overwhelming sensation that you’re doing a lot of the same things over and over again — in about the most verbose way possible. Let’s face it, Objective-C is not only an ugly language, but the SDK is missing a lot of very basic functionality to get your job done more concisely.
That’s why I built and maintain the iBoost Library. It’s a small module that serves as a sidekick of sorts to your normal development. The idea came to me (and its original co-developer, Sean Christmann) following a series of projects where we were using the Three20 framework. The promise of Three20 was that it gave you the tools and added functions that you need to save time architecting your app so that you can focus on the business logic.
So much for promises, right?
Three20 suffers from the taint that many open-source projects can’t shake: It’s practically undocumented and it’s got bugs. What this means is that you’ll spend hours trying to figure out how it works, and when you do, you’ll spend many more hours either fixing it or making it do what you think it’s supposed to do. That’s not saving time.
To make matters worse, Three20 takes the traditional SDK. If you use it to all its abilities, you’ll be building your navigation without using UITabBar or UINavigationController, you won’t be using the table view framework anymore, you’ll be lost to change your status bar color and you’ll be locked into an unwieldy styling framework. Then, when iOS 5 comes out, you need to wait for a whole new slew of updates.
So, after three projects’ worth of using it, I’d gained a ton of experience and knew how to use it pretty well, but found I’d use it less and less the more I knew. I’d scrapped the styling framework, it just didn’t win me enough to overcome all I was losing. I dropped the new table view framework because individual styling of cells became an absolute nightmare. (I yelled out loud once in the office over it.) By the third project, I was having so many crashing issues using modal views (due to added constraints of simultaneously opening and closing them) that I was practically reduced to tears.
And so I made iBoost.
iBoost is Born
The goals of iBoost are simple:
- Build a library that complements the iOS SDK rather than override it.
- Make it so simple and integrated that the new functionality feels native.
- Make it quick and easy to install in your project!
When you import iBoost into your project, you are receiving a set of classes, categories, and macros that augment your daily development in a way that speeds it up without hiding the SDK from you. You can exercise every aspect of it or nothing at all. If you’re using the new Core Data methods to make access easier, you can use the standard methods at any time, too. It’s your choice.
It’s also 100% unit tested. It uses GHUnit to make sure everything that gets added to iBoost works and won’t fail you. My hope is that iBoost will speed up your development, not require to become a low-level expert on it’s code to be able to use correctly.
Installation is Simple!
The iBoost Library is hosted on github and offers a unique installation method once you’ve downloaded it. You install a minification of iBoost that encapsulates the entire library into a single .h/.m pair!
(NOTE: You’ll need Python 3 installed on your system if you want to minify from the source. If you don’t have it or want it, you can simply copy the iBoost/ sub-directory into your project instead.)
Run the minifier with the following commands:
cd iBoost/iBoostThis creates a minification that includes the complete iBoost system. If you don’t care to use the UI components, you can also run this instead:
The minified source is copied to your Desktop. Just copy it to your project and import it where needed. I recommend adding the import to your PCH file so you don’t have to worry about doing it everywhere.
You’ll need to add the CoreData framework to your project as well, and then that’s it. Now let’s check out what you’ve gained!
The first thing you’ll want to check out in iBoost are the macros. If you want to browse these, they are in Macros.h. Here are some highlights.
Boxing and Unboxing Numbers
Gone are the days of bulky NSNumber wrapping!
#define BOX_BOOL(x) [NSNumber numberWithBool:(x)]
#define BOX_INT(x) [NSNumber numberWithInt:(x)]
#define BOX_SHORT(x) [NSNumber numberWithShort:(x)]
#define BOX_LONG(x) [NSNumber numberWithLong:(x)]
#define BOX_UINT(x) [NSNumber numberWithUnsignedInt:(x)]
#define BOX_FLOAT(x) [NSNumber numberWithFloat:(x)]
#define BOX_DOUBLE(x) [NSNumber numberWithDouble:(x)]
#define UNBOX_BOOL(x) [(x) boolValue]
#define UNBOX_INT(x) [(x) intValue]
#define UNBOX_SHORT(x) [(x) shortValue]
#define UNBOX_LONG(x) [(x) longValue]
#define UNBOX_UINT(x) [(x) unsignedIntValue]
#define UNBOX_FLOAT(x) [(x) floatValue]
#define UNBOX_DOUBLE(x) [(x) doubleValue]
These macros are not only quicker to type, they are easier to read and understand when reading the code. They are named for what they do and they stand out, which is they they’re among my personal favorites.
There are many cases where releasing memory should be followed up with a proper nil’ing. In addition, cleaning up timers and doing ivar assignments demanded macros such as these:
#define SAFE_RELEASE(obj) ([obj release], obj = nil)
#define SAFE_TIMER_RELEASE(obj) ([obj invalidate]; [obj release]; obj = nil)
#define SAFE_ASSIGN(obj, expr) ([(expr) retain], [obj release], obj = (expr))
I use all of them everywhere in my code. If I had to type these manually, I’d likely end up cut-and-paste’ing, and no doubt I’d occasionally miss something and either leak memory or nil out the wrong thing. These keep me safe while remaining responsible — and they’re concise and self-descriptive.
Rectangle manipulation can require some bulky code. For example, the standard procedure for changing a View’s size is to copy the frame, change some part of it, and then reassign back to the View. That’s 3 or more lines of code and is re-written so often it begs to be cleaned up. These macros do that.v
#define RECT_WITH_WIDTH_HEIGHT(rect, width, height) CGRectMake((rect).origin.x, (rect).origin.y, (width), (height))
#define RECT_WITH_WIDTH(rect, width) CGRectMake((rect).origin.x, (rect).origin.y, (width), (rect).size.height)
#define RECT_WITH_HEIGHT(rect, height) CGRectMake((rect).origin.x, (rect).origin.y, (rect).size.width, (height))
#define RECT_INSET_BY_LEFT_TOP_RIGHT_BOTTOM(rect, left, top, right, bottom) CGRectMake(rect.origin.x + (left), rect.origin.y + (top), rect.size.width - (left) - (right), rect.size.height - (top) - (bottom))
#define RECT_INSET_BY_TOP_BOTTOM(rect, top, bottom) CGRectMake(rect.origin.x, rect.origin.y + (top), rect.size.width, rect.size.height - (top) - (bottom))
#define RECT_INSET_BY_LEFT_RIGHT(rect, left, right) CGRectMake(rect.origin.x + (left), rect.origin.y, rect.size.width - (left) - (right), rect.size.height)
#define RECT_STACKED_OFFSET_BY_X(rect, offset) CGRectMake(rect.origin.x + rect.size.width + (offset), rect.origin.y, rect.size.width, rect.size.height)
#define RECT_STACKED_OFFSET_BY_Y(rect, offset) CGRectMake(rect.origin.x, rect.origin.y + rect.size.height + (offset), rect.size.width, rect.size.height)
The first batch of macros return a copy of the CGRect with modified width and/or height. These come in handy when you simply want to resize a View.
The second batch returns CGRects inset from the original (use a negative inset to expand the rectangle). Note that CGRectInset is similar to these, but only takes one value for the X and Y coordinates.
The third batch duplicates a CGRect and then offsets them from each other. In other words, if you’re laying out a set of 3 UIButtons in a horizontal stack (ala LinearLayout in Android), you could write code like this:
UIButton *a = ...create my button somewhere...;
UIButton *b = ...create new button...'
UIButton *c = ...create new button...'
This results in three buttons flowing left to right with 10-pixel gaps between them.
These are intended to solve some common maths for you.
#define DEG_TO_RAD(degrees) ((degrees) * M_PI / 180.0)
#define RAD_TO_DEG(radians) ((radians) * 180.0 / M_PI)
#define RGB256_TO_COL(col) ((col) / 255.0f)
#define COL_TO_RGB256(col) ((int)((col) * 255.0))
Their use should be clear.
Other Useful Tidbits
Among many others, here are some you may find the most interesting.
#define SEL(x) @selector(x)
#define IS_EMPTY_STRING(str) (!(str) || ![(str) isKindOfClass:NSString.class] || [(str) length] == 0)
#define IS_POPULATED_STRING(str) ((str) && [(str) isKindOfClass:NSString.class] && [(str) length] > 0)
#define DEVICE_UDID ([UIDevice currentDevice].uniqueIdentifier)
#define DOCUMENTS_DIR ([NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject])
The macro, SEL(x), is prolly the one that causes the most ruckus. It’s sole purpose to me is to shorten the bulkier @selector(x) call.
Where Do We Go From Here?
In Part 2, I’ll review the Core Data Store, a handy set of methods that make using Core Data much easier!