Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to set a property on UIView via Swift 4 KeyPaths in an extension?

I'm trying to create a set method on UIView via an extension that will allow me to set a color via the new Swift 4 KeyPaths. If I do the following I get the error Cannot assign to immutable expression of type 'UIColor?'

extension UIView {
    func set(color: UIColor, forKeyPath path: KeyPath<UIView, UIColor?>) {
        self[keyPath: path] = color // 🛑 Error: Cannot assign to immutable expression of type 'UIColor?'
    }
}
view.set(color: .white, forKeyPath: \.backgroundColor)

If I use this outside of an extension it works fine:

let view = UIView()
let path = \UIView.backgroundColor
view[keyPath: path] = .white // Works fine

Also using the old style of KeyPath works fine:

extension UIView {
    func set(color: UIColor, forKeyPath path: String) {
        self.setValue(color, forKey: path)
    }
}
view.set(color: .white, forKeyPath: #keyPath(UIView.backgroundColor))

Thanks for any help.

like image 399
Radther Avatar asked Jul 08 '17 10:07

Radther


1 Answers

In your standalone example, if you option-click on path you will see that its declaration is:

let path: ReferenceWritableKeyPath<UIView, UIColor?>

So it isn't just a KeyPath but a ReferenceWritableKeyPath. Clicking on ReferenceWritableKeyPath reveals that it is:

A key path that supports reading from and writing to the resulting value with reference semantics.

So the KeyPath type that you are using in your extension is too restrictive as it doesn't allow writing.

Changing KeyPath to ReferenceWritableKeyPath passes along the correct type makes it work:

extension UIView {
    func set(color: UIColor, forKeyPath path: ReferenceWritableKeyPath<UIView, UIColor?>) {
        self[keyPath: path] = color
    }
}

view.set(color: .white, forKeyPath: \.backgroundColor)  // success!
like image 99
vacawama Avatar answered Oct 21 '22 23:10

vacawama