A UISwitch is placed within a UITableViewCell, when switch is toggled want to save the state of the switch to UserDefaults.
class SettingsTableViewCell: UITableViewCell {
@objc fileprivate func switchToggled(_ cellSwitch: UISwitch) {
if let option = self.option {
switch option.tag {
case 1:
let state = cellSwitch.isOn
SettingsValues.standard.setState(state)
default:
print(">>> Settings Cell >> Tag for option does not exist")
}
}
}
}
On viewDidLoad it works fine and it saves to UserDefaults, however the moment you scroll the cell off screen, its dequeued. When you scroll back and flip the switch it cause a crash:
EXC_BAD_ACCESS(code=1, address=0x20)
The SettingsValues is a custom singleton, the function saves to UserDefaults. I have tried moving the saving out of UITableViewCell through a protocol to the ViewController but it still causes a crash when saving to UserDefaults.
I feel it has something to do with the dequeue nature of the TableViewCell but I don't understand what is happening hence not sure how to tackle this problem.
EDIT: The singleton
final class SettingsValues: NSObject {
static let standard = SettingsValues()
private override init() { super.init() }
@objc dynamic var state: Bool = UserDefaults.standard.bool(forKey: kEnableState) {
didSet {
saveSettingsState()
}
}
private func saveSettingsState() {
UserDefaults.standard.set(state, forKey: kEnableState)
}
func setState(_ bool: Bool) {
self.state = bool
}
}
The crash is on line UserDefaults.standard.set(state, forKey: kEnableState)
.
I set a breakpoint on that line and when I press step over, the EXC_BAD_ACCESS
occurs
I found the bug. It was because of the observers. There was another cell in the tableView
that had an observer for the state. I did not remove the observer on deinit
hence multiple instances of the observer was added everytime the cell got dequeued and redrawn.
Lesson to all watch how many times your observers are being created for the same reference object. I won't go so far to always remove on deinit
sometimes deinit
won't ever be called. Example: a viewcontroller on the tabbarcontroller.
Safer bet would be to watch the number of created instances. In this case I should have removed the observer on deinit
Thanks to all who left a comment and helped jiggle things up