Objective-C safe downcasting

Swift has this nice concept of optional chaining which is used in a lot of places. One really good use case is when down casting a type the operation becomes a no-op if casting to an incorrect sibling. To illustrate take a look at this Swift example code:

class FruitCompany {
  static var apple: FruitCompany { Apple() }
  static var orange: FruitCompany { Orange() }
}

class Apple: FruitCompany {
  func makeMoney() { print("💰") }
}

class Orange: FruitCompany {}

func makeMoney(with fruitCompany: FruitCompany) {
  (fruitCompany as? Apple)?.makeMoney()
}

func testOptional() {
  makeMoney(with: FruitCompany.apple) // Prints
  makeMoney(with: FruitCompany.orange) // Does nothing
}

Let’s see how this behaves in Objective-C

@interface Apple : FruitCompany
- (void)makeMoney;
@end

@interface Orange : FruitCompany
@end

@interface FruitCompany : NSObject
+ (instancetype)apple;
+ (instancetype)orange;
@end

@implementation FruitCompany
+ (instancetype)apple { return [Apple new]; }
+ (instancetype)orange { return [Orange new]; }
@end

@implementation Apple
- (void)makeMoney { NSLog(@"💰"); }
@end

@implementation Orange
@end
- (void)makeMoneyWithFruitCompany:(FruitCompany *)company
{
  [(Apple *)company makeMoney];
}

- (void)testOptional
{
  [self makeMoneyWithFruitCompany:[FruitCompany apple]]; // Prints
  [self makeMoneyWithFruitCompany:[FruitCompany orange]]; // Crash !!
}

A crash! This is the crash log

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', 
    reason: '-[Orange makeMoney]: unrecognized selector sent to instance 0x6000024f8110'

Since Objective-C supports passing message to nil, so if we explicitly do a down casting to nil it works

- (void)makeMoneyWithFruitCompany:(FruitCompany *)company
{
  Apple *apple = [company isKindOfClass:[Apple class]] ? (Apple *)company : nil;
  [apple makeMoney];
}

So, how can we make this more aligned with Swift like. The first step would be to implement a Category on NSObject that does the casting magic.

@interface NSObject (WLSafeCast)
- (id)wl_safeCastToClass:(Class)aClass;
@end

@implementation NSObject (WLSafeCast)

- (id)wl_safeCastToClass:(Class)aClass;
{
  return [self isKindOfClass:aClass] ? self : nil;
}

@end

With this one can already start using it like

- (void)makeMoneyWithFruitCompany:(FruitCompany *)company
{
  Apple *apple = [company wl_safeCastToClass:[Apple class]];
  [apple makeMoney];
}

Next step we can implement the C MACRO

#define CAST_OR_NIL(v, T) [v wl_safeCastToClass:[T class]]

With that we can start using the Swift like down casting

- (void)makeMoneyWithFruitCompany:(FruitCompany *)company
{
  Apple *apple = CAST_OR_NIL(company, Apple);
  [apple makeMoney];
}

Or can even be reduced to one line

- (void)makeMoneyWithFruitCompany:(FruitCompany *)company
{
  [CAST_OR_NIL(company, Apple) makeMoney];
}

Do you know a better way? Please share your thought or let me know on twitter