First, I have a protocol that defines only a few, readonly properties, ex:
protocol Example {
var var1:String { get }
var varArray:[String] { get }
}
I then want to create a struct that conforms to that protocol. The problem I'm running into, is that I have two conflicting requirements:
I can't seem to find a way to do this. The closest I've come is something like this:
struct AStruct : Example {
private lazy var data:(var1:String, varArray:[String]) = {
var stringValue:String = ""
var stringArray:[String] = []
//Generate data
return (stringValue, stringArray)
}()
var var1:String {
return self.data.var1
}
var varArray:[String] {
return self.data.varArray
}
}
The problem is, I'm getting the error: Immutable value of type 'AStruct' only has mutating members named 'data'
.
Does anyone know of a way I might be able to accomplish my goal? Technically, the data
variable is mutable but will never change. I can't use let
with lazy
so I can't specify that the value will never change once it's been generated. I need the values to be generated though, because the struct is created on the main thread, but the values will be generated on a background thread by another process altogether.
So it was pointed out to me that I can make the getters mutating
in both the protocol and the struct. That works, except I now have the problem that I cannot use this struct in any other struct (which I am). So in the end, I've pushed off the problem into another struct, which I do not want to be mutable.
Ex:
struct Container {
let astruct:AStruct
let callback:() -> ()
}
I cannot access the variables in AStruct
from Container
because Container
is immutable and the member variables of AStruct
are mutating. Trying to access them gives me the same error message I spoke of earlier.
Changing the container to use var
instead of let
yields the same error:
struct Container {
var astruct:AStruct
let callback:() -> ()
}
If I set up a function in the processing class that receives a Container
to process:
func processContainer(cont:Container){
self.doSomething(cont.astruct.var1)
}
I get the same error: Immutable value of type 'AStruct' only has mutating members names 'sql'
.
Lazy Properties in Swift Swift language allows you to create several different types of properties, including computed, property observers and even lazy properties. In this article, we will learn how lazy properties can provide performance benefits for time consuming calculations. Implementation
In Swift, a struct is used to store variables of different data types. For example, Suppose we want to store the name and age of a person. We can create two variables: name and age and store value. However, suppose we want to store the same information of multiple people.
A Swift property does not have a corresponding instance variable, and the backing store for a property is not accessed directly. This approach avoids confusion about how the value is accessed in different contexts and simplifies the property’s declaration into a single, definitive statement.
In C and Objective-C, you define static constants and variables associated with a type as global static variables. In Swift, however, type properties are written as part of the type’s definition, within the type’s outer curly braces, and each type property is explicitly scoped to the type it supports.
Because accessing the lazy data
variable mutates AStruct
, any access to it must be marked as also mutating the struct. So you need to write:
struct AStruct : Example {
// ...
var var1: String {
// must declare get explicitly, and make it mutating:
mutating get {
// act of looking at data mutates AStruct (by possibly initializing data)
return self.data.var1
}
}
var varArray:[String] {
mutating get {
return self.data.varArray
}
}
}
However, you'll find now that Swift complains you aren't conforming to Example
, because its get var1
isn't marked as mutating. So you'd have to change it to match:
protocol Example {
var var1:String { mutating get }
var varArray:[String] { mutating get }
}
So I want to detail out the solution I ended up following. As it turns out, I don't think what I want is currently possible in Swift. Once you start down the mutating get
road, it ends up cascading into too many areas (all container structs need to be mutating, etc). In the end, this kind of ruined the whole reason I wanted to use structs in the first place.
Perhaps down the road Apple will add a lazy let var = ...
. This would ensure immutability by guaranteeing the lazy variable is only ever set once... just not immediately.
So my solution was simply to forego structs
altogether and use classes
instead. I keep the classes functionally immutable, so I still retain that. Quite literally, all I had to do was change struct
to class
and now the lazy construction works perfectly and all my problems go away... except I'm using a class.
All that said, @AirspeedVelocity has a correct solution, even if untenable for my needs, so I'll be accepting his solution. I'm just leaving this here so others can understand how I overcame the problem... use classes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With