-1

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

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Mert Köksal
  • 817
  • 1
  • 7
  • 26
  • related [How to update a specific property value of all elements in array using Swift](https://stackoverflow.com/questions/59925964/how-to-update-a-specific-property-value-of-all-elements-in-array-using-swift/59926097#59926097) – Leo Dabus Mar 29 '22 at 14:00

1 Answers1

10

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.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Cannot assign to property: 'languageTitle' is a 'let' constant – Mert Köksal Mar 29 '22 at 12:32
  • I guess it kinda worked but it only deletes the first colored button. Does not select another cell. Just all unselected cells. – Mert Köksal Mar 29 '22 at 12:43
  • You're doing the same thing with `item`. It's a *copy* of the element in the array, not a reference into the array. You modify `item`, and then throw it away. That's not actually doing anything. Similarly, when you pass it to `setupSelected`, that's passing a copy, so if it makes any changes, those will be ignored as well. https://developer.apple.com/swift/blog/?id=10 – Rob Napier Mar 29 '22 at 12:44