dziobaczy
dziobaczy

Reputation: 931

Swift can't infer conditional type in a loop

When I'm casting views with case let Swift properly recognizes the type of circle to PinCircleView inside the loop

 for case let (index, circle) as (Int, PinCircleView) in pinCirclesStackView.arrangedSubviews.enumerated() {
    print(index, circle) // <- circle is PinCircleView and print's as expected 
 }

But if I'm type checking with is Swift still only allows me to access circle as it would be UIView

for (index, circle) in pinCirclesStackView.arrangedSubviews.enumerated() where circle is PinCircleView {
  print(index, circle) // <- circle is UIView but print's PinCircleView
}

If I'm understanding correctly is does not cast but only checks the type, but this leads me to another question if the type is matched why can't I "fully" use it?

Upvotes: 0

Views: 140

Answers (1)

vacawama
vacawama

Reputation: 154513

Swift types are established at compile time.

In the first case, the case let only assigns values to (index, circle) if the types are (Int, PinCircleView). So at compile time, Swift knows circle is of type PinCircleView. You can verify this by option-clicking on circle.

for case let (index, circle) as (Int, PinCircleView) in pinCirclesStackView.arrangedSubviews.enumerated() {
    print(index, circle) // <- circle is PinCircleView and print's as expected
}

In the second case, (index, circle) gets assigned every value from arrangedSubviews.enumerated(). In that case, arrangedSubviews returns [UIView] and .enumerated() turns that into a sequence of (Int, UIView) tuples. So in this case, circle is a UIView.

The where circle is PinCircleView then checks if circle contains the PinCircleView subclass of UIView, but it doesn't change the fact that circle is typed as UIView. Again, use option-click on circle to verify that its type is UIView.

for (index, circle) in pinCirclesStackView.arrangedSubviews.enumerated() where circle is PinCircleView {
    print(index, circle) // <- circle is UIView but print's PinCircleView
}

Inside of the for, you know that circle contains a PinCircleView, but Swift doesn't. You could downcast it inside of the loop with let pincircle = circle as! PinCircleView and know that it would always work without crashing because you checked it using is.

But the better form is to use the for case let version to properly establish the type of circle as PinCircleView.

Upvotes: 3

Related Questions