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
A 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 🙁