Reputation: 805
I have a struct with var grid
struct Machina {
....
var grid: [Whatever] = []
var other: Something
... // other stuff
}
But grid
needs to be counted every time other
changes. So, I can call
var other: Something {
didSet {
defineGrid()
}
...
mutating func defineGrid() {
var result: [Whatever]
... // count result
grid = result
}
but I would love to simply call grid
as var and keep counted grid in _grid
var _grid: [Whatever]? = nil
var other: Something {
didSet {
_grid = nil
}
...
mutating func defineGrid() {
var result: [Whatever]
... // count result
_grid = result
}
...
var grid: [Whatever] {
if _grid == nil {
defineGrid() // error: Cannot use mutating member on immutable value: 'self' is immutable
}
return _grid!
}
but in this case I got an error. Is a first approach nice? Is it possible to get var grid
from second approach? Maybe it's just a cosmetics. I don't know.
Upvotes: 1
Views: 89
Reputation: 6600
If Swift 5
is used you can also use this handy @propertyWrapper
:
@propertyWrapper
struct LazyWithReset<T> {
private var value: T? = nil // this is hidden storage for the value, like _grid in the question
var wrappedValue: T { // this is lazy-ish interface to access/generate value, like grid in the question
mutating get {
if (value == nil) {
value = generator()
}
return value!
}
}
var generator: () -> T // this is the function that sets value, like defineGrid in the question
init(_ generator: @escaping () -> T) {
self.generator = generator
}
mutating func reset() { // this is just resetting the storage while keeping internals hidden
value = nil
}
}
Usage:
struct Machina {
@LazyWithReset(defineGrid)
private(set) var grid: [Whatever] // no initial value required, generator will lazily provide value if needed
var other: Something {
didSet {
_grid.reset() // variable set, we reset our lazy variable
}
}
private static func defineGrid() -> [Whatever] { // now it's pure static function, no mutability required
return … // whatever you was directly setting to the grid var previously
}
}
This way you can hide all boilerplate code under the rug and keep your business logic clean.
Also LazyWithReset<T>
can be reused wherever you want, so you won't have to copy-paste any code.
Upvotes: 1
Reputation: 271905
As Alexander said, you need a mutating
getter for grid
:
var grid: [Whatever] {
mutating get {
if _grid == nil {
defineGrid()
}
return _grid!
}
}
Also, _grid
should probably be private
.
Upvotes: 1