My Toggle doesn't work the way I want it to

I have an Alarm struct, there is an Bool based on this I want to switch the toggle.

struct Alarm: Hashable  {
    var id: Int
    var alarmTime: String
    var selected: Bool
    @Binding var isAlarmOn: Bool
    
    init(id: Int, alarmTime: String, selected: Bool, isAlarmOn: Binding<Bool>) {
        self.id = id
        self.alarmTime = alarmTime
        self.selected = selected
        self._isAlarmOn = isAlarmOn
    }
    
    public var alarmId: Int {
        get {
            return self.id
        }
        set {
            self.id = newValue
        }
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
    
    static func == (lhs: Alarm, rhs: Alarm) -> Bool {
        return lhs.alarmId == rhs.alarmId
    }
}

I have an AlarmModel, there is the Alarm's array.

class AlarmModel: ObservableObject {
    
    @Published var alarms: [Alarm] = []
    
    func addAlarm(_ alarmTime: String, _ alarmId: Int) {
        let newAlarm = Alarm(id: alarmId, alarmTime: alarmTime, selected: false, isAlarmOn: .constant(false))
        alarms.append(newAlarm)
    }
    
    //other functions...
}

And I have a ListView, this has the toggle that doesn't want to work the way I want it to.

struct AlarmListView: View {
    @ObservedObject var alarmModel : AlarmModel
    
    var body: some View {
        List {
            ForEach(alarmModel.alarms, id: \.self) { alarm in
                HStack {
                    Toggle("", isOn: alarm.$isAlarmOn)
                }
            }
        }
    }
}

So if I use this sintax the code build, but Toggle doesn't do anything:

Toggle("", isOn: alarm.$isAlarmOn)

Else if I use this sintax: $alarm.isAlarmOn -> I get two errors:

  1. Cannot find '$alarm' in scope
  2. Failed to produce diagnostic for expression; please submit a bug report (https://swift.org/contributing/#reporting...

Thank you in advance for your help!

Answered by Claude31 in 749424022

Change

    @ObservedObject var alarmModel : AlarmModel

to a StateObject

    @StateObject var alarmModel = AlarmModel()

And change

    @Binding var isAlarmOn: Bool

To a published (in a class

    @Published var isAlarmOn: Bool = false 

So complete code is now working (as far I could test):

class Alarm: Hashable  {
    var id: Int
    var alarmTime: String
    var selected: Bool
    @Published var isAlarmOn: Bool = false // @Binding var isAlarmOn: Bool
    
    init(id: Int, alarmTime: String, selected: Bool, isAlarmOn: Bool) { // isAlarmOn: Binding<Bool>) {
        self.id = id
        self.alarmTime = alarmTime
        self.selected = selected
        self.isAlarmOn = isAlarmOn
//        self._isAlarmOn = isAlarmOn
    }
    
    public var alarmId: Int {
        get {
            return self.id
        }
        set {
            self.id = newValue
        }
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
    
    static func == (lhs: Alarm, rhs: Alarm) -> Bool {
        return lhs.alarmId == rhs.alarmId
    }
}

class AlarmModel: ObservableObject {
    
    @Published var alarms: [Alarm] = []
    
    func addAlarm(_ alarmTime: String, _ alarmId: Int) {
        let newAlarm = Alarm(id: alarmId, alarmTime: alarmTime, selected: false, isAlarmOn: false) // , isAlarmOn: .constant(false))
        alarms.append(newAlarm)
    }
    
    //other functions...
}


struct AlarmListView: View {   
//    @ObservedObject var alarmModel : AlarmModel
    @StateObject var alarmModel = AlarmModel()
    
    var body: some View {
        List {
            ForEach($alarmModel.alarms, id: \.self) { $alarm in
                HStack {
                    Text(" \(alarm.alarmTime)")
                    Toggle("", isOn: $alarm.isAlarmOn)
                }
            }
        }
        .onAppear {
            alarmModel.addAlarm("Alarm1", 1)
            alarmModel.addAlarm("Alarm2", 2)
        }
    }
}

Note you can simplify a little, suppressing HStack:

            ForEach($alarmModel.alarms, id: \.self) { $alarm in
                    Toggle("\(alarm.alarmTime)", isOn: $alarm.isAlarmOn)
            }
Accepted Answer

Change

    @ObservedObject var alarmModel : AlarmModel

to a StateObject

    @StateObject var alarmModel = AlarmModel()

And change

    @Binding var isAlarmOn: Bool

To a published (in a class

    @Published var isAlarmOn: Bool = false 

So complete code is now working (as far I could test):

class Alarm: Hashable  {
    var id: Int
    var alarmTime: String
    var selected: Bool
    @Published var isAlarmOn: Bool = false // @Binding var isAlarmOn: Bool
    
    init(id: Int, alarmTime: String, selected: Bool, isAlarmOn: Bool) { // isAlarmOn: Binding<Bool>) {
        self.id = id
        self.alarmTime = alarmTime
        self.selected = selected
        self.isAlarmOn = isAlarmOn
//        self._isAlarmOn = isAlarmOn
    }
    
    public var alarmId: Int {
        get {
            return self.id
        }
        set {
            self.id = newValue
        }
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
    
    static func == (lhs: Alarm, rhs: Alarm) -> Bool {
        return lhs.alarmId == rhs.alarmId
    }
}

class AlarmModel: ObservableObject {
    
    @Published var alarms: [Alarm] = []
    
    func addAlarm(_ alarmTime: String, _ alarmId: Int) {
        let newAlarm = Alarm(id: alarmId, alarmTime: alarmTime, selected: false, isAlarmOn: false) // , isAlarmOn: .constant(false))
        alarms.append(newAlarm)
    }
    
    //other functions...
}


struct AlarmListView: View {   
//    @ObservedObject var alarmModel : AlarmModel
    @StateObject var alarmModel = AlarmModel()
    
    var body: some View {
        List {
            ForEach($alarmModel.alarms, id: \.self) { $alarm in
                HStack {
                    Text(" \(alarm.alarmTime)")
                    Toggle("", isOn: $alarm.isAlarmOn)
                }
            }
        }
        .onAppear {
            alarmModel.addAlarm("Alarm1", 1)
            alarmModel.addAlarm("Alarm2", 2)
        }
    }
}

Note you can simplify a little, suppressing HStack:

            ForEach($alarmModel.alarms, id: \.self) { $alarm in
                    Toggle("\(alarm.alarmTime)", isOn: $alarm.isAlarmOn)
            }
My Toggle doesn't work the way I want it to
 
 
Q