A more generic builder pattern for Objective-C

From pizza builders to mutable pizza's

A few weeks ago I came across this blog post about a builder pattern for Objective-C. It was one of our interns who pointed me to the article and asked if I considered this to be a good idea. Here’s what it looks like:

Pizza *pizza = [Pizza pizzaWithBlock:^(PizzaBuilder *builder]) {
        builder.size = 12;
        builder.pepperoni = YES;
        builder.mushrooms = YES;
    }];

At first I dismissed it altogether. Being familiar with the builder pattern in C#, I considered this Objective-C variant to be somewhat contrived and lacking the typical fluent syntax characterized by method chaining.

A week or two later, I watched a video about Building Paper and learned about Pop, the animation engine that was released by Facebook as (yet another) open source framework for iOS. Looking further into this the next day, I encountered the following code snippet in the Pop documentation:

prop = [POPAnimatableProperty propertyWithName:@"com.foo.radio.volume"
                                       initializer:^(POPMutableAnimatableProperty *prop) {
        // read value
        prop.readBlock = ^(id obj, CGFloat values[]) {
            values[0] = [obj volume];
        };
        // write value
        prop.writeBlock = ^(id obj, const CGFloat values[]) {
            [obj setVolume:values[0]];
        };
        // dynamics threshold
        prop.threshold = 0.01;
    }];

It’s that same builder pattern again! However, there is one small but important thing that the Facebook example makes obvious and that I failed to realize before: there is no need to add dedicated builder objects to Objective-C because they are already present in the language in the form of mutable objects. In fact, this allows us to implement the builder patter in a more generic way, e.g. as a category on NSObject:

@interface NSObject (Builder)

    - (instancetype)initUsingBlock:(void(^)(id mutableCopy))block;

    @end

    @implementation NSObject (Builder)

    - (instancetype)initUsingBlock:(void(^)(id mutableCopy))block
    {
        self = [self init];
        if (self && block)
        {
            id mutableCopy = [self mutableCopy];
            block(mutableCopy);
            self = [mutableCopy copy];
        }
        return self;
    }

    @end

There are some obvious limitations with this simple approach (most notably it only works for default initializers), but without adding anything else we can now start writing code like this:

NSParagraphStyle *paragraphStyle =
    [[NSParagraphStyle alloc] initUsingBlock:^(NSMutableParagraphStyle *paragraphStyle) {
        paragraphStyle.alignment = NSTextAlignmentCenter;
        paragraphStyle.lineSpacing = 0.0;
    }];
    
    NSDictionary *attributes = @{ NSParagraphStyleAttributeName : paragraphStyle };
    
    NSAttributedString *text =
    [[NSAttributedString alloc] initUsingBlock:^(NSMutableAttributedString *text) {
        [text appendAttributedString:[[NSAttributedString alloc] initWithString:paragraph1]];
        [text appendAttributedString:[[NSAttributedString alloc] initWithString:paragraph2
                                                                     attributes:attributes]];
        [text appendAttributedString:[[NSAttributedString alloc] initWithString:paragraph3]];
    }];

The same implementation can be reused for all other classes with mutable variants. Instead of implementing a PizzaBuilder class with a build method, and adding an initWithBuilder method in the Pizza class, we just need to implement a MutablePizza class and the default copy methods. This limits boilerplate code to a bare minimum and makes the builder pattern a more natural fit for the language.