I’m reading Kent Beck’s Smalltalk Best Practice Patterns and learning a lot about how control structures work in what’s probably the purest object-oriented language. For example, if isn’t used to manage conditionals. In Smalltalk, since all things are objects, including booleans, you can just send the message -ifTrue: and -ifFalse:, with blocks, to the True object.

Let’s try that out with Objective-C:

@implementation NSNumber (IfTrue)

- (BOOL)isBoolean {  
	return [@([self objCType]) isEqualToString:@(@encode(BOOL))];  
}

- (id)ifTrue:(void (^)())onTrue {  
	if ([self isBoolean]) {  
		if ([self boolValue]) {  
			if (onTrue) onTrue();  
		} else {  
			return self;  
		}  
	}  
	return nil;  
}

- (id)ifFalse:(void (^)())onFalse {  
	if ([self isBoolean]) {  
		if ([self boolValue]) {  
			return self;  
		} else {  
			if (onFalse) onFalse();  
		}  
	}  
	return nil;  
}

@end  

When the case is not satisfied (i.e., the NSNumber is @YES, but we messaged -ifFalse: ), we return self, so that the messages can be chained, like so:

[[@YES ifTrue:^{  
	NSLog(@"this code block is entered!");  
}] ifFalse:^{  
	NSLog(@"this one is not");  
}];  

Pretty elegant! Smalltalk also allows us to ask an object to execute block if it’s nil. Hm. This actually presents a problem for us, since nil in Objective-C eats all messages and returns nil again. Our message would be swallowed and our block wouldn’t get executed. Fortunately, the runtime is here to save us! Let’s #import <objc/runtime.h>. Grab the whiskey.

Bill Bumgarner teaches us how to create our own nil object and tell the runtime that we’d like to use it instead of the built in nil. First, let’s make STNil. It eats all messages, just like good old regular nil :

@implementation STNil

+ (BOOL)resolveClassMethod:(SEL)sel { return NO; }

+ (BOOL)resolveInstanceMethod:(SEL)sel { return NO; }

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {  
	return [NSObject instanceMethodSignatureForSelector: @selector(description)];  
}

- (void)forwardInvocation:(NSInvocation *)anInvocation { }

- (void)ifNil:(void (^)())onNil {  
	if (onNil) onNil();  
}

@end  

And let’s add the -ifNil: message to NSObject as a category and make it a no-op:

@implementation NSObject (IfNil)

- (void)ifNil:(void (^)())onNil { }

@end  

And then lets use _objc_setNilReceiver (which is private!) to set STNil as the nil receiver;

extern id _objc_setNilReceiver(id newNilReceiver);

int main(int argc, const char **argv) {  
	@autoreleasepool {  
		id smalltalkyNil = [STNil new];  
		_objc_setNilReceiver(smalltalkyNil);
		
		NSString *nilString = nil;
		
		[nilString ifNil:^{  
			NSLog(@"this object is nil");  
		}];
		
		_objc_setNilReceiver(nil);
		
	}  
	return 0;  
}  

This works! We can now message nil as easily as any other object.

Let’s take it even further. In Ruby on Rails, objects support something called presence. Presence makes truthiness behave in a more useful and consistent way. Nil, empty strings, and empty collections are “not present” (or “blank”), so you can call myArray.blank instead of the more convoluted myArray.count == 0. Let’s bring that stuff over to this new block based model as well.

Let’s put some more stuff on NSObject :

@implementation NSObject (Presence)

- (BOOL)present {  
	return YES;  
}

- (BOOL)blank {  
	return !self.present;  
}

- (void)ifPresent:(void (^)())onPresent {  
	if (self.present && onPresent) onPresent();  
}

- (void)ifBlank:(void (^)())onBlank {  
	if (self.blank && onBlank) onBlank();  
}

@end  

And then, all we have to do create a category on NSArray that overrides present (and optionally forward -ifEmpty: to -ifBlank: ):

@implementation NSArray (Presence)

- (BOOL)present {  
	return !!self.count;  
}

- (void)ifEmpty:(void (^)())onEmpty {  
	[self ifBlank:onEmpty];  
}

@end  

Now, we can call -ifEmpty: exactly as we’d expect to:

[@[] ifEmpty:^{  
	NSLog(@"this array is empty!");  
}];  

Who knew Smalltalk could be so much fun?