kelan.io

Swift - didSet on Local Variables

In Chris Eidhof's recent post I learned that you can use willSet and didSet on local vars. This is just a quick write-up to explore it a bit.

Here is a simple example; just print out a message every time a variable is set. Something like this could maybe be useful for debugging?
var s1 = "initial value" {
    didSet {
        print("changed s1 to:", s1)
    }
}
Now change the variable, and see that our print() is called.
s1 = "new value"
s1 = "newer value"
changed s1 to: new value changed s1 to: newer value

Recording History

Here is another example, that keeps a history of previous values.

First, set up an array to store the history of changes, and capture that in the closure. Note that I never said this is a good idea — it's just interesting that it's possible.
var history = [String]()
var s = "initial" {
    willSet {
        history.append(s)
    }
}
Now, set it a few times.
s = "a"
s = "b"
s = "c"
s = "d"
And see the history was recorded.
print("history:", history)
history: ["initial", "a", "b", "c"]

Final Thoughts

I'm not sure if this is an intended feature, or some weird side effect of the implementation. And, I don't know what an actual good use of it is. But it sure is a neat trick! Also, you can tell this isn't a very common thing, because Xcode's auto-indent doesn't handle it very well.

But, it seems like it wasn't a total accident, because of the error message if you try to do it on a let variable.
let s = "initial" {
    didSet { print("test") }
}
error: 'let' declarations cannot be observing properties

I'm not exactly sure what "observer properties" means, but at least it correctly points out that this only make sense on things that can actually change.

Follow Up

Turns out, there are might be some practical uses for this after all. My esteemed work colleague, Marc Palmer had an inspired idea to use this to implement a DSL, based on his experience with Groovy. I'm familiar with similar tricks from Ruby (using #instance_eval and friends), but I never thought that kind of thing would be possible in Swift.

Unfortunately, he noticed a problem (in an update in his post). It appears that multiple repeated sets to the local var get optimized out, so only the last one actually triggers the didSet. So this technique isn't quite reliable yet. But maybe there is some way to fix this, with some further experimentation.