Clean Code With Categories and Extensions

Clean Code With Categories and Extensions

A technique I use in almost every project leverages the power of categories (Objective-C) and extensions (Swift). This technique is very easy to implement and it rids your project of magic numbers and code duplication. Let me give you an example.

Colors

Every project uses colors to spice up the user interface. But if your designer changes her mind and tweaks the color scheme of the project, you don't want to make dozens of changes in dozens of files.

The following example illustrates a neat solution to this problem using categories in Objective-C. Later in this tutorial, I show a similar implementation in Swift.

#import <UIKit/UIKit.h>

@interface UIColor (Styles)

+ (UIColor *)baseColor;
+ (UIColor *)darkTextColor;
+ (UIColor *)lightTextColor;
+ (UIColor *)defaultBackgroundColor;

@end
#import "UIColor+Styles.h"

@implementation UIColor (Styles)

+ (UIColor *)baseColor {
    return [UIColor colorWithRed:1.00 green:0.37 blue:0.36 alpha:1.00];
}

+ (UIColor *)darkTextColor {
    return [UIColor colorWithRed:0.20 green:0.25 blue:0.30 alpha:1.00];
}

+ (UIColor *)lightTextColor {
    return [UIColor colorWithRed:1.00 green:1.00 blue:1.00 alpha:1.00];
}

+ (UIColor *)defaultBackgroundColor {
    return [UIColor colorWithRed:0.69 green:0.81 blue:0.90 alpha:1.00];
}

@end

As you can see, we define a category on UIKit's UIColor class and implement a handful of class methods that return the colors for the color scheme of the project. This is what that looks like.

#import "ViewController.h"

#import "UIColor+Styles.h"

@implementation ViewController

#pragma mark - View Life Cycle

- (void)viewDidLoad {
    [super viewDidLoad];

    // Configure View
    [self.view setBackgroundColor:[UIColor defaultBackgroundColor]];
}

@end

In Swift, you can use extensions to accomplish a similar result. Notice that we follow the Swift API guidelines by omitting the color suffix.

import UIKit

extension UIColor {

    static let base = UIColor(red:1.00, green:0.37, blue:0.36, alpha:1.00)
    static let darkText = UIColor(red:0.20, green:0.25, blue:0.30, alpha:1.00)
    static let lightText = UIColor(red: 1.00, green: 1.00, blue: 1.00, alpha: 1.00)
    static let defaultBackground = UIColor(red:0.69, green:0.81, blue:0.90, alpha:1.00)

}
import UIKit

class ViewController: UIViewController {

    // MARK: - View Life Cycle

    override func viewDidLoad() {
        super.viewDidLoad()

        // Configure View
        view.backgroundColor = .defaultBackground
    }

}

You can also use class computed properties to accomplish a similar result. The difference is that a new UIColor instance is created every time you access the computed property.

import UIKit

extension UIColor {

    class var base: UIColor { return UIColor(red:1.00, green:0.37, blue:0.36, alpha:1.00) }
    class var darkText: UIColor { return UIColor(red:0.20, green:0.25, blue:0.30, alpha:1.00) }
    class var lightText: UIColor { return UIColor(red: 1.00, green: 1.00, blue: 1.00, alpha: 1.00) }
    class var defaultBackground: UIColor { return UIColor(red:0.69, green:0.81, blue:0.90, alpha:1.00) }

}

More Applications

You can apply this technique to various other aspects of your project, such as fonts. The following example is a simple implementation of this technique.

extension UIFont {

    static let regular = UIFont(name: "Avenir-Book", size: 12.0)!
    static let light = UIFont(name: "Avenir-Light ", size: 12.0)!
    static let bold = UIFont(name: "Avenir-Black", size: 12.0)!

    class func regular(size: CGFloat) -> UIFont {
        return UIFont(name: "Avenir-Book", size: size)!
    }

    class func light(size: CGFloat) -> UIFont {
        return UIFont(name: "Avenir-Light", size: size)!
    }

    class func bold(size: CGFloat) -> UIFont {
        return UIFont(name: "Avenir-Black", size: size)!
    }

}

Configuring views becomes easy and painless.

private func setupMainLabel() {
    mainLabel.font = UIFont.light(size: 16.0)
}

We can slightly improve the above example using another nice pattern in Swift, namespaces.

struct Styles {

    private init() {}

    struct Fonts {

        private init() {}

        static let small: CGFloat = 12.0
        static let large: CGFloat = 24.0
        static let regular: CGFloat = 16.0

    }

}
private func setupMainLabel() {
    mainLabel.font = UIFont.light(size: Styles.Fonts.regular)
}

This looks pretty nice. Doesn't it? Later this week, I show you how to take it one step further.