"Explicitly unwrapped" optionals aren't unwrapped.

With Swift 3, it seems like explicitly unwrapped optionals are no longer unwrapped. I'm not seeing this in the swift 3 migration guide notes. Can somebody help me understand the new rules on this? For example:


class Foo {
   var something: Int!
}


That's a very commpon pattern when using segue assignments. It seems now though all through my code I have to say something! (with the !) instead of just something. That means it's not really unwrapping any longer.


Confused...

Answered by rsharp in 176661022

The only change I saw in recent months was how IUOs worked with reference types. Example:


class Bar {
    func doWork() { }
}

class Foo {
    var bar : Bar!
    init() {
        bar = Bar()
    }

    func doWork() {
        bar.doWork()

        let theBar = bar

        theBar?.doWork()
    }
}


If doing directy ivar access, no need to unwrap. But if assigning the IUO to a local variable ('theBar' above), you then need to work with it differently.


I cannot speak to any changes regarding value types as in my current projects, I only use IUO for reference types (just how things turned out for me).

Accepted Answer

The only change I saw in recent months was how IUOs worked with reference types. Example:


class Bar {
    func doWork() { }
}

class Foo {
    var bar : Bar!
    init() {
        bar = Bar()
    }

    func doWork() {
        bar.doWork()

        let theBar = bar

        theBar?.doWork()
    }
}


If doing directy ivar access, no need to unwrap. But if assigning the IUO to a local variable ('theBar' above), you then need to work with it differently.


I cannot speak to any changes regarding value types as in my current projects, I only use IUO for reference types (just how things turned out for me).

That seems to jive with what I'm seeing. Is it documented somewhere that you know of?

Just found it in the original Xcode 8 beta (1) release notes:


"The behavior of implicitly-unwrapped optionals has changed. The ! syntax (e.g. NSObject!) is now only allowed directly on the type of a variable, constant, parameter, or result. An implicitly- unwrapped optional value can still be used immediately as a value of its non-optional type, but generic features such as type inference will consider it to have an ordinary optional type. [SE-0054]"

https://github.com/apple/swift-evolution/blob/master/proposals/0054-abolish-iuo.md

Just posted a reply (with links), but it's somehow in a "moderated" state. Maybe it didn't like the URL. In that case, look on the swift evolution sites for SE-0054 which covers the change. There's also a note in the original beta 8 release notes.

Wow, that's horrible! What a PITA for developers. I really wish they had added some type of "@thisIsSetBySegue" on a property. The only reason to use the ! at the end of the property was to get around not having an initializer in the UIViewController classes. Bleh.

The change is that automatic propagation of the "implicitness" has been removed, since it's now an attribute of the declaration, not the type. If you specify the type on your temporary variable, you can still get the old behaviour:


class Test {
    var name : String!

    init(name: String) {
        self.name = name
    }
   
    func printName() {
        let tempName1 = name
        print(tempName1! + " is the name") // here your have to explicitly unwrap
        let tempName2 : String! = name
        print(tempName2 + " is the name") // here you don't, since the type is the same
    }
}


When do you actually run into problems with this? Are you assigning the variable to temporary variables a lot without using it in an expression? If you actually do something with the variable directly, the behaviour is the same.


If you have some code where it really matters, please share it, and maybe it can be taken into account when designing further changes to the language.

Yes, I do assign them to temporary variables frequently. One very common case though is where the property is on a UIViewController, and then in another view controller's exit segue destination I then pull those values. Now I'm stuck unwrapping all the time when I access that.

Actually, I use the opposite solution:


    func printName() {
        let tempName1 = name!
        print(tempName1 + " is the name") // no unwrap
    }


One reason is that this is a one-character "fix" to the errors you get after the IUO behavior is lost during Swift 3 syntax conversion, rather than multiple keystrokes.


The other is that these IUO instance variables are ones you said were IUO only because of the init timing. That is, after initialization to their non-nil value, they're never nil again. So, I think it's clearer to "assert" the non-nil condition once when creating the local copy, rather than every time you reference the local copy.


Personally, I wish they'd chosen to infer the local variable type as non-optional, so that if you really wanted to propagate the IUO to the local variable, you'd do something like:


     let tempName1 = name as String?


or maybe even:


     let tempName1 = name?


After all, declaring the instance variable as IUO kinda implies you'll typically expect it to be non-nil when referenced, not occasionally.

Exactly. There's no reason for them to have done this. It just makes things harder. I do the same as you do, but it's just confusing to look at and wonder why I took something marked as EXPLICITLY unwrapped, and then had to explicitly unwrap it again.


Don't even get me started on this "Must now add 'as AnyObject'" to every other line of your code change either. Grrr....

"Explicitly unwrapped" optionals aren't unwrapped.
 
 
Q