3 simple for-in iteration tricks

There’s a lot more to life than just simple for-in’s. Add flair to your code with these iteration power-ups.

Adding iteration requirements

where clause introduces simple predicate support during iteration. It tells Swift to skip any element that doesn’t fulfill the logic laid out in an associated requirement.

For example, you can iterate only over even cases:

for i in 1...10 where i % 2 == 0 {
    print("\(i) is even")
}

Or you might pick out long words:

for word in ["Lorem", "ipsum", "dolor", "sit", "amet", "consectetur", 
    "adipiscing", "elit"] where word.characters.count > 5 {
     print(word, "is a long word")
}

You can complicate your requirements however you like, using complex conditions in your where clauses. This example prints even numbers between 21 and 29:

for i in 1...50 where i > 20 && i < 30 && (i % 2 == 0) {
    print("\(i) is even")
}

Using where enables iteration to pick and choose which items to process.

Processing non-nil items

Optionals are the natural outcome of many Swift development scenarios, such as mapping a function over a collection or retrieving potentially failable assets.

You can combine Swift 2’s .Some case shortcut with iteration to operate only on those items that aren’t nil:

let items: [String?] = [nil, nil, "Hello", nil, "World"]
for case let item? in items {
    print(item)
}

The bound item is unwrapped within the for loop’s clause, so you can use it directly however you need.

Conditional Casting

Swift enables you to use conditional casting to filter lists of heterogenous objects to those that belong to a specific superclass. Here’s an example that extracts UIView instances.

let mySwitch = UISwitch()
let myView = UIView()
let myDate = NSDate()

let myItems: [NSObject] = [mySwitch, myDate, myView]
for case let item as UIView in myItems {
    print(item.frame)
}

This approach also works with protocols. Here’s a super-contrived case that demonstrates the approach:

protocol MyCustomProtocol {
    var frame: CGRect {get}
}

struct Bar {}
struct Foo: MyCustomProtocol {
    let frame: CGRect = .zero
}
extension UIView: MyCustomProtocol {}

let otherItems = [Bar(), Foo(), UIView()] as [Any]
for case let item as MyCustomProtocol in otherItems {
    print(item.frame)
}

A note about for var

Although I did not use for var in any of these examples but I thought I’d give you a heads up in this write-up that this mutable feature is on its way out of the Swift language. In Swift 2, the following code prints  4, 5, and 6.

for var x in 1...3 {
    x += 3
    print(x)
} // outputs 4, 5, 6

Swift 3 eliminates all for-var constructs. Replace

for var x in 1...5 {...}

with

for x in 1...5 {var x = x; ...}

The same caution holds for for case var  and all other similar approaches.

5 Comments

  • Cool stuff. The where and the case statements in a for loop make code a lot cleaner.

  • hello erica thanks of great books, what about cloudkit ,…..your evaluation

  • And, of course, you can filter an array like this:

    let subset = el in arr where el.is_good

    …Oh wait, no you can’t!

    >:(

    • Use filter
      public func filter(@noescape includeElement: (Self.Base.Generator.Element) -> Bool) -> [Self.Base.Generator.Element]

      • Thanks Erica, it’s not that the language lacks other ways to filter. It’s that, and maybe I have OCD, but it’s that the “in where” construct screams “hey, i can make an array.” But it lies 🙁