Why Force Unwrapping Is Required in Case of Enum and Switch

Why force unwrapping is required in case of enum and switch?

Update: This has been fixed in Swift 5.1. From the CHANGELOG:

SR-7799:

Enum cases can now be matched against an optional enum without requiring a '?' at the end of the pattern.

This applies to your case of implicitly unwrapped optionals as well:

var colours: Colours!

switch colours {
case .red:
break // colours is .red
default:
break // colours is .white, .black or nil
}

Previous answer:

When used in a switch statement, even implicitly unwrapped
optionals are not automatically unwrapped. (A reason might be that you
could not match them against nil otherwise.)

So you have to unwrap (either forcibly with
colours! which will crash if colours == nil, or with optional binding), or – alternatively – match against .Red?
which is a shortcut for .Some(.Red):

var colours: Colours!

switch colours {
case .Red?:
break // colours is .Red
default:
break // colours is .White, .Black or nil
}

The same holds for other pattern-matching expressions, e.g.

if case .Red? = colours {
// colours is .Red
} else {
// colours is .White, .Black or nil
}

Also this has nothing to do with enumeration types, only with implicitly
unwrapped optionals in a pattern:

let x : Int! = 1

switch x {
case nil:
break // x is nil
case 1?:
break // x is 1
default:
break // x is some other number
}

Unwrapping associated value for all cases in switch

Try this in Playground.

enum RowType {
case single(_ content: [Any])
case double(_ content: [Any])
case triple(_ content: [Any])
case noVal

var associatedValue: Any? {
get {
let mirror = Mirror(reflecting: self)
if let associated = mirror.children.first {
return associated.value
}
print("WARNING: Enum option of \(self) does not have an associated value")
return nil
}
}
}

let row : RowType = .double([1,2,3])
let rowNoVal : RowType = .noVal
row.associatedValue
rowNoVal.associatedValue

How does the '?' unwrap an optional in a case let declaration?

This is syntactic sugar for option patterns. The docs on option pattern says:

An optional pattern matches values wrapped in a some(Wrapped) case of an Optional<Wrapped> enumeration. Optional patterns consist of an identifier pattern followed immediately by a question mark and appear in the same places as enumeration case patterns.

Thus, your code is the same as:

var x: Int? = 42

if case .some(let a) = x {
print(a)
}

It's not typical for simple if statements as you can just do this instead:

if let a = x {
print(a)
}

But consider an enum wrapped in an optional:

enum Foo {
case bar
case baz
}

let y: Foo? = .bar

switch y {
case .none: break
case .some(.bar): break
case .some(.baz): break
}

This switch can be written in a more succinct way using some sugar:

switch y {
case nil: break
case .bar?: break
case .baz?: break
}

Swift enum's unwrapping by reference

The Swift compiler has quite a bit of built-in support for Optional; including for the postfix operators ! and ? which can produce l-values (values which reside at known locations in memory; therefore allowing for mutations of that memory if the expression is mutable).

Unfortunately I don't believe it's possible to implement your own l-value returning operators (or functions in general), although constructs that allow you to define getters and setters (such as computed properties and subscripts) can be treated as l-values when they have setters:

enum MyOptional<Type> {

case none, some(Type)

var forceUnwrapped: Type {
get {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
set {
self = .some(newValue)
}
}

// just for demonstration; don't actually implement this as a subscript!
subscript() -> Type {
get {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
set {
self = .some(newValue)
}
}
}

var test = MyOptional.some("testString")
test.forceUnwrapped.append("#")
test[].append("#")

Here, test.forceUnwrapped and test[] can be treated as l-values. When mutating through them, the compiler will create a temporary variable from calling the getter, mutate this temporary, and then call the setter with the mutated value.

Although it's worth noting in both cases that when used with assignment
(i.e test.forceUnwrapped = ... & test[] = ...), the getter won't be called; only the setter, which gives them slightly different semantics to Optional's postfix !, which will crash on the optional being nil even on assignment (i.e someOptional! = ...).

As an alternative, you could also define a method that takes a closure with an inout parameter, allowing the caller to mutate the force-unwrapped value:

enum MyOptional<Type> {

case none
case some(Type)

mutating func forceMutate<R>(_ body: (inout Type) throws -> R) rethrows -> R {
switch self {
case .some(var x):
defer {
self = .some(x)
}
return try body(&x)
case .none:
fatalError()
}
}
}

var test = MyOptional.some("testString")
test.forceMutate { $0.append("#") }

How to unwrap double optionals in switch statement -- Swift

This works, but I'm curious why I need to double unwrap the default
case? I was thinking once the lhs and rhs get unwrapped from the
switch inputs, the default case would work with a Bool? for lhs and
rhs.

You aren't changing lhs and rhs when you do lhs ?? nil and rhs ?? nil. You are creating new values. So when you get to the default case, lhs and rhs are still Bool??. You can use let lhs and let rhs to capture the unwrapped values as I did in my solution below.


Here is another way to do it. A bit cleaner with some pattern matching:

switch (lhs as? Bool, rhs as? Bool) {
case (nil, nil):
return nil
case (nil, let rhs):
return rhs
case (let lhs, nil):
return lhs
case (let lhs?, let rhs?):
return lhs || rhs
}

Explanation

Casting the Bool?? with as? Bool leaves you with a Bool?. The let rhs and let lhs in the pattern matching catch the Bool? value so that it can be returned. In the final case, let lhs? and let rhs? unwrap the Bool? values to get Bool values so that || can be performed.


Test Cases

test(nil, nil) // nil
test(nil, false) // false
test(nil, true) // true
test(nil, Optional(nil)) // nil
test(nil, Optional(false)) // false
test(nil, Optional(true)) // true

test(false, nil) // false
test(false, false) // false
test(false, true) // true
test(false, Optional(nil)) // false
test(false, Optional(false)) // false
test(false, Optional(true)) // true

test(true, nil) // true
test(true, false) // true
test(true, true) // true
test(true, Optional(nil)) // true
test(true, Optional(false)) // true
test(true, Optional(true)) // true

test(Optional(nil), nil) // nil
test(Optional(nil), false) // false
test(Optional(nil), true) // true
test(Optional(nil), Optional(nil)) // nil
test(Optional(nil), Optional(false)) // false
test(Optional(nil), Optional(true)) // true

test(Optional(false), nil) // false
test(Optional(false), false) // false
test(Optional(false), true) // true
test(Optional(false), Optional(nil)) // false
test(Optional(false), Optional(false)) // false
test(Optional(false), Optional(true)) // true

test(Optional(true), nil) // true
test(Optional(true), false) // true
test(Optional(true), true) // true
test(Optional(true), Optional(nil)) // true
test(Optional(true), Optional(false)) // true
test(Optional(true), Optional(true)) // true

raw Value of optional type not unwrapped

As featureType is an optional you have to add ? or ! as the error says

someDict["featureType"] = featureType?.rawValue ?? "" 

But be aware that your code reliably crashes when you create an instance of Chat from a dictionary and the key does not exist because there is no case "".

Actually the purpose of an enum is that the value is always one of the cases. If you need an unspecified case add none or unknown or similar.

This is a safe version

enum ChatFeatureType: String {
case none, tenants, leaseholders, residents
}

class Chat {

var featureType: ChatFeatureType

init(featureType: ChatFeatureType = .none)
self.featureType = featureType
}

//download data from firebase
init(dictionary : [String : Any]) {
featureType = ChatFeatureType(rawValue: dictionary["featureType"] as? String) ?? .none
}

func toDictionary() -> [String : Any] {

var someDict = [String : Any]()
someDict["featureType"] = featureType.rawValue
return someDict
}
}

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").

Alternative to implicitly unwrapped optional using @propertyWrapper in swift

You can simulate implicitly unwrapped optionals with a property wrapper as follows:

@propertyWrapper
struct MaybeUninitialized<T> {
private var storage: T?
var wrappedValue: T {
get { storage! }
set { storage = newValue}
}
}

Then you can even use possibly uninitialized fields storing optionals without accidentially unwrapping the optional. Something like this:

@MaybeUninitialized var x: Int?

print(x) // will crash
x = nil
print(x) // print nil

How to choose a random enumeration value

Swift has gained new features since this answer was written that provide a much better solution — see "How to choose a random enumeration value" instead.


I'm not crazy about your last case there -- it seems like you're including .GeometryClassificationMax solely to enable random selection. You'll need to account for that extra case everywhere you use a switch statement, and it has no semantic value. Instead, a static method on the enum could determine the maximum value and return a random case, and would be much more understandable and maintainable.

enum GeometryClassification: UInt32 {
case Circle
case Square
case Triangle

private static let _count: GeometryClassification.RawValue = {
// find the maximum enum value
var maxValue: UInt32 = 0
while let _ = GeometryClassification(rawValue: maxValue) {
maxValue += 1
}
return maxValue
}()

static func randomGeometry() -> GeometryClassification {
// pick and return a new value
let rand = arc4random_uniform(_count)
return GeometryClassification(rawValue: rand)!
}
}

And you can now exhaust the enum in a switch statement:

switch GeometryClassification.randomGeometry() {
case .Circle:
println("Circle")
case .Square:
println("Square")
case .Triangle:
println("Triangle")
}


Related Topics



Leave a reply



Submit