Objective C implementation of Swift's if-let This [Objective-]C-preprocessor macro currently handles single-variable if-let-not-nil and if-let-is-type, but doesn't handle multiple simultaneous tests and doesn't handle where clauses.

I prefer functions to macros, but I don't see any way to pass a local variable name to a function such that it would create a code block which can use that name internally when being compiled. Is there any way to make this a function?

This does not handle commas in code block: this can be worked around with () brackets, e.g.
     foo = @[1, 2];
         becomes
     foo = (@[1, 2]);, but I'd prefer something that doesn't surprise anyone using the macro, especially as the compiler error when commas are not wrapped in brackets is unhelpful.

#define IF_LET(tempType, temp, optional, code) \
if ((optional)!=nil &amp;&amp; [optional isKindOfClass:tempType.class]) { \
    tempType *temp = (tempType*)optional;\
    {code}\
}


example usage:

for (SKNode *node in touchedNodes) {
    IF_LET(Tile, tile, node, {
        // Tile (capital) is one of my own classes
        // tile (lowercase) is defined at this point by the macro and only available within this code block
        SKAction *scale = [SKAction scaleBy: 1.2 duration: 0.15];
        SKAction *action = ([SKAction sequence:@[scale, scale.reversedAction]]);
        [tile runAction:action];
    })
}

SKLabelNode *t = nil;
IF_LET(SKLabelNode, temp, t, {
    // doesn't run: t was nil
})

t = [SKLabelNode labelNodeWithText:@"some text"];
IF_LET(NSObject, temp, t, {
    // this block runs, letting you do stuff with the local scope variable
    // NSObject *temp = (NSObject*)t;
}) else {
    // else block available for use, but
    // this one doesn't run because t is not nil and is a kind of NSObject
}

IF_LET(NSArray, temp, t, {
    // doesn't run, t is of wrong type
})


How can I improve this macro?
My suggestion would be: Don't do it. The main disadvantages 
(in my opinion) are:


As you noticed, commas inside the macro argument are not handled,
leading to "strange" error messages:

for (SKNode *node in touchedNodes) {
    IF_LET(Tile, tile, node, { 
        // Use of undeclared identifier 'IF_LET'
        SKAction *scale = [SKAction scaleBy: 1.2 duration: 0.15];
        SKAction *action = [SKAction sequence:@[scale, scale.reversedAction]];
        // Too many arguments provided to function-like macro invocation
        [tile runAction:action];
    })
}


It is not obvious how this must be fixed, and enclosing all Objective-C
message calls feels unnatural.
Macros are not known to the debugger. As a consequence, you cannot
set a breakpoint at a certain line inside the block – the debugger will
stop at the end of the block instead. 
For the same reason you cannot single-step through the block statements. 


Some of your intended applications have alternatives in pure
Objective-C, which are (almost) as good.

Example 1: Your code

for (SKNode *node in touchedNodes) {
    IF_LET(Tile, tile, node, {
        SKAction *scale = [SKAction scaleBy: 1.2 duration: 0.15];
        SKAction *action = ([SKAction sequence:@[scale, scale.reversedAction]]);
        [tile runAction:action];
    })
}


can be replaced by

for (Tile *tile in touchedNodes) {
    if ([tile isKindOfClass:Tile.class]) {
        SKAction *scale = [SKAction scaleBy: 1.2 duration: 0.15];
        SKAction *action = ([SKAction sequence:@[scale, scale.reversedAction]]);
        [tile runAction:action];
    }
}


The fast-enumeration for statement does not care how about the
type of the loop variable, you only have to verify its class before
accessing it.

Example 2: Your code 

SKLabelNode *t = nil;
IF_LET(SKLabelNode, temp, t, {
    // doesn't run: t was nil
    [temp setText:@"text"];
})


can be replaced by (the even shorter)

SKLabelNode *t = nil;
[t setText:@"text"];


because sending Objective-C message to nil is a no-op.

Example 3: Your code

t = [SKLabelNode labelNodeWithText:@"some text"];
IF_LET(NSObject, temp, t, {
    // this block runs, letting you do stuff with the local scope variable
    // NSObject *temp = (NSObject*)t;
}) else {
    // else block available for use, but
    // this one doesn't run because t is not nil and is a kind of NSObject
}


is equivalent to

t = [SKLabelNode labelNodeWithText:@"some text"];
if (t != nil) {
    // Local scope for if-case
} else {
    // Local scope for else-case
}