Cannot use mutating getter on immutable value: 'self' is immutable error
Because when you call x
inside the struct, it's not clear whether the contentView
itself is mutable or not. A value type gets mutable only when it is defined as a var
.
So it would help if you wrapped it with an immutable value before using it inside a builder function inside the struct.
like this:
func xValue() -> Double {
var mutatableSelf = self
return mutatableSelf.x
}
var body: some View {
VStack {
Text("\(xValue())")
}
}
ð¡Note: Lazy
property's value will be associated on the first call and this mutates the object. So they are considered as mutating
A getter cannot mutate
This is mainly a design Swift is enforcing with its getters. The principle is:
The getters should not mutate the object. Because developers may not be expecting that. They should only expect a change when you're using the setter or calling a mutating function. A getter is neither of them.
The following example works as expected:
struct Device {
var isOn = true
}
let x = Device()
let y = Device()
y.isOn // Doing such will not cause the object to mutate.
Yet the following example, the getter will have a side-effect. The Swift architecture just doesn't allow it.
struct Device2 {
var x = 3
var isOn: Bool {
x = 5
return true
}
}
let a = Device2()
let b = Device2()
a.isOn // Doing such will mutate the object. a.x will be '5'. While `b.x` will be '3'. Swift doesn't want to allow this.
Lazy is mutating:
struct ContentView: View {
lazy var x : Double = 3.0
var body: some View {
Text("\(x)") // ERROR
}
}
Will result in the follow ERROR:
Cannot use mutating getter on immutable value: 'self' is immutable
SwiftUI - special case
Because the body
variable is a computed property, you can't mutate/set variables. There's a way around that though.
Mark the variable with a @State
property wrapper.
Example. The following code won't compile:
struct ContentView: View {
var currentDate = Date()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text("\(currentDate)")
.onReceive(timer) { input in
currentDate = input // ERROR: Cannot assign to property: 'self' is immutable
}
}
}
Yet the following will, just because it has @State
struct ContentView: View {
@State var currentDate = Date()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text("\(currentDate)")
.onReceive(timer) { input in
currentDate = input
}
}
}
For more on that see here