SpaceX
SpaceX

Reputation: 2890

Cannot assign to immutable expression of type Swift

I've seen several similar SO questions without much help,

I am trying to update availabilityTimes for Monday with timesArray using the function updateDailyTimes

Example of availabilityTimes and timesArray

 var availabilityTimes = [
         "Monday": [ "available" : true,
                     "times": [
                         ["startTime": "9:00 am", "endTime": "1:30 pm" ],
                         ["startTime": "2:30 pm", "endTime": "6:00 pm" ],
                         ["startTime": "7:30 pm", "endTime": "9:00 pm" ]
                     ]
         ],
         "Tuesday": [ "available" : true,
                     "times": [
                         ["startTime": "9:00 am", "endTime": "6:00 pm" ]
                     ]
                    ]
          ]




var timesArray = [
                    ["startTime": "9:00 am", "endTime": "1:30 pm" ],
                    ["startTime": "2:30 pm", "endTime": "6:00 pm" ],
                    ["startTime": "7:30 pm", "endTime": "9:00 pm" ]
                  ]

Here is my code, when I call the below the function, I get an error message cannot assign to immutable expression of type [[String : String]] in the code line value["times"] as [[String : String]] = timesArray

func updateDailyTimes(day: String, timesArray: [[String:String]]){

    guard var updatedTimes = self.availabilityTimes as? [String: AnyObject] else{
        return
    }

    for (key, var value) in updatedTimes {

        if key == day{
            for (key1, value1) in value as! [String : AnyObject]{
                if key1 as String == "times" {
                    value["times"] as [[String : String]] = timesArray // I get an error message in this line
                }
            }

        }
    }
}

Upvotes: 1

Views: 3189

Answers (2)

vadian
vadian

Reputation: 285069

According to my comment I mean a custom struct Times for the start and end times and a struct DailyTimes with the required properties and a mutating function to update the Times array:

struct Times {
    let start : String
    let end : String
}

struct DailyTimes {
    let weekday : String
    var available : Bool
    var times = [Times]()

    mutating func update(times : [Times]) {
        self.times = times
    }
}

Now create the availableTimes dictionary:

var availableTimes = ["Monday" : DailyTimes(weekday: "Monday", available: true, times: [Times(start: "9:00 am", end:"1:30 pm"),
                                                                                        Times(start: "2:30 pm", end:"6:00 pm"),
                                                                                        Times(start: "7:30 pm", end:"9:00 pm")]),
                      "Tuesday" : DailyTimes(weekday: "Tuesday", available: true, times: [Times(start: "9:00 am", end:"6:00 pm")])]

and the timesArray array:

let timesArray = [Times(start: "9:00 am", end:"1:30 pm"),
                  Times(start: "2:30 pm", end:"6:00 pm"),
                  Times(start: "7:30 pm", end:"9:00 pm")]

Since the Times array of Monday contains already the values of timesArray let's update Tuesday

availableTimes["Tuesday"]?.update(times:timesArray)

Finally prove it

print(availableTimes["Tuesday"]?.times)

Another improvement could be to use DateComponents instead of Strings for the times.

Upvotes: 2

allenh
allenh

Reputation: 6622

In Swift, a Dictionary is a struct, which is a value type. Think of value types just like a primitive type such as int.

If you have this:

int a = 0
int b = a

Then b gets the value of a, they are not identical.

So when you are looping over the dictionary, your (key, value) pair are NOT actually what is in the dictionary, they just have the same value. It's also important not to mutate a value or reference while iterating over it. That can lead to weird bugs.

enum AvailabilityTimeMarker {
    case start
    case end
}
enum DayOfTheWeek: String {
    case monday = "Monday"
    case tuesday = "Tuesday"
    case wednesday = "Wednesday"
    case thursday = "Thursday"
    case friday = "Friday"
    case saturday = "Saturday"
    case sunday = "Sunday"
}

typealias Availability = [DayOfTheWeek : AvailabilityDay]
typealias AvailabilityDay = [String : Any]
typealias AvailabilityArray = [AvailabilityTime]
typealias AvailabilityTime = [AvailabilityTimeMarker : String]

var availabilityTimes: Availability = [
    .monday : [ "available" : true,
                "times": [
                    ["startTime": "9:00 am", "endTime": "1:30 pm" ],
                    ["startTime": "2:30 pm", "endTime": "6:00 pm" ],
                    ["startTime": "7:30 pm", "endTime": "9:00 pm" ]
        ]
    ],
    .tuesday : [ "available" : true,
                 "times": [
                    ["startTime": "9:00 am", "endTime": "6:00 pm" ]
        ]
    ]
]

var timesArray: AvailabilityArray = [
    [.start : "9:00 am", .end : "1:30 pm" ],
    [.start : "2:30 pm", .end : "6:00 pm" ],
    [.start : "7:30 pm", .end : "9:00 pm" ]
]

func updateDailyTimes(day: DayOfTheWeek, timesArray: AvailabilityArray) {

    let availabilities = self.availabilityTimes

    var newtimes = availabilities

    if var newDayAvailability = availabilities[day] {
        newDayAvailability["times"] = newtimes

        newtimes[day] = newDayAvailability

        self.availabilityTimes = newtimes// Since we're not using reference semantics, we have to change the entire value
    }
}

Upvotes: 1

Related Questions