Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - avoiding nested forEach closures?

Let's say I have an array of closures that I want to run on every UITouch. Here's the code I use:

touches.filter { touch in
    return touch.phase == .Ended && touch.tapCount == 1
}.forEach { touch in
    actionsOnTap.forEach { action in
        action(touch)
    }
}

It bugs me that there's nested forEach statement, and I guess there's some clean way that can be applied exactly for that case, but I can't think of it. Can anyone give me a hint?

like image 750
Alexander Woodblock Avatar asked May 25 '16 18:05

Alexander Woodblock


3 Answers

Personally, I like the nesting. I would write:

for touch in touches {
    if touch.phase == .Ended {
        if touch.tapCount == 1 {
            actionsOnTap.forEach {$0(touch)}
        }
    }
}

To me, that's clean and (above all) clear.

like image 182
matt Avatar answered Nov 15 '22 00:11

matt


You should definitely eliminate the filter from your logic and possibly use a guard inside the first loop instead, for the sake of efficiency and conciseness. I also agree with @Rob's and @matt's suggestion of using a traditional for loop instead of forEach – at least for the first loop.

Although a (maybe even cleaner) alternative is the integrate the touch conditional logic into the for loop directly through using the where clause, as well as possibly folding your forEach into a single line (whichever you find more readable).

I'd write it like this:

for touch in touches where touch.phase == .Ended && touch.tapCount == 1 {
    actionsOnTap.forEach{$0(touch)}
}
like image 35
Hamish Avatar answered Nov 14 '22 22:11

Hamish


This is a good example of why forEach is not a universal (or even appropriately common) replacement for for-in. This code become shorter (140 chars vs 186 chars) and clearer just using a traditional for loop:

for touch in touches where touch.phase == .Ended && touch.tapCount == 1 {    
    for action in actionsOnTap {
        action(touch)
    }
}

It also doesn't create an extra array copy the way the filter does. This isn't a general reason not to use filter. filter is a very powerful tool that should be used often, but in this case, it's clearer and more efficient to use for.

Edited to use @originaluser2's suggestion of where rather than guard. That probably is better Swift.

like image 6
Rob Napier Avatar answered Nov 14 '22 23:11

Rob Napier