swiftdidselectrowatindexpath

Cannot assign to property: '$0' is immutable


I have my default selected cell in my table view. I want to change this selected language when tapped but could not able to do so. I am getting an error at '$0' line:

var languageViewModel = LanguageViewModel()
var languageTitles: [LanguageModel] = []
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    var item = languageTitles[indexPath.row]
    var value = item.isSelected
    // Edit isSelected property and refresh table
    languageTitles.forEach { $0.isSelected = false  }
    item.isSelected = !value
    tableView.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    return languageCell(indexPath: indexPath)
}
private func languageCell(indexPath: IndexPath) -> LanguageTableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: LanguageTableViewCell.identifier, for: indexPath) as! LanguageTableViewCell
    let item = languageTitles[indexPath.row]
    if item.isSelected {
        cell.setupSelected(item)
    } else {
        cell.setup(item)
    }
    return cell
}

struct LanguageModel {
    let language: Language
    var isSelected: Bool = false
}

class LanguageViewModel {
    var locales: [Language] = [.tr, .eng, .az] //.ar
    var currentLanguage = UserDefaultsUtil.getDeviceLanguage()
    var languages: [LanguageModel] = []
    func getLanguageTitles() {
        languages = [
            LanguageModel(language: .tr, isSelected: currentLanguage == locales[0].code),
            LanguageModel(language: .eng, isSelected: currentLanguage == locales[1].code),
            LanguageModel(language: .az,  isSelected: currentLanguage == locales[2].code)
        ]
    }
}

I can not tap on another cell when I want to change the language. It is always the selected one from the view model

if I change didselectrowat to

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    var item = languageTitles[indexPath.row]
    var value = item.isSelected
    // Edit isSelected property and refresh table
    for languageTitle in languageTitles {
        languageTitle.isSelected = false
    }
    item.isSelected = !value
    tableView.reloadData()
}

Cannot assign to property: 'languageTitle' is a 'let' constant

enter image description here


Solution

  • Replace this:

            languageTitles.forEach { $0.isSelected = false  }
    

    With:

    for index in languageTitles.indices {
        languageTitles[index].isSelected = false
    }
    

    forEach has various subtle and surprising behaviors. It is generally best to use a for-in instead. But the key issue is that LanguageModel is a struct and has value semantics. When you assign it to a variable (i.e. $0, or the variable in a for-in loop), a copy is made. If you make changes to the copy, they don't modify the array. (By default they're constants, so you can't modify them anyway.) Assigning through the subscript operator, however, does what you expect.