Implicitly unwrapped optional assign in Xcode 8
This is a consequence of SE-0054 Abolish ImplicitlyUnwrappedOptional type which has been implemented in Swift 3. Extract from that proposal (emphasis added):
However, the appearance of ! at the end of a property or variable declaration's type no longer indicates that the declaration has IUO type; rather, it indicates that (1) the declaration has optional type, and (2) the declaration has an attribute indicating that its value may be implicitly forced. ...
If the expression can be explicitly type checked with a strong optional type, it will be. However, the type checker will fall back to forcing the optional if necessary. The effect of this behavior is that the result of any expression that refers to a value declared as T! will either have type T or type T?. For example, in the following code:
let x: Int! = 5
let y = x
let z = x + 0
… x is declared as an IUO, but because the initializer for y type checks correctly as an optional, y will be bound as type Int?. However, the initializer for z does not type check with x declared as an optional (there's no overload of + that takes an optional), so the compiler forces the optional and type checks the initializer as Int.
In your case, the assignment
let foo = implicitlyUnwrappedOptionalString
makes foo
a strong optional, as in the example let y = x
from the proposal.
You could make foo
an IUO by adding an explicit type annotation
let foo: String! = implicitlyUnwrappedOptionalString
but generally you should try to get rid from IUOs in your code,
as stated in the same proposal:
Except for a few specific scenarios, optionals are always the safer bet, and we’d like to encourage people to use them instead of IUOs.
Implicitly unwrapped optional from init!() in Swift 3.1
InitTest(text: "Hello!")
returns an implicitly unwrapped optional,
which is an optional that is unwrapped if necessary. For example
you can access its properties without explicit unwrapping
let string = InitTest(text: "Hello!").text
or pass it to functions taking a (non-optional) InitTest
argument:
func foo(_ x: InitTest) { }
foo(InitTest(text: "Hello"))
But the assignment
let testResult = InitTest(text: "Hello!")
makes testResult
a regular ("strong") optional, see
SE-0054 Abolish ImplicitlyUnwrappedOptional type and Implicitly unwrapped optional assign in Xcode 8:
If the expression can be explicitly type checked with a strong optional type, it will be.
Actually I cannot think of a good reason to define an init!()
method.
Implicit Unwrapped Optional
As others have said, IUOs (implicitly unwrapped optionals) are now regular optionals with some hints to the compiler that gives it permission to unwrap them.
That being said, print is handled differently. Try using an assignment to a non-optional variable instead. That's a better test, e.g:
var a : Int! = nil
let b: Int = a
Why does captured object reference to implicitly unwrapped variable have to be unwrapped?
You can declare implicit unwrappedness, but you cannot propagate it. There is actually no such thing as an implicitly unwrapped Optional type, so when you pass or assign or capture a value declared as that type, it reverts to a normal Optional. For example:
var forced: String! = "test"
let x = forced
You will find that x
is an ordinary Optional, a String?
. Your captured value is like that.
Why optional chaining is required in the following case if the variable type is not declare explicitly?
This behaviour is introduced in SE-054: Abolish ImplicitlyUnwrappedOptional
type. The point is to limit the use of implicitly unwrapped optionals.
As the proposal describes:
If the expression can be explicitly type checked with a strong optional type, it will be.
So the "explicitly" unwrapped optional is always preferred, when no other information is provided. The key
in let key = UIWindow.key
is of type UIWindow?
.
It's only when you, e.g. provide an explicit type annotation : UIWindow
, that the right hand side being of type UIWindow?
won't typecheck anymore, and so the compiler has to implicitly unwrap the UIWindow!
.
The rationale is:
This model is more predictable because it prevents IUOs from propagating implicitly through the codebase, and converts them to strong optionals, the safer option, by default.
Why does implicitly unwrapped optional not unwrap in dictionary of type [String : Any]
Under the rules set out by SE-0054, IUOs are only force unwrapped in contexts that demand their unwrapped type. In your case, the IUO doesn't need to be force unwrapped in order to be coerced to Any
(as Any
can represent any value), so it isn't.
This behaviour is discussed in more detail in these Q&As:
- Swift 3 incorrect string interpolation with implicitly unwrapped Optionals
- Implicitly unwrapped optional assign in Xcode 8
The fact that you end up with an ImplicitlyUnwrappedOptional
value in your dictionary is legacy behaviour that has been removed in the latest Swift snapshots, in the future you will end up with an Optional
value instead (as IUO is no longer a type).
One important thing to note here however (that I'm sure will trip up people) is that the printing of IUOs got changed in 4.1.
In Swift 4.0.3, your example prints like this:
var aString: String! = "hello"
var params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": hello]
giving you the illusion that the IUO was force unwrapped when coerced to Any
. This however is just how IUOs were printed in Swift 4.0.3 – if they had a value, then they would print as that value, otherwise they would print as nil
:
var aString: String! = nil
var params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": nil]
The reason why this changed in Swift 4.1 is that ImplicitlyUnwrappedOptional
's conformance to Custom(Debug)StringConvertible
was removed in this commit in order to make progress towards removing the type itself. So now ImplicitlyUnwrappedOptional
values get printed using Swift's default printing mechanism (using reflection).
So, in a dictionary, you get the IUO's default debugDescription
, which looks like this:
let aString: String! = "hello"
let params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": Swift.ImplicitlyUnwrappedOptional<Swift.String>.some("hello")]
If you had printed it on its own, you would get its default description
, which looks like this:
let aString: String! = "hello"
print(aString) // some("hello")
This is because in Swift 4.1, the ImplicitlyUnwrappedOptional
type is implemented in the same way as Optional
, an enumeration with two cases:
public enum ImplicitlyUnwrappedOptional<Wrapped> : ExpressibleByNilLiteral {
// The compiler has special knowledge of the existence of
// `ImplicitlyUnwrappedOptional<Wrapped>`, but always interacts with it using
// the library intrinsics below.
/// The absence of a value. Typically written using the nil literal, `nil`.
case none
/// The presence of a value, stored as `Wrapped`.
case some(Wrapped)
// ...
}
For an IUO with a payload value, Swift's default reflection will therefore print it as the case some
containing the wrapped value.
But this is only temporary; the IUO type is currently (in Swift 4.1) deprecated, however it will be removed in Swift 4.2. The compiler was internally using the IUO type in quite a few places, which took quite a bit of work to remove. Therefore in 4.2 you'll have actual Optional
values in your dictionary, which will print like Optional("hello")
.
Why are implicitly unwrapped variables now printing out as some(...) in Swift 4.1?
String!
is an implicitly unwrapped optional but it's still an optional.
The value will get unwrapped to a non-optional only in situations when it has to be unwrapped, e.g. when being passed to a function that cannot take an optional. However, print
can accept an optional and String!
will be treated just as String?
.
This change actually happened in Swift 3 already as part of SE-0054.
In your example:
var aString: Int!
let aBool = true
if aBool {
aString = 2
}
print(aString)
You should not be using an implicitly unwrapped optional because since it's a var
, it get initialized to nil
. You should either handle the unassigned case explicitly by using Int?
, or, give it a default value:
let aString: Int
let aBool = true
if aBool {
aString = 2
} else {
aString = 0
}
print(aString)
Related Topics
Transport Security Has Blocked a Cleartext Http
How to Check For an Active Internet Connection on iOS or Macos
Getting Current Device Language in Ios
How to Deal With the Nsdateformatter Locale "Feechur"
Uidevice Uniqueidentifier Deprecated - What to Do Now
What's the Difference Between the Atomic and Nonatomic Attributes
How to Pass Prepareforsegue: an Object
How to Determine the Current Iphone/Device Model
How to Store Custom Objects in Nsuserdefaults
How to Size a Uitextview to Its Content
How to Get the Indexpath.Row When an Element Is Activated
Evenly Space Multiple Views Within a Container View
Having a Uitextfield in a Uitableviewcell
Handling Applicationdidbecomeactive - "How Can a View Controller Respond to the App Becoming Active"
Request Failed: Unacceptable Content-Type: Text/Html Using Afnetworking 2.0