Reputation: 532
I am currently getting a list of sites from a Firebase Firestore and then returning them to a list in SwiftUI. Each list item has a label and Toggle. The list of sites is dynamic so could be anywhere from 1-30+. How can I create an @State or similar bindable to observe each toggle's state.
I am currently rendering to UI with the following
@State private var SiteA = false
Form {
Section (header: Text("Select Sites")) {
ForEach(siteData.sites) { site in
HStack {
Toggle(isOn: self.$SiteA) {
Text(site.name)
Spacer()
}
}
}
}
}
Sites are retrieved using a Bindable object
import SwiftUI
import Combine
import Firebase
import FirebaseFirestore
struct Message: Identifiable {
var title: String
var messageBody: String
var sentBy: String
var targetSite: String
var expired: Bool
var timeStamp: Timestamp
var emergency: Bool
var id: String
}
struct Site: Identifiable {
var id: String
var name: String
}
class FirestoreMessages : ObservableObject {
var db = Firestore.firestore()
var didChange = PassthroughSubject<FirestoreMessages, Never>()
@Published var messages: [Message] = [] {
didSet{ didChange.send(self) }
}
@Published var sites: [Site] = [] {
didSet { didChange.send(self) }
}
func listen() {
db.collection("messages")
.whereField("expired", isEqualTo: false)
.addSnapshotListener { (snap, error) in
if error != nil {
print("Firebase Snapshot Error: \(error?.localizedDescription ?? "")")
} else {
self.messages.removeAll()
for doc in snap!.documents {
let title = doc["title"] as! String
let messageBody = doc["body"] as! String
let sentBy = doc["sentBy"] as! String
let targetSite = doc["targetSite"] as! String
let expired = doc["expired"] as! Bool
let timeStamp = doc["timeStamp"] as! Timestamp
let emergency = doc["emergency"] as! Bool
let id = doc.documentID
let message = Message(
title: title,
messageBody: messageBody,
sentBy: sentBy,
targetSite: targetSite,
expired: expired,
timeStamp: timeStamp,
emergency: emergency,
id: id)
self.messages.append(message)
}
}
}
}
func getSites() {
db.collection("sites")
.order(by: "name", descending: false)
.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting docs: \(err)")
} else {
self.sites.removeAll()
for document in querySnapshot!.documents {
let doc = document.data()
let id = document.documentID
let name = doc["name"] as! String
let site = Site(id: id, name: name)
self.sites.append(site)
}
}
}
}
}
How can I create an @State
unique to each list item to monitor their states individually?
Upvotes: 4
Views: 1574
Reputation: 1092
The answer to your problem is composition. Move the HStack and enclosed Toggle to a SiteRow view where each row has its own State.
struct SiteRow: View {
@State private var state: Bool = false
private let site: Site
init(_ site: Site) {
self.site = site
self.state = site.isOn
}
var body: some View {
HStack {
Toggle(isOn: self.$state) {
Text(site.name)
Spacer()
}
}
}
}
Then...
ForEach(siteData.sites) { site in SiteRow(site) }
Upvotes: 5