How to mute the warning of "never mutated" in Swift? [duplicate] - swift

This question already has answers here:
Why constant constraints the property from a structure instance but not the class instance?
(2 answers)
Swift constant UIView never mutated warning
(1 answer)
UITableViewCell var "table view cell" was never mutated
(1 answer)
Closed 5 years ago.
Regarding duplicate flag: This question is different from the flagged question as I am asking about how to mute the warnings as I was not aware of the concept of Swift. The provided below answer helps me understand the very basic nature of Swift. Thus this question should not flagged as duplicate.
I have a class name Person having following variables.
private var _id:String = ""
var id:String {
get {
return _id
}
set (newId) {
_id = newId
}
}
private var _name:String = ""
var name:String {
get {
return _name
}
set (newName) {
_name = newName
}
}
private var _signedDate:Date? = nil
var signedDate:Date {
get {
return _signedDate!
}
set(newDate) {
_signedDate = newDate
}
}
These private var's are going to update with a setter.
So while creating an object for the Person class, I am writing this code.
var p1 = Person()
p1.id = "1"
p1.name = "Hemang"
array.append(p1)
Maybe later, I will update the value of signedDate with a setter.
So I should not create this object with let.
However, it's showing me this warning:
Variable 'p1' was never mutated; consider changing to 'let' constant.
How to mute this warning?
Please let me know if you need more information on this.

Because actually you don't change the Person object,
With let you can change the properties of the object. But you can't change the object it self.
So change your code to what the warning lead you.
And of course you can try before asking this question.

Related

How can I access private property outside that class in Swift? [duplicate]

This question already has answers here:
Read-Only properties
(3 answers)
Closed 9 months ago.
Here I have a class and make its properties private to prevent from modifications by accident.
class Article {
private var lineIndex: [Int] = []
private var text: [String] = []
....
}
I know I can write a function like func text(_ index: Int) -> String to get its value at index. But when I call it, article1.text(2) would be weird. Because it's less clear to indicate 2 is an index than what an array does like article1.text[2]. So can I use getter or something else instead, while keeping the clear syntax like text[2]. It couldn't be better if you can offer some examples.
You can use one of these ways:
with private(set), which allows editable inside the class:
class Article {
private(set) var text: [String] = []
...
}
with get only computed property (this is the same like your get function)
class Article {
var _text: [String] {
return text
}
}

Get a Swift class's property name as a String [duplicate]

This question already has answers here:
Get a Swift Variable's Actual Name as String
(7 answers)
Closed 5 years ago.
How do I get the property name as a string?
class Person {
var firstName: String
var lastName: String
var downloading: Bool
func excludePropertiesFromCloud() -> [String] {
return //here I want to return ["downloading"]
}
}
I would like to avoid having to type return ["downloading"] to prevent errors if the name of the variable changes later on.
To get the class name you can use String(describing: Person.self), so I am looking at something similar for properties.
Note:
Although the title of the question is very similar to Get a Swift Variable's Actual Name as String, it is clear that the accepted answer in the original question returns the value of the property and does not answer this straightforward question. The original question is the first to come up on any google search with "Swift get property name", and the answer answers something else, as pointed to by #MarqueIV. This is why I created this question
If you are ok with making your properties #objc you can get the property name like so:
class Person {
#objc var firstName: String
var lastName: String
var downloading: Bool
func excludePropertiesFromCloud() -> [String] {
return [#keyPath(firstName)]
}
}

Expected declaration when adding values to a Swift dictionary [duplicate]

This question already has answers here:
swift compiler shows Expected declaration error? [duplicate]
(3 answers)
Expected Declaration Error using Swift
(1 answer)
Closed 5 years ago.
I get the error "expected declaration" on the last line when trying to add values to the dictionary tablesBooked.
class BookingSystem {
var tablesBooked = Dictionary<Int, String>()
var table = Table(tableID: 1 , tableCapacity: 2, status: "A")
var bookings = [Booking]()
tablesBooked.setValue(table.status, forKey: table.tableID)
}
You get this error because your line setValue cannot just live here inside your class without being inside a method. Of course here it really depends on what (and how) you want to accomplish, but you could put it in the init() method of your BookingSystem class, or you could build your own custom init().
Here is how it would look like:
import Foundation
class Booking {
// Some interesting things here
}
class Table : NSObject {
// MARK: Properties
var tableID: Int
var tableCapacity: Int
var status: String
// MARK: Initializers
init(tableID: Int, tableCapacity: Int, status: String) {
self.tableID = tableID
self.tableCapacity = tableCapacity
self.status = status
}
}
class BookingSystem {
// MARK: Properties
var tablesBooked = [Int: String]()
var table = Table(tableID: 1 , tableCapacity: 2, status: "A")
var bookings = [Booking]()
// MARK: Initializers
init() {
// I am not sure what you are trying to do here, but anyway you should add it in a custom method or your init. If I were to use the code in your example, you would add this here:
tablesBooked[table.tableID] = table.status
}
// ...
}
I added the Table class here on purpose, just to show you an example on how to create your own custom init.
Also, another thing worth mentioning here is that Swift Dictionaries don't have a setValue:forKey: method. Instead, to add an object to your Dictionary, you should use:
yourDictionnary["yourKey"] = yourValue
Hope it helps, and if you have any questions just feel free asking :)
Use init method:
class BookingSystem {
var tablesBooked = Dictionary<Int, String>()
var table = Table(tableID: 1 , tableCapacity: 2, status: "A")
var bookings = [Booking]()
init() {
tablesBooked.setValue(table.status, forKey: table.tableID)
}
}

Getters and Setters in Swift - Does it make sense to use WillSet and DidSet instead?

I was doing some research about the reasons we should use Get and Set for our properties.
I've noticed 3 main reasons for it
When you want to do/check something before you actually set the
property
When you want to have a property that you can only Get from it
(maybe for security purposes I guess? ), or give it different access
levels.
Hiding the internal representation of the property while exposing a
property using an alternative representation. (which for me doesn't
make a lot of sense since i can access it on the wrong place using
the Set function anyways)
The code below is a example of how you would implement Get and Set for properties in Swift, taking advantage of those 3 points I mentioned:
class Test
{
private var _testSet:String!
private var _testGetOnly:String
var testSet:String{
get{
return _testSet
}
set{
_testSet = newValue + "you forgot this string"
}
}
var testGetOnly:String!{
get{
return _testGetOnly
}
}
init(testSet:String, testGetOnly:String)
{
_testSet = testSet
_testGetOnly = testGetOnly
}
}
But this other example below also take advantage of those points mentioned but instead of using another computed property to return the private property value I just use the willSet and didSet observers
class Test
{
var testGet:String {
willSet{
fatalError("Operation not allowed")
}
}
var testWillSet:String!{
didSet{
self.testWillSet = self.testWillSet + "you forgot this string"
}
}
init(testGet:String, testWillSet:String)
{
self.testGet = testGet
self.testWillSet = testWillSet
}
}
So I'm curious to know what are the ADVANTAGES and DISADVANTAGES of each implementation.
Thanks in advance
Your question boils down to compile time vs. run time error. To address your 3 questions:
Yes, willCheck is your only option here
Readonly properties fall into 2 types: (a) those whose value derive from other properties, for example, their sum; and (b) those that you want to be able to change by yourself, but not by the users. The first type truly have no setter; the second type has a public getter and a private setter. The compiler can help you check for that and the program will not compile. If you throw a fatalError in didSet you get a runtime error and your application will crash.
There can be state objects that you don't want the user to freely mess with, and yes, you can completely hide those from the users.
Your code first example was too verbose in defining the backing variables - you don't need to do that. To illustrate these points:
class Test
{
// 1. Validate the new value
var mustBeginWithA: String = "A word" {
willSet {
if !newValue.hasPrefix("A") {
fatalError("This property must begin with the letter A")
}
}
}
// 2. A readonly property
var x: Int = 1
var y: Int = 2
var total: Int {
get { return x + y }
}
private(set) var greeting: String = "Hello world"
func changeGreeting() {
self.greeting = "Goodbye world" // Even for private property, you may still
// want to set it, just not allowing the user
// to do so
}
// 3. Hide implementation detail
private var person = ["firstName": "", "lastName": ""]
var firstName: String {
get { return person["firstName"]! }
set { person["firstName"] = newValue }
}
var lastName: String {
get { return person["lastName"]! }
set { person["lastName"] = newValue }
}
var fullName: String {
get { return self.firstName + " " + self.lastName }
set {
let components = newValue.componentsSeparatedByString(" ")
self.firstName = components[0]
self.lastName = components[1]
}
}
}
Usage:
let t = Test()
t.mustBeginWithA = "Bee" // runtime error
t.total = 30 // Won't compile
t.greeting = "Goodbye world" // Won't compile. The compiler does the check for you
// instead of a crash at run time
t.changeGreeting() // OK, greeting now changed to "Goodbye world"
t.firstName = "John" // Users have no idea that they are actually changing
t.lastName = "Smith" // a key in the dictionary and there's no way for them
// to access that dictionary
t.fullName = "Bart Simpsons" // You do not want the user to change the full name
// without making a corresponding change in the
// firstName and lastName. With a custome setter, you
// can update both firstName and lastName to maintain
// consistency
A note about private in Swift 2 vs. Swift 3: if you try this in a Swift 2 playground, you will find t.greeting = "Goodbye world" works just fine. This is because Swift 2 has a strange access level specifier: private means "only accessible within the current file". Separate the class definition and the sample code into different files and Xcode will complain. In Swift 3, that was changed to fileprivate which is both clearer and save the private keyword for something more similar to to Java and .NET

Swift protocol settable property through a read-only property [duplicate]

This question already has an answer here:
Swift: Failed to assign value to a property of protocol?
(1 answer)
Closed 6 years ago.
Can someone please tell me why Swift has to call the setter of a property when it's only being used to access an object (a protocol) in order to set one of its properties? This first example shows the error I get if I don't declare the indirect object as settable:
protocol AProtocol {
var name: String { get set }
}
class AnImplementation: AProtocol {
var name = ""
}
class AParent {
var test = AnImplementation()
}
class AChild {
var parent: AParent!
var test: AProtocol {
get { return parent.test }
// Note: Not settable
}
}
var parent = AParent()
var child = AChild()
child.parent = parent
child.test.name = "Hello world!" // Error: Cannot assign to property : 'test' is a get-only property
print(child.test.name)
If I give it a setter, it compiles and works but it calls the setter:
protocol AProtocol {
var name: String { get set }
}
class AnImplementation: AProtocol {
var name = ""
}
class AParent {
var test = AnImplementation()
}
class AChild {
var parent: AParent!
var test: AProtocol {
get { return parent.test }
set(newTest) { print("Shouldn't be here!") }
}
}
var parent = AParent()
var child = AChild()
child.parent = parent
child.test.name = "Hello world!"
print(child.test.name)
Output is:
Shouldn't be here!
Hello world!
I'm not sure what I'm not understanding here. I assume I can just give it an empty setter, but I'd like to understand the reason for it.
Any information is much appreciated!
Change your protocol declaration to this:
protocol AProtocol:class {
var name: String { get set }
}
Otherwise, it is taken by default as a value type. Changing a value type's property replaces the value type instance (as shown by the setter observer). And you can't do that if the reference is a let reference.
This is probably caused by the fact that the compiler doesn't know whether AChild.test is a class or a value type. With classes there is no problem but with value types assigning to name would also create an assignment to test (value-copy behavior). Marking APProtocol as class protocol will fix the problem.
To expand, when the compiler is not sure whether test is a value or a class type, it will use the following rewrite of child.test.name = "Hello world!":
var tmp = child.test
tmp.test = "Hello world!"
child.test = tmp
because that will work for both class and value types.