I am the author of iOS 17 Fundamentals, Building iOS User Interfaces with SwiftUI, and eight other courses on Pluralsight.
Deepen your understanding by watching!
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.