7
votes

I've run across something in my code that seems curious and was wondering if there is a straightforward explanation for this behavior. Given the following statement:

    if let tabBarController = topViewController as? UITabBarController {
        for subcontroller in tabBarController.viewControllers! {
            println(subcontroller.view)
            if let subcontrollerView = subcontroller.view {
                println(subcontrollerView)
                println(subcontrollerView!)
                if subcontrollerView!.window != nil && subcontroller.isViewLoaded() {
                    topViewController = subcontroller as? UIViewController
                    break;
                }
            }
        }
    }

Now as far as I know the if-let statement should unwrap the conditional for me--but this is not the behavior exhibited here. I cannot access the window property of subcontrollerView unless I unwrap the optional again. The x-code console returns the following:

Optional(<UILayoutContainerView: 0x7fbccd44e7f0; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x7fbccacdde90>; layer = <CALayer: 0x7fbccd440e30>>)
Optional(<UILayoutContainerView: 0x7fbccd44e7f0; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x7fbccacdde90>; layer = <CALayer: 0x7fbccd440e30>>)
<UILayoutContainerView: 0x7fbccd44e7f0; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x7fbccacdde90>; layer = <CALayer: 0x7fbccd440e30>>  

The unwrapped optional and the if-let constant are the same. Why?

1
Nothing will be unwrapped if your optional is nil.gnasher729
right, but look at the console output--it's not nilkellanburket
looks like a bug to me, can you isolate this in a tiny project? If so, you could post it here and we could take a look at it ourselves. If there still seems to be an issue that project would be great for submitting a radar to Apple.drewag
Can you do a clean (full i.e. option clean) of your project and recompile?Woodstock
Just cleaned the project and recompiled--issue wasn't resolved. I'll put together a miniproject and post it momentarily.kellanburket

1 Answers

7
votes

Your problem is AnyObject. (When in doubt, your problem is always AnyObject; it is an evil type that should be avoided as much as possible. The only thing worse is AnyObject?.)

The trouble is that tabBarController.viewControllers returns [AnyObject]?, and optional promotion probably causes things to go sideways. It's likely promoting an AnyObject? to an AnyObject?? and then getting confused. This is somewhat a compiler bug, but also just the madness that comes with AnyObject. So the answer is to get rid of it as quickly as you can.

Instead of this:

for subcontroller in tabBarController.viewControllers! {

You want this:

if let viewControllers = tabBarController.viewControllers as? [UIViewController] {
   for subcontroller in viewControllers {

So the full code is this:

if let tabBarController = topViewController as? UITabBarController {
    if let viewControllers = tabBarController.viewControllers as? [UIViewController] {
        for subcontroller in viewControllers {
            if let subcontrollerView = subcontroller.view {
                if subcontrollerView.window != nil && subcontroller.isViewLoaded() {
                    topViewController = subcontroller
                    break;
                } } } } }

But we can do better. First, optional chaining is often a better way to manage multiple if-lets, and when it doesn't work well, we can use Swift 1.2's new multi-if-let syntax to get this:

if let tabBarController = topViewController as? UITabBarController,
    viewControllers = tabBarController.viewControllers as? [UIViewController] {
        for subcontroller in viewControllers {
            if subcontroller.view?.window != nil && subcontroller.isViewLoaded() {
                topViewController = subcontroller
                break;
            } } }