Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Implicitly unwrapped property" warning?

Tags:

swift

swift5

I have a class where I have a property like this:

var residenceId: Int!

When building with Xcode 10 and Swift 4.2 there are no issues. However, after installing Xcode 11 Beta and converting to Swift 5 I get the following warning:

Implicitly unwrapped property 'residenceId' declared here

Later in the class I also have this function:

func jsonDictionary() -> [String : Any] {
    return ["residenceId": residenceId]
}

Here I get the warning

Coercion of implicitly unwrappable value of type 'Int?' to 'Any' does not unwrap optional

Are we no longer allowed to use implicitly unwrapped optional?

enter image description here

EDIT:

After some more looking into it, I have come to believe that "Implicitly unwrapped property 'residenceId' declared here" is not actually a warning, but rather some info (it is in a grey label rather than usual yellow) to help me understand why I am getting the second warning.

And to clarify, my question is if we are no longer able to use the '!' symbol on properties to define an implicitly unwrapped property (obviously only if we are sure it will not be nil) in order to later avoid explicitly unwrapping it (and thus simplifying the code).

like image 824
pajevic Avatar asked Sep 09 '19 09:09

pajevic


People also ask

What are implicitly unwrapped optionals?

Checking an optionals value is called “unwrapping”, because we're looking inside the optional box to see what it contains. Implicitly unwrapping that optional means that it's still optional and might be nil, but Swift eliminates the need for unwrapping.

Why are outlets defined as implicitly unwrapped optionals?

It's only after the view controller is initialized that it loads its view. This also means that any outlets declared in the view controller class don't have a value immediately after the view controller's initialization. That's why an outlet is always declared as an (implicitly unwrapped) optional.


1 Answers

Since Swift 4, ImplicitlyUnwrappedOptional or ! as we knew it, became Optional.

Check:

let a: ImplicitlyUnwrappedOptional<Int> = 1

will spit out the error:

'ImplicitlyUnwrappedOptional' has been renamed to 'Optional'

So instead if we do:

let a: Int! = 1
print(type(of: a)) //will print "Optional<Int>"

It's still Optional<Int> but indicates to the compiler that it can be implicitly unwrapped.

Implicit Unwrapping is Part of a Declaration.

...

consider ! to be a synonym for ? with the addition that it adds a flag on the declaration letting the compiler know that the declared value can be implicitly unwrapped.
Ref: Reimplementation of Implicitly Unwrapped Optionals


Now getting to the main question:

If you do:

let a: Int! = 1

let b: Any = a
print(type(of: b)) //prints "Optional<Int>"

It will give the following warning:

Expression implicitly coerced from 'Int?' to 'Any'

or as per Xcode 11

Coercion of implicitly unwrappable value of type 'Int?' to 'Any' does not unwrap optional

Note here that we tried to get a non-optional Any out of an Int? which means we were basically expecting an Int but just by specifying Any it won't also unwrap the Optional.
It will remain an Optional, and this is the meaning behind that warning.


Solutions:

To handle this warning gracefully, we can do any of the following:

let a: Int! = 1

let b: Any? = a
type(of: b) //Optional<Any>.Type

let c: Any! = a
type(of: c) //Optional<Any>.Type

let d: Any = a!
type(of: d) //Int.Type

EDIT: (based on comment)

! instead of ? have any practical difference for the programmer?

! tells the compiler that it can be implicitly unwrapped so it can help ease in the need for optional chaining.

Example:

  • With ?

    class A {
        var n: Int? = 1
    }
    
    class B {
        var a: A? = A()
    }
    
    let b: B? = B()
    print(b?.a?.n)
    
    /*
     but the following won't compile as compiler
     will not implicitly unwrap optionals
    
     print(b.a.n)
     */
    
  • With !

    class A {
        var n: Int! = 1
    }
    
    class B {
        var a: A! = A()
    }
    
    let b: B! = B()
    print(b.a.n) //compiler will implicitly unwrap `!` similar to print(b!.a!.n)
    
    //also... you can still safely unwrap if you want
    print(b?.a?.n)
    
    • b.a.n and b?.a?.n will both give an Optional<Int> at the end
    • Ofcourse if b or a is nil then b.a.n will crash because it's implicitly unwrapping b and a to get to n which is Optional<Int>
    • To get Int instead of Optional<Int>, you would do b.a.n! and so in your case you would do: print(residenceId!) to get Int

I hope I made sense

like image 184
staticVoidMan Avatar answered Nov 13 '22 00:11

staticVoidMan