Marco Boschi
Marco Boschi

Reputation: 2333

Strange error with KeyPath on optional value

Consider the following snippet of code

class A {
    
    var value: Int?
    
}

let a: A? = A()
let kp = \A.value
a?[keyPath: kp] = 10
print(a?.value)

This works perfectly and Optional(10) is printed as expected. In my actual app the field I'm trying to set this way is declared as Date? and it causes some strange errors. The MWE of my actual app is this:

class A {
    
    var value: Date?
    
}

let a: A! = A()
let kp = \A.value
a?[keyPath: kp] = Date() // Line with error
print(a?.value)

However the compiler complains about the highlighted line and says:

Value of optional type 'Date?' must be unwrapped to a value of type 'Date'

Fix: Coalesce using '??' to provide a default when the optional value contains 'nil'

Fix: Force-unwrap using '!' to abort execution if the optional value contains 'nil'

Is this a bug of the compiler we can expect to be fixed before the final release or there's something I'm not understanding about key-paths?

I'm using Xcode 11 beta 3 but I've encountered the same problem also in beta 2. If it can be useful the actual code is here.

Upvotes: 7

Views: 1281

Answers (2)

Ting Yi Shih
Ting Yi Shih

Reputation: 876

I bumped into similar problem:

import Foundation

struct Outer {
    var inner: Inner

    init() {
        inner = Inner()
    }
}

struct Inner {
    var date: Date?
}

var outer = Outer()
outer[keyPath: \Outer.inner][keyPath: \Inner.date] = Date() // Line with error
print(outer.inner.date)
error: value of optional type 'Date?' must be unwrapped to a value of type 'Date'
outer[keyPath: \Outer.inner][keyPath: \Inner.date] = Date() // Line with error
                            ^
note: coalesce using '??' to provide a default when the optional value contains 'nil'
outer[keyPath: \Outer.inner][keyPath: \Inner.date] = Date() // Line with error
                            ^
                                                   ?? <#default value#>
note: force-unwrap using '!' to abort execution if the optional value contains 'nil'
outer[keyPath: \Outer.inner][keyPath: \Inner.date] = Date() // Line with error
                            ^
                                                  !

Strangely, casting the value to an optional works

outer[keyPath: \Outer.inner][keyPath: \Inner.date] = Date() as Date? // Works

Therefore, I believe a more general workaround to this problem is to explicitly cast the value to an optional as Date?

Upvotes: 1

Joakim Danielson
Joakim Danielson

Reputation: 51920

Writing this without ! and ? works

let a = A()
let kp = \A.value
a[keyPath: kp] = Date() 
print(a.value)

or only as optional

let a: A? = A()
let kp = \A.value
a?[keyPath: kp] = Date() 
print(a?.value)

Upvotes: 3

Related Questions