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