'Cannot use mutating getter on immutable value: 'self' is immutable error
I'm trying to reuse an older piece of Swift code, but getting an error 'Cannot use mutating getter on immutable value: 'self' is immutable error'. Xcode wanted to add 'mutating' before the func, and offered to do so through a 'fix'. So the error is gone there but still remains at the 'Text' statements.
import SwiftUI
struct ContentView: View {
typealias PointTuple = (day: Double, mW: Double)
let points: [PointTuple] = [(0.0, 31.98), (1.0, 31.89), (2.0, 31.77), (4.0, 31.58), (6.0, 31.46)]
lazy var meanDays = points.reduce(0) { $0 + $1.0 } / Double(points.count)
lazy var meanMW = points.reduce(0) { $0 + $1.1 } / Double(points.count)
lazy var a = points.reduce(0) { $0 + ($1.day - meanDays) * ($1.mW - meanMW) }
lazy var b = points.reduce(0) { $0 + pow($1.day - meanDays, 2) }
lazy var m = a / b
lazy var c = meanMW - m * meanDays
lazy var x : Double = bG(day: 3.0)
lazy var y : Double = bG(day: 5.0)
lazy var z : Double = bG(day: 7.0)
mutating func bG(day: Double) -> Double {
return m * day + c
}
var body: some View {
VStack {
Text("\(x)")
Text("\(y)")
Text("\(z)")
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
Solution 1:[1]
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
Solution 2:[2]
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
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 |