Platinum Partner
mobile,objective-c,tips and tricks,categorization,tools & methods

Don’t Write a Class, Write a Category!

As an iPhone freelancer, I develop a lot of iOS apps. A lot. And one of the most important things to my business is that I leverage as much of my past work as possible when it comes to new projects. I have several strategies, but today I wanted to share one of my favorites: categorization.

If you’re an experienced developer who’s worked with various languages and platforms over the years, you’ll know that the classic way to add “core” functionality was to either extend a class or create a new (probably static) class. We’d create classes like BetterString (extends String) or StringUtils as ways to add functionality to the platform we think could be handy later.

Of course, the problem with extending classes is that they’re not always interoperable with library code. Taking the example above, you might have to wrap your String in a BetterString before passing it to your code. It gets ugly. Creating new classes separates the functionality out a bit, but providing a bunch of static methods from StringUtils just feels disconnected and incorrect. (Hint: it is.)

Abuse the $*#@&^ Out of Categories!

Generally, when we think about categories, the first thing that comes to mind is adding useful NSString or UIColor methods. In InnerBand, I provide popular UIColor#-colorWithHex: and NSString#contains: methods that can be re-used all over the place without interfering with the categorized classes.

But what if I told you that using categories for app-specific functionality unlocks their true power?

You see, the best use for categories is dropping ALLLL the reusable bits you need down to the microscopic level. How much do you use categories? Answer yes or no to these questions:

  1. Do you specify fonts or font sizes in place when you need them?
  2. Do you specify colors all over your code?
  3. Do you access NSUserDefaults values by providing keys where you access them?
  4. Do you check for NSNull when you’re parsing JSON?

Did you answer yes to any of these? If so, you need to abuse categories more. You see, as a Clean Coder, it’s your mission to isolate these kinds of choices and checks as much as possible.

If you’re still reading, you want to know more. So let’s inspire you! Here’s how we can put categories to good use!

Pre-Define Your Fonts in a Category

If you’re specifying a font face or size in your view controller, you’re doing it wrong. You should define the fonts you’ll use in one place and reference them elsewhere. And we’ll use a category:

@implementation UIFont (AppFonts)
 
+ (UIFont *)titleFont {
    return [self bebasFontOfSize:20];
}
 
+ (UIFont *)subtitleFont {
    return [self bebasFontOfSize:15];
}
 
+ (UIFont *)bebasFontOfSize:(NSInteger)size {
    return [UIFont fontWithName:@"Bebas Neue" size:size];
}
 
@end

As a bonus, notice how I even have a convenient private method for creating the Bebas Nueue font in case I ever need to change it.

Create Your Colors in a Category

Colors are tough in XCode. First, I DO NOT LIKE the Apple format of using decimal numbers to define them. Designers don’t use that format, it’s harder to eyeball the distributed syntax, and sooo many apps that can help you with color will output in Hex rather than Apple’s format. (If you insist on using the Apple format, I use and recommend Color Snapper.)

Past that, even if you do use hex colors, you should confine them to a category:

@implementation UIColor (AppColors)
 
+ (UIColor *)headlineColor {
    return [UIColor colorWithString:@"#ff0000"];
}
 
+ (UIColor *)standardBackgroundColor {
    return [UIColor colorWithString:@"#111111"];
}
 
@end

Categorizing your colors in this way makes them easy to change and locates them right on the class you’ll find the most convenient. Look at the client code:

self.view.backgroundColor = [UIColor standardBackgroundColor];

Contrast that with this macro/function usage:

self.view.backgroundColor = MA_COLOR_STANDARD_BACKGROUND_COLOR();

The former just feels so natural. Naming is shorter and more readable, and I know where to find it should I need to look at it.

Don’t Re-Use the Key, Re-Use the Access

One of the more prevalent anti-patterns I’ve seen is to create a constant for use as an NSUserDefaults key. I know, my old code does it, too. :-) And yes, constants are good for this sort of thing to avoid typos, but using categories we can make saving and restoring these values even more reusable and easy to do:

// keys
static NSString * const kUserDefaultsDealerId = @"kUserDefaultsDealerId";
 
@implementation NSUserDefaults (AppDefaults)
 
- (void)setDealerId:(NSString *)dealerId {
    [self setObject:dealerId forKey:kUserDefaultsDealerId];
}
 
- (NSString *)dealerId {
    return [self objectForKey:kUserDefaultsDealerId];
}
 
@end

You can see that I use a constant for the key, but the constant and the accessors are all here on this NSUserDefaults category and, again, all very logically located. (Assuming you don’t have a separate model class.) I can read and write the value, and I do it right on the class:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 
[defaults setDealerId:@"ABCDE"];
[defaults synchronize];

It just feels so natural to perform these operations on the very class we intend to interact with. Notice I don’t do the synchronize in the call because there are cases where doing so would be very ineffecient, such as when you were setting the value within a loop.

Even the Lowest Level Code Can Be Categorized

I love JSON, but it can be a bit annoying to constantly check for NSNull in values you receive. (You are doing that, right?). In the old days, you may have seen a lot of code looking this:

NSString *firstName = [json valueForKey:@"first_name"];
self.firstName = (firstName && firstName != (NSString *)[NSNull null]) ? firstName : nil;

Repeated everywhere. Well, let’s categorize!

self.firstName = [json safeValueForKey:@"first_name"];

We can reuse it everywhere. What’s it look like?

@implementation NSDictionary (Nulls)
 
- (id)safeValueForKey:(NSString *)key {
    id value = [self valueForKey:key];
 
    if (value && value != [NSNull null]) {
        return value;
    }
 
    return nil;
}
 
@end

Final Thoughts

Categories are a powerful part of the Objective-C language and, for once, one that’s been there quite a while. (*cough* blocks *cough*) They get decent use in every codebase I’ve seen, but I think many stop short of truly abusing them. Hopefully, I’ve turned you into a frequent abuser today!

One last thing, I am a proponent of importing these in the PCH. Every one. Why? Well, if you take the concept of “forgotten code” as code that doesn’t get used by developers who weren’t aware the functionality was there, then the advantage of importing these in the PCH is that a new developer can more easily stumble on these without even being told. They go to define a new font and…hey, what’s that? A -headlineFont method? I think it’s a great way to help developers find their way around the codebase.

Alright, that’s all I got. Code away!

Published at DZone with permission of {{ articles[0].authors[0].realName }}, DZone MVB. (source)

Opinions expressed by DZone contributors are their own.

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}