Suggestion: add `newValue` to `didSet`

Found myself in a situation where having newValue or some new currentValue variable would help in the body of didSet where currently only oldValue special variable is available:

struct Struct {
    var somePropertyName: String {
        didSet { doSomething(newValue) } // or currentValue
    }
    var anotherPropertyName: String {
        didSet { doSomething(newValue) } // copy pasted
    }
    var yetAnotherPropertyName: String {
        didSet { doSomething(newValue) } // copy pasted
    }
    
    func doSomething(_ v: String) {
        // ...
    }
}

Without it I need to use the name of the property explicitly which is visually somewhat more noisy, somewhat harder to parse and process mentally and somewhat more error prone:

var yetAnotherPropertyName: String {
    didSet { doSomething(anotherPropertyName) } // oops, copy-paste error
}

I wonder if this is frequent enough use case and if it's worth to consider for an addition to swift.

1 Like

Have you considered using willSet? That provides you with newValue.

If you've got a scenario where you must do something only after the value is set, this sort of copy and paste is still a good sign that you can consider refactoring.

The very powerful @dynamicMemberLookup feature allows you to write a wrapper type so that you can make sure that setting any property triggers calling the function you want, without requiring you to copy and paste your code over and over again:

// Storage type.
struct _S {
    var a, b, c: String
}

// Wrapper type.
@dynamicMemberLookup
struct S {
    var _s: _S
    func f(_: String) {
        print("Hello, World!")
    }
    subscript(dynamicMember keyPath: WritableKeyPath<_S, String>) -> String {
        get { _s[keyPath: keyPath] }
        set {
            _s[keyPath: keyPath] = newValue
            f(newValue)
        }
    }
}

You can even make the wrapping type generic so that multiple different struct types can have that functionality.

3 Likes

Maybe I’m misunderstanding the question, but can’t you just use the property name in the didSet?

2 Likes

I can, just as I said it is "visually somewhat more noisy, somewhat harder to parse and process mentally and somewhat more error prone". It is analogous to using a function name instead of #function:

var somePropertyName: String {
    didSet {
        print("somePropertyName set")
        doSomething(somePropertyName)
    }
}
var anotherPropertyName: String {
    didSet {
        print("anotherPropertyName set")
        doSomething(anotherPropertyName)
    }
}

vs

var somePropertyName: String {
    didSet {
        print("\(#function) didSet")
        doSomething(newValue) // or a new `currentValue`
    }
}
var anotherPropertyName: String {
    didSet {
        print("\(#function) didSet")
        doSomething(newValue)
    }
}
1 Like

Exactly! :slight_smile:

func foo () {
   print ("foo", "running...")
   ...
   print ("foo", "finished.")
}

func jibberJabber () {
   print ("jibberJabber", "running...")
   ...
   print ("jibberJabber", "finished.")
}

Versus:

func foo () {
   print (#function, "running...")
   ...
   print (#function, "finished.")
}

func jibberJabber () {
   print (#function, "running...")
   ...
   print (#function, "finished.")
}