Type casting in for-in loop

For LoopSwiftFor in-Loop

For Loop Problem Overview


I have this for-in loop:

for button in view.subviews {
}

Now I want button to be cast into a custom class so I can use its properties.

I tried this: for button in view.subviews as AClass

But it doesnt work and gives me an error:'AClass' does not conform to protocol 'SequenceType'

And I tried this: for button:AClass in view.subviews

But neither does that work.

For Loop Solutions


Solution 1 - For Loop

For Swift 2 and later:

Swift 2 adds case patterns to for loops, which makes it even easier and safer to type cast in a for loop:

for case let button as AClass in view.subviews {
    // do something with button
}

Why is this better than what you could do in Swift 1.2 and earlier? Because case patterns allow you to pick your specific type out of the collection. It only matches the type you are looking for, so if your array contains a mixture, you can operate on only a specific type.

For example:

let array: [Any] = [1, 1.2, "Hello", true, [1, 2, 3], "World!"]
for case let str as String in array {
    print(str)
}

Output:

> Hello > World!


For Swift 1.2:

In this case, you are casting view.subviews and not button, so you need to downcast it to the array of the type you want:

for button in view.subviews as! [AClass] {
    // do something with button
}

Note: If the underlying array type is not [AClass], this will crash. That is what the ! on as! is telling you. If you're not sure about the type you can use a conditional cast as? along with optional binding if let:

if let subviews = view.subviews as? [AClass] {
    // If we get here, then subviews is of type [AClass]
    for button in subviews {
        // do something with button
    }
}

For Swift 1.1 and earlier:

for button in view.subviews as [AClass] {
    // do something with button
}

Note: This also will crash if the subviews aren't all of type AClass. The safe method listed above also works with earlier versions of Swift.

Solution 2 - For Loop

This option is more secure:

for case let button as AClass in view.subviews {
}

or swifty way:

view.subviews
  .compactMap { $0 as AClass }
  .forEach { .... }

Solution 3 - For Loop

The answers provided are correct, I just wanted to add this as an addition.

When using a for loop with force casting, the code will crash (as already mentioned by others).

for button in view.subviews as! [AClass] {
    // do something with button
}

But instead of using an if-clause,

if let subviews = view.subviews as? [AClass] {
    // If we get here, then subviews is of type [AClass]
    ...
}

another way is to use a while-loop:

/* If you need the index: */
var iterator = view.subviews.enumerated().makeIterator()
while let (index, subview) = iterator.next() as? (Int, AClass) {
    // Use the subview
    // ...
}

/* If you don't need the index: */
var iterator = view.subviews.enumerated().makeIterator()
while let subview = iterator.next().element as? AClass {
    // Use the subview
    // ...
}

Which seems to be more convenient if some elements (but not all) of the array might be of type AClass.

Although for now (as of Swift 5), I'd go for the for-case loop:

for case let (index, subview as AClass) in view.subviews.enumerated() {
    // ...
}

for case let subview as AClass in view.subviews {
    // ...
}

Solution 4 - For Loop

You can also use a where clause

for button in view.subviews where button is UIButton {
    ...
}

Solution 5 - For Loop

The answer provided by vacawama was correct in Swift 1.0. And no longer works with Swift 2.0.

If you try, you will get an error similar to:

> '[AnyObject]' is not convertible to '[AClass]';

In Swift 2.0 you need to write like:

for button in view.subviews as! [AClass]
{
}

Solution 6 - For Loop

You can perform the casting and be safe at the same time with this:

for button in view.subviews.compactMap({ $0 as? AClass }) {

}

Solution 7 - For Loop

If you want to have an index in your loop:

for case let (index, button as AClass) in view.subviews.enumerated() {
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionArbiturView Question on Stackoverflow
Solution 1 - For LoopvacawamaView Answer on Stackoverflow
Solution 2 - For LoopoberView Answer on Stackoverflow
Solution 3 - For Loopj3141592653589793238View Answer on Stackoverflow
Solution 4 - For Loopedelaney05View Answer on Stackoverflow
Solution 5 - For LoopMidhun MPView Answer on Stackoverflow
Solution 6 - For LoopnandodelauniView Answer on Stackoverflow
Solution 7 - For LoopmelvinrudolphView Answer on Stackoverflow