Better Class Variables with Settings Objects

What a week! WWDC 2014 was chock full of exciting news for developers, and we at Two Toasters ate it up. We’re super excited about extensions, custom keyboards, Hand-off, size classes, and of course Swift. There’s a lot to digest, but rest assured that we’ll be discussing iOS 8’s new APIs and technologies here in the coming weeks and months.

Today though, we’re going to discuss a really simple design pattern that works around one of Objective-C’s shortcomings: the lack of class variables. When we started working on URLMock, we found ourselves in a bind. Because of NSURLProtocol’s architecture, URLMock’s primary interface is the UMKMockURLProtocol class itself, not instances of it. To add an expected mock request, you have to use +expectMockRequest:; to verify that your URL code is working as expected, you have to use +verifyWithError:. This means that all our bookkeeping data—whether URLMock is enabled, whether verification is enabled, what mock requests are expected, which unexpected requests have been received, etc.—have to be stored at the class level.

Objective-C doesn’t support class variables. In most cases, this isn’t a big deal. You can use a static variable and write accessor methods to accomplish what you need:

static BOOL UMKMockURLProtocol_enabled;
static NSMutableArray *UMKMockURLProtocol_expectedMockRequests;

// Private methods
+ (BOOL)isEnabled
{
    return UMKMockURLProtocol_enabled;
}

+ (void)setEnabled:(BOOL)enabled
{
    UMKMockURLProtocol_enabled = enabled;
}

…

// Public methods
+ (void)enable
{
    [self setEnabled:YES];
    // Perform other necessary tasks
}

+ (void)disable
{
    [self setEnabled:NO];
    // Perform other necessary tasks
}

That’s not too bad, right? Unfortunately, these static variables are accessible everywhere in our implementation file, including inside any functions, categories, or secondary classes we implement, so we need to be disciplined to avoid any mishaps. One way to help is to prefix our variable names with our class’s name to distinguish them from non-class variables. To give them initial values, we override +initialize:

+ (void)initialize
{
    if (self == [UMKMockURLProtocol class]) {
        UMKMockURLProtocol_enabled = NO;
        UMKMockURLProtocol_expectedMockRequests = [[NSMutableArray alloc] init]];
    } 
}

This isn’t too bad either, though it’s definitely getting worse. A lot of Objective-C programmers aren’t familiar with +initialize, and even fewer understand why we wrapped our initializers in that if statement, but hey, they’ll learn right?

Note too that our accessors are not atomic. Atomic accessors are arguably more useful for class variables than instance variables, since class accessor methods can be easily invoked from any number of threads simultaneously. While writing atomic accessors isn’t horrible, it’s horrible enough with all this other code that I’ve had to write that I’m whiney and sick of this class variable thing and want a better way.

A Better Way

The problem with class variables in Objective-C is that we don’t get any of the niceties of declared properties. We muck up our namespace with these static variables that we had to declare, and we waste time writing atomic accessor methods. Things would be so much better if we could just declare class variables like properties. So, let’s try that.

The game plan is simple. Let’s create a simple private class called UMKMockURLProtocolSettings. Its only purpose is to store and manage our class variables. We’ll declare it like so:

@interface UMKMockURLProtocolSettings : NSObject

@property (atomic, assign, getter = isEnabled) BOOL enabled;
@property (atomic, assign, getter = isVerificationEnabled) BOOL verificationEnabled;
@property (atomic, strong, readonly) NSMutableArray *expectedMockRequests;
@property (atomic, strong, readonly) NSMutableArray *unexpectedRequests;
@property (atomic, strong, readonly) NSMutableDictionary *servicedRequests;

// And so on…

@end

Our implementation is pretty trivial:

@implementation UMKMockURLProtocolSettings

- (instancetype)init
{
    self = [super init];
    if (self) {
        _expectedMockRequests = [[NSMutableArray alloc] init];
        _unexpectedRequests = [[NSMutableArray alloc] init];
        _servicedRequests = [[NSMutableDictionary alloc] init];
        // And so on…
    }

    return self;
}

@end

In UMKMockURLProtocol, we declare a private class method that lazily instantiates an instance of this class:

+ (UMKMockURLProtocolSettings *)settings
{
    static UMKMockURLProtocolSettings *settings = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        settings = [[UMKMockURLProtocolSettings alloc] init];
    });

    return settings;
}

And that’s it. Because the settings variable is declared static inside a method, it doesn’t pollute our namespace. Whenever we want to access a class variable, we first invoke +settings, and then access the variable in question:

+ (void)enable
{
    self.settings.enabled = YES;
    // Perform other necessary tasks
}

+ (void)disable
{
    self.settings.enabled = NO;
    // Perform other necessary tasks
}

+ (void)expectMockRequest:(id<UMKMockURLRequest>)mockRequest
{
    …
    [self.settings.expectedMockRequests addObject:mockRequest];
}

Now we have class variables with atomic accessors that are initialized in a way any Objective-C programmer should understand. But wait, there’s more! Since we now have a dedicated object that manages our class variables, we can add methods to it. For example, URLMock users can reset UMKMockURLProtocol’s various bookkeeping data structures using +reset. It basically clears out these collections in a thread-safe manner. We can implement this in the settings class itself:

@implementation UMKMockURLProtocolSettings

…

- (void)reset
{
    // Do some thread safety stuff
    [self.expectedMockRequests removeAllObjects];
    [self.unexpectedRequests removeAllObjects];
    [self.servicedRequests removeAllObjects];
}

@end

In UMKMockURLProtocol, we just send the settings object the +reset message:

+ (void)reset
{
    [self.settings reset];
}

Summary

The concept of a settings object is incredibly simple, but it buys you a lot. First and foremost, it’s easier for most Objective-C programmers to understand and use correctly. It doesn’t require that you name your variables in a special way or use lower-level initialization patterns that are unfamiliar to most developers. You don’t have to pollute your file’s namespace with variables that should only be accessible to your class. You get atomic accessors for free. Finally, you can manage all of your class-level data in one place, which keeps concerns nicely separated and reduces the likelihood of mistakes. We’ve used it in a few different spots in URLMock and other projects at Two Toasters, and we’re really pleased with how much nicer it makes our code. Take a look at UMKMockURLProtocol’s implementation for a more full-fledged example.

And that’s it for this week’s post. See you next week, same Bat-time, same Bat-channel!

Tweet about this on Twitter