Conveniently Transforming Immutable Types in Swift

A few weeks ago I wrote about Immutable Types and Changing State in Swift, where I hoped to convey an “aha!-moment” that happened for me.

Since then, I’ve learned a bit more. For example, the technique I presented for transforming instances of a Type immutably actually come for free when you use a value Type, such as a Struct! Check out @NatashaTheRobot’s writeup on the subject, titled “Mutating Functions in Swift Structs”, for more information.

But let’s say, for whatever reason, you’d like to use a reference Type, such as a Class. In that case, the technique I presented in the aforementioned blog entry works out quite nicely. Until….

Many init parameters == Pain

… It works great right up until you have more than a few immutable properties that you need to transform.

I want to thank @Jarsen for his comment. He pointed out the exact pain point I was feeling, since I was actually using my own advice in a personal project. Not only that, he offers a solution in the form of a GitHub gist!

I’m bringing in his example so that we have it before us with a few minor modifications to make it relevant for this blog entry. However, I want to give 100% credit to Jarsen for his insight.

It’s all about convenience

The gist of Jarsen’s solution was to create a second helper initializer which would help setting the values for all the properties easier. Take a look:

 1class Scorekeeper {
 2    let runningScore: Int
 3    let climbingScore: Int
 4    // potentially more properties
 5    
 6    init(runningScore: Int = 0, climbingScore: Int = 0) {
 7        self.runningScore = runningScore
 8        self.climbingScore = climbingScore
 9    }
10    
11    // second helper initializer
12    init(scoreKeeper: Scorekeeper, runningScore: Int? = nil, climbingScore: Int? = nil) {
13        self.runningScore = runningScore ?? scoreKeeper.runningScore
14        self.climbingScore = climbingScore ?? scoreKeeper.climbingScore
15    }
16    
17    func incrementRunningScoreBy(points: Int) -> Scorekeeper {
18        return Scorekeeper(scoreKeeper: self, runningScore: self.runningScore + points)
19    }
20    
21    // other functions to transform Scorekeeper by incrementing other score properties
22}

Note the use of optionals, and the corresponding nil-coalescing operator (??) in the helper initializer’s implementation. It’s simple, and it’s concise. I like it!

The bottom line is that I couldn’t help but share Jarsen’s tip. I thought it deserved a little more attention than to be stuck down in the comment section where folks may or may not find it and be helped.

comments powered by Disqus