Question about var and let

Hi,


Swift just threw me a warning which is going against what I thought I new.

If someone can offer some clarification that would be appreciated.


I have an object called deliveryCustomer which contains such fields as

.id

.firstname

.companyName

etc etc...


I am running a for loop and grabbing an array of customers as follows..


//**LOOP THROUGH THE JSON ARRAY**

for anItem in jsonResult as! [Dictionary<String, AnyObject>] {

var deliveryCustomer:deliveryCustomers = deliveryCustomers()


if let idString = (anItem["id"]) as? NSString {

deliveryCustomer.id = idString

}

if let firstNameString = (anItem["firstName"]) as? NSString {

deliveryCustomer.firstName = firstNameString

}

}


you get the idea...


In my line where I declare var deliveryCustomer, swift is suggesting that I replace this with a let.


My understanding was that once I declare it as a let, I can no longer modify the variable, but in my code, I am modifying it in quite a few locations in the for loop.


Can someone please advise if the warning is correct, and I am able to replace this var as a let??


Thanks,

Wade.

Accepted Reply

          for anItem in jsonResult as! [Dictionary<string, anyobject="">] {
                        var deliveryCustomer:deliveryCustomers = deliveryCustomers()

                        if let idString = (anItem["id"]) as? NSString {
                            deliveryCustomer.id = idString
                        }
                     
                        if let firstNameString = (anItem["firstName"]) as? NSString {
                            deliveryCustomer.firstName = firstNameString
                        }
}


Line 2, you create the object.


Line 5, you do not change the object reference, you just change one of its properties.

Hence, the object itself is unchanged.


So, you can replace var with let.


If that can help you, imagine you have a


class Person {
    private var name: String    // Make it private, so that it cannot be changed from outside
    private var firstName: String
    var age: Int
  
    init(name: String, firstName: String, age: Int) {
        self.name = name
        self.firstName = firstName
        self.age = age
    }
}


let someGenius = Person(name: "Einstein", firstName: "Albert", age: 80)
someGenius.age = 85
someGenius.name = "Other"  // Error 'name' is inaccessible due to 'private' protection level


someGenius points to a person that will not change. To make it sure, the name and firstName are declared as private.

But the age can change, of course. That does not change the person, it is still Albert Einstein !


That would be different, if you declared a struct and not a class:

You get an error with a let declaration


struct Personnage {
    var name: String    // Make it private, so that it cannot be changed from outside
    var firstName: String
    var age: Int
   
    init(name: String, firstName: String, age: Int) {
        self.name = name
        self.firstName = firstName
        self.age = age
    }
}

let someOne = Personnage(name: "Einstein", firstName: "Albert", age: 80)
someOne.age = 85    // Cannot assign to property: 'someOne' is a 'let' constant



The core reason is that

- class are reference objects; instance is a reference to the object ; changing the content does not change the instance value

- struct are value objects: instance is the content ; changing the content does change the instance value.


« Use struct to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference. »

Extrait de: Apple Inc. « The Swift Programming Language (Swift 4). » Apple Books.

Replies

          for anItem in jsonResult as! [Dictionary<string, anyobject="">] {
                        var deliveryCustomer:deliveryCustomers = deliveryCustomers()

                        if let idString = (anItem["id"]) as? NSString {
                            deliveryCustomer.id = idString
                        }
                     
                        if let firstNameString = (anItem["firstName"]) as? NSString {
                            deliveryCustomer.firstName = firstNameString
                        }
}


Line 2, you create the object.


Line 5, you do not change the object reference, you just change one of its properties.

Hence, the object itself is unchanged.


So, you can replace var with let.


If that can help you, imagine you have a


class Person {
    private var name: String    // Make it private, so that it cannot be changed from outside
    private var firstName: String
    var age: Int
  
    init(name: String, firstName: String, age: Int) {
        self.name = name
        self.firstName = firstName
        self.age = age
    }
}


let someGenius = Person(name: "Einstein", firstName: "Albert", age: 80)
someGenius.age = 85
someGenius.name = "Other"  // Error 'name' is inaccessible due to 'private' protection level


someGenius points to a person that will not change. To make it sure, the name and firstName are declared as private.

But the age can change, of course. That does not change the person, it is still Albert Einstein !


That would be different, if you declared a struct and not a class:

You get an error with a let declaration


struct Personnage {
    var name: String    // Make it private, so that it cannot be changed from outside
    var firstName: String
    var age: Int
   
    init(name: String, firstName: String, age: Int) {
        self.name = name
        self.firstName = firstName
        self.age = age
    }
}

let someOne = Personnage(name: "Einstein", firstName: "Albert", age: 80)
someOne.age = 85    // Cannot assign to property: 'someOne' is a 'let' constant



The core reason is that

- class are reference objects; instance is a reference to the object ; changing the content does not change the instance value

- struct are value objects: instance is the content ; changing the content does change the instance value.


« Use struct to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference. »

Extrait de: Apple Inc. « The Swift Programming Language (Swift 4). » Apple Books.

Thank you Claude31.


This is exactly what I was after.


I have been thinking about it wrong, but this clarifies it for me.


I was thinking that changing a property variable of the object was changing the class object as well.


Thanks

Claude31 has answered the question you asked (yay!) but I was curious if you’ve looked at using

Codable
for this. It’s the standard approach for dealing with JSON and it can radically simplify your code. For example, if you define a struct like this:
struct Person: Codable {
    var id: Int
    var firstName: String
    var lastName: String
}

you can parse JSON [1] in a single line:

let persons = try! JSONDecoder().decode([Person].self, from: jsonData)

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] This line assumes you have input like this:

let json = """
    [{
        "id": 1,
        "firstName": "Steve",
        "lastName": "Wozniak"
    }, {
        "id": 8,
        "firstName": "Chris",
        "lastName": "Espinosa"
    }]
    """
let jsonData = Data(json.utf8)