简体   繁体   中英

Swift KeyPath on optional value

If I have

class Info {
    var name: String?
}

class User {
    var info: Info?
}

class WrappedClass {
    var user: User = User()
}


let nameKeyPath = \WrappedClass.user.info?.name //Gets me KeyPath
let referencedNameKeyPath = \WrappedClass.user.info!.name //Gets me ReferenceWritableKeyPath

nameKeyPath gets me a KeyPath which I can't later on use to modify the name value, but if I force unwrap it I get a ReferenceWritableKeyPath which is what I'm after.

Unfortunately using referencedNameKeyPath with a nil value along the line expectedly crashes, due to unexpectedly finding nil.

My question is is there a way to convert the KeyPath to a ReferenceWritableKeyPath or somehow unwrap it along the way?

You can also use optional chaining on keypaths. Create two keypaths one for user and the other one for name in info.

let infoKeyPath = \WrappedClass.user.info

let nameKeyPath = \Info.name

Now, use optional chaining with keypath and it will yield String? as the result.

let name = wrappedInstance[keyPath: infoKeyPath]?[keyPath: nameKeyPath]

Try having the key path as a computed optional variable. Like that:

class WrappedClass {
  var user: User = User()

  var nameKeyPath: ReferenceWritableKeyPath<WrappedClass, String?>? {
    guard let _ = user.info else { return nil }
    return \WrappedClass.user.info!.name
  }
}

You still end up with the force unwrap notation but it should not cause any issues since you specifically guard for it.

That way you can use the computed key path in a safe and convenient way:

let instance = WrappedClass()

if let nameKeyPath = instance.nameKeyPath {
  instance[keyPath: nameKeyPath] = "Nikola"
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM