Cannot assign to property: 'self' is immutable, I know how to fix but needs understanding - swift

I have a struct :
public struct MyStruct {
public var myInt: Int = 0
...
}
I have a extension of MyStruct:
extension MyStruct {
public func updateValue(newValue: Int) {
// ERROR: Cannot assigned to property: 'self' is immutable
self.MyInt = newValue
}
}
I got the error showing above, I know I can fix the error by several ways, e.g. add a mutating keyword before func.
I am here not asking how to fix the error, but ask why swift doesn't allow this kind of value assignment ? I need an explanation besides a fix.

struct is a value type. For value types, only methods explicitly marked as mutating can modify the properties of self, so this is not possible within a computed property.
If you change struct to be a class then your code compiles without problems.
Structs are value types which means they are copied when they are passed around.So if you change a copy you are changing only that copy, not the original and not any other copies which might be around.If your struct is immutable then all automatic copies resulting from being passed by value will be the same.If you want to change it you have to consciously do it by creating a new instance of the struct with the modified data. (not a copy)

A tricky one: mark it as #State

Because a Struct is a value type, and therefore should be immutable

Related

Swift constants: Struct or Enum

I'm not sure which of both are better to define constants. A struct or a enum. A struct will be copied every time i use it or not? When i think about a struct with static let constants it makes no sense that it will copied all the time, in my opinion. But if it won't copied then it doesn't matter what I take?
What advantages does the choice of a struct or enum?
Francescu says use structs.
Ray Wenderlich says use enums. But I lack the justification.
Both structs and enumerations work. As an example, both
struct PhysicalConstants {
static let speedOfLight = 299_792_458
// ...
}
and
enum PhysicalConstants {
static let speedOfLight = 299_792_458
// ...
}
work and define a static property PhysicalConstants.speedOfLight.
Re: A struct will be copied every time i use it or not?
Both struct and enum are value types so that would apply to enumerations as well. But that is irrelevant here
because you don't have to create a value at all:
Static properties (also called type properties) are properties of the type itself, not of an instance of that type.
Re: What advantages has the choice of a struct or enum?
As mentioned in the linked-to article:
The advantage of using a case-less enumeration is that it can't accidentally be instantiated and works as a pure namespace.
So for a structure,
let foo = PhysicalConstants()
creates a (useless) value of type PhysicalConstants, but
for a case-less enumeration it fails to compile:
let foo = PhysicalConstants()
// error: 'PhysicalConstants' cannot be constructed because it has no accessible initializers
Here's a short answer: Do your constants need to be unique? Then use an enum, which enforces this.
Want to use several different constants to contain the same value (often useful for clarity)? Then use a struct, which allows this.
One difference between the two is that structs can be instantiated where as enums cannot. So in most scenarios where you just need to use constants, it's probably best to use enums to avoid confusion.
For example:
struct Constants {
static let someValue = "someValue"
}
let _ = Constants()
The above code would still be valid.
If we use an enum:
enum Constants {
static let someValue = "someValue"
}
let _ = Constants() // error
The above code will be invalid and therefor avoid confusion.
Using Xcode 7.3.1 and Swift 2.2
While I agree with Martin R, and the Ray Wenderlich style guide makes a good point that enums are better in almost all use cases due to it being a pure namespace, there is one place where using a struct trumps enums.
Switch statements
Let's start with the struct version:
struct StaticVars {
static let someString = "someString"
}
switch "someString" {
case StaticVars.someString: print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}
Using a struct, this will match and print out Matched StaticVars.someString.
Now lets consider the caseless enum version (by only changing the keyword struct to enum):
enum StaticVars {
static let someString = "someString"
}
switch "someString" {
case StaticVars.someString: print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}
You'll notice that you get a compile time error in the switch statement on the case StaticVars.someString: line. The error is Enum case 'someString' not found in type 'String'.
There's a pseudo-workaround by converting the static property to a closure that returns the type instead.
So you would change it like this:
enum StaticVars {
static let someString = { return "someString" }
}
switch "someString" {
case StaticVars.someString(): print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}
Note the need for parentheses in the case statement because it's now a function.
The downside is that now that we've made it a function, it gets executed every time it's invoked. So if it's just a simple primitive type like String or Int, this isn't so bad. It's essentially a computed property. If it's a constant that needs to be computed and you only want to compute it once, consider computing it into a different property and returning that already computed value in the closure.
You could also override the default initializer with a private one, and then you'll get the same kind of compile time error goodness as with the caseless enum.
struct StaticVars {
static let someString = "someString"
private init() {}
}
But with this, you'd want to put the declaration of the struct in its own file, because if you declared it in the same file as, say, a View Controller class, that class's file would still be able to accidentally instantiate a useless instance of StaticVars, but outside the class's file it would work as intended. But it's your call.
In the Combine framework, Apple has chosen to prefer enums for namespaces.
enum Publishers
A namespace for types that serve as publishers.
enum Subscribers
A namespace for types that serve as subscribers.
enum Subscriptions
A namespace for symbols related to subscriptions.

How should we initialize stored properties in Structs and Classes?

Apple's documentation states:
Classes and structures must set all of their stored properties to an
appropriate initial value by the time an instance of that class or
structure is created. Stored properties cannot be left in an
indeterminate state.
I have also seen this question, but still wasn't able to find an answer.
// struct
struct FlickrObj { // No Error ????
var title : String
var photographer : String
}
// class
class FlickrObj { // will create error: Class 'FlickrObj' has no initializers
var title : String
var photographer : String
}
Apple says both classes and structs must set their stored properties but why doesn't the Struct give any compile time errors?‌
It's because, in the absence of any explicitly declared initializers, the struct itself declares the implicit memberwise initializer init(title:photographer:).
But classes don't do that.
So the class is failing to fulfill the initialization contract (all instance properties must be initialized before the instance itself finishes initializing), but the struct is not failing the contract.

Using switch to assign an instance variable

I'd be grateful for any help - been racking my brains for days and I can't see why this isn't working.
Essentially, I have a main view controller which will be controlled by different classes depending on which game the user selects
'classic'
'unlimited'
'timed'
When the user button is pushed, it needs to flick through the options and assign an instance of the class to a variable 'brain'.
this is what I have:
var brain = GuessMeComparer()
func switcher (random:String) {
switch random {
case "Classic": self.brain = ClassicBrain()
case "unlimited": self.brain = GuessMeComparer()
case "timed": self.brain = TimedBrain()
default:break
}
}
I get the error 'cannot assign a value of type 'ClassicBrain' to a value of type 'GuessMeComparer'.
All I can think of is that you cannot assign instance variables using switch?
Any help would be great, cheers!
Using AnyObject will work but – as vadian is saying – will force you to cast to a specific type later. A better option will be abstract a common interface for all the brain classes in a swift protocol, e.g.:
protocol BrainProtocol {
// common interface here
...
}
class /* or struct */ ClassicBrain : BrainProtocol {
// classic implementation here
...
}
class /* or struct */ TimedBrain : BrainProtocol {
// timed implementation here
...
}
...
var brain : BrainProtocol
Swift is a strong type language, the variable brain is declared as type GuessMeComparer.
Once declared you cannot change the type.
To consider different types, declare the variable explicitly as generic type AnyObject.
var brain : AnyObject = GuessMeComparer()
Now you can assign different types to the variable, but in many cases you have to cast the variable to a specific type later in the code.

How can I make the memberwise initialiser public, by default, for structs in Swift?

I have a Swift framework that defines a struct:
public struct CollectionTO {
var index: Order
var title: String
var description: String
}
However, I can't seem to use the implicit memberwise initialiser from another project that imports the library. The error is:
'CollectionTO' cannot be initialised because it has no accessible initialisers
i.e. the default synthesized memberwise initialiser is not public.
var collection1 = CollectionTO(index: 1, title: "New Releases", description: "All the new releases")
I'm having to add my own init method like so:
public struct CollectionTO {
var index: Order
var title: String
var description: String
public init(index: Order, title: String, description: String) {
self.index = index;
self.title = title;
self.description = description;
}
}
... but is there a way to do this without explicitly defining a public init?
Quoting the manual:
"Default Memberwise Initializers for Structure Types
The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Otherwise, the initializer has an access level of internal.
As with the default initializer above, if you want a public structure type to be initializable with a memberwise initializer when used in another module, you must provide a public memberwise initializer yourself as part of the type’s definition."
Excerpt from "The Swift Programming Language", section "Access Control".
While it is not possible to have the default memberwise initializer at least you can make one quickly with the following steps:
UPDATE: Xcode 11 and later
As mentioned by Brock Batsell on the comments, for Xcode 11 and later all you need to is this:
Right click the class or struct name and choose refactor ->
Generate Memberwise Initializer
Xcode 10 and earlier answer
Make the object a class temporarily instead of a struct
Save
Right click the class name and choose refactor ->
Generate Memberwise Initializer
Change it back to a struct
We now have a ruby gem 💎 to parse a complete swift data model file, line-by-line, and add public access modifiers, public member-wise default initializers, and other things into a separate auto-generated output swift file.
This gem is called swift_republic
Please check out the following documentation for running this gem:
https://github.com/mehul90/swift_republic
Sometimes it's really annoying having an initializer when you don't need one. If you're constantly updating the variables to the object, it becomes bothersome very quickly to update the variables in 3 places (variable declaration, initializer parameter, and initializer implementation).
A workaround I've used for this issue is to have a static variable on the struct to act as (or essentially wrap) the "initializer". For instance:
struct MyStruct {
static var empty = Self()
static func empty(name: String) -> Self {
.init(privateName: name)
}
private var identifier: String = ""
}
Then you can call it similar to how you would an initializer (with autocomplete and everything!):
func someFunction(_ value: MyStruct) { ... }
//someFunction(.init()) -> ERROR, invalid due to `private` variable
someFunction(.empty)
someFunction(.empty(name: "Dan IRL"))
let myObject = MyStruct.empty
let myObject2 = MyStruct.empty(name: "Monty Python")
You have to define public init by yourself, luckily starting from Xcode 14 🥳 there is an automatic initializer completion (source - 60399329)

Can 'mutating' be inferred for functions inside structs?

Why should we be required to specify mutating for a function in a struct explicitly? Can't it be inferred automatically from the function body while compiling? If it can't, can you please give me an example where it can't...
There are some special examples when the compiler cannot infer mutability because it's the programmer decision to say whether the behavior should be considered as mutable.
For example, when the struct is backed up by a class:
struct MyArray<T : AnyObject> {
let buffer: NSMutableArray = NSMutableArray()
func append(item: T) {
buffer.addObject(item)
}
}
let myArray = MyArray<NSString>()
myArray.append("something")
Note that the append function is modifying the struct but it doesn't have the mutating attribute, so you can use it even on constant variables. If you want to prevent that, you need to add the mutating attribute.
I didn't choose the array example accidentally. The dictionaries and arrays in Swift are backed up by classes and they had to choose which methods would be mutating and which wouldn't.