I anticipate that in less than 12 months time, I will be writing almost exclusively in Swift. Many developers, companies and projects have already hit that changeover point. Objective-C isn’t quite dead yet though, and there are many people still using it.

Over the years, I’ve developed a personal library of Objective-C categories, macros and functions that have helped me to write cleaner, more concise, and more reliable code. Instead of letting this collection of Objective-C categories and macros wither away on my hard drive given the coming Swift-pocalypse, I decided that it was worth tidying them up a little bit, adding some tests, and releasing them to the (rapidly dwindling) community of Objective-C developers!

What is Bedrock?

There are a ton of open-source Objective-C libraries and frameworks out there that implement functional-style APIs on top of Foundation classes for map, filter and select, plus a bunch of other “helper” category methods. Most (if not all?) of these libraries, however, have at least one of the following issues:

  1. They don’t closely follow Apple’s Objective-C naming conventions.
  2. They don’t use prefixes for categories on system classes.
  3. They don’t make use of Objective-C lightweight generics.
  4. They don’t make use of nullability annotations.

Bedrock attempts to provide a rich library of helper and convenience methods without any of these issues, whilst not being too heavyweight.

You can find it on Github.

Here are some of the ways that Bedrock can make your life easier…

Safe Type Casting

One of the nice features of Swift is the as operator, which allows you to safely type cast a variable, eliminating an entire class of runtime crashing bugs. Swift forces you to use this approach, since it is a “safety first” language, but you can get a lot of the same benefits in the more “flexible” world of Objective-C. I’ve actually been using something very similar to Swift’s as operator in Objective-C for many years, first with a method on NSObject (when Apple introduced the instancetype keyword), then later on in the form of a macro. I was never entirely happy with the name of the macro, but as soon as I saw the Swift as operator, it was clear that this word was the perfect basis for type-safe casting in Objective-C as well! Here’s the final result in use:

id mysteryObject = @"An inanimate carbon rod";

NSString *string = AsClass(mysteryObject, NSString);
if (string != nil) {
	NSLog(@"It's a string! %@", string);
}

I generally try to avoid macros, unless they provide a really major benefit with minimal downside, but this is one scenario that, for me anyway, falls into that category. I use this macro essentially everywhere that involves a potentially unsafe downcast, which makes the resulting code a whole lot safer. Bedrock also contains an AsProtocol() macro, which takes the name of a protocol instead of the name of a class as its second argument.

Easy Generation of -[NSObject description] Strings

Out of the box, Objective-C and Apple’s Foundation framework gives you very little in terms of helpful information when you want to log the state of a custom object. Lets say you have a custom class…

@interface Person : NSObject
@property (nonatomic, strong, readwrite) NSString *name;
@property (nonatomic, assign, readwrite) NSInteger age; // in years
@end

If you want to log your Person object, you have two options – you pull out the properties you want at the point of logging, or you implement the -description or -debugDescription NSObject methods. If you try to log the object without doing these things, you’ll get something that looks like this:

<Person: 0x019482>

Implementing description methods isn’t hard, but it’s essentially tedious boilerplate, and it’s easy to just not bother until you really need it. Some frameworks (e.g. Mantle) take care of this for you automatically, but they tend to be all-or-nothing – you can’t easily add an automatically formatted -description method to a class that doesn’t heavily integrate with the framework. Bedrock provides methods that, along with some macros, make generating -description methods very straightforward. Here’s how you would implement -[Person description] using Bedrock:

- (NSString *)description
{
	return [self br_descriptionWithKeys:@[SelfKey(name), SelfKey(age)]];
}

It’s worth noting here that the SelfKey() macro is compile-time safe – it will give a compiler error if Person doesn’t have a -name or -age method. Using a Person instance in a format string or printing it in the debugger will now generate a string that looks like this:

<Person: 0x019482 { name: "Fry", age: 37 }>

Easy. Laziness is no longer an excuse not to implement -description on your classes.

Functional Collection Methods

It’s no secret that the building blocks of Foundation, NSArray, NSDictionary and NSSet have somewhat limited APIs. In the era of blocks and closures, the built-in APIs can lead to extremely verbose code, and overly verbose code means more bugs, and less readability. Bedrock provides a number of functional collection category methods that attempt to be self-documenting, so they should be usable even to those unfamiliar or inexperienced with these concepts. Some examples:

NSArray<NSString *> *names = @[@"Luke", @"Leia", @"Han", @"Darth"];
NSArray<NSString *> *fourLetterNames = [names br_allObjectsWhere:^BOOL(NSString *s) { return s.length == 4; }];
// => ["Luke", "Leia"]
NSInteger numberOfFiveLetterNames = [names br_numberOfObjectsWhere:^BOOL(NSString *s) { return s.length == 5; }];
// => 1

NSDictionary<NSString *,NSString *> *employees = @{
	@"Fry": @"Delivery Boy",
	@"Leela": @"Space Captain",
	@"Professor Farnsworth", @"Mad Scientist"
};
NSArray<NSString *> *employeeDescriptions = [employees br_arrayByMapping:^id(NSString *name, NSString *role) {
	return [NSString stringWithFormat:@"@% - @%", name, role];
}];
// => ["Fry - Delivery Boy", "Leela - Space Captain", "Professor Farnsworth - Mad Scientist"]

NSArray<NSString *> *flyingRoles = @[@"Space Captain", @"Space Pilot"];
NSDictionary<NSString *, NSString *> *employeesWhoCanFly = [employess br_allEntriesWhere:^BOOL(NSString *name, NSString *role) {
	return [flyingRoles containsObject:role];
}];
// => {"Leela": "Space Captain"}

But… Objective-C?

Yes, I know. The number of new projects that will be started from this point in time onwards (WWDC 2016 is about a week away, come and say hi if you’re heading along!) that are written in Objective-C is likely to be quite low. In fact, relative to the past 5 years (the most popular time in Objective-C’s bumpy history), it’s likely to be extremely low. The target market for this library is therefore… small. That’s okay – my intention isn’t to set the world on fire, but to collate all of the snippets and tricks that I’ve built up over the years, add tests, and put them in one place so that I (and anyone else who may be interested) can make use of them. You can find Bedrock on Github.