I am creating a section within my app where it will be possible to see the list of users and their permissions (inserted in the form of an array) inserted into the database.
I view the user's information and permissions in this way:
List(administratorManager.users) { user in
HStack {
VStack(alignment: .leading) {
Text(user.number).font(.subheadline)
Text(user.name).font(.subheadline)
Text(user.surname).font(.subheadline)
VStack(alignment: .leading){
Text("Permessi")
.font(.title2)
.fontWeight(.bold)
.padding(1)
HStack{
Image(systemName: user.permessi[0] ? "checkmark.square.fill" : "square")
.foregroundColor(user.permessi[0] ? Color(UIColor.systemBlue) : Color.secondary)
.onTapGesture {
user.permessi[0].toggle()
}
Text("0")
.onTapGesture {
user.permessi[0].toggle()
}
}
HStack{
Image(systemName: user.permessi[1] ? "checkmark.square.fill" : "square")
.foregroundColor(user.permessi[1] ? Color(UIColor.systemBlue) : Color.secondary)
.onTapGesture {
user.permessi[1].toggle()
}
Text("1")
.onTapGesture {
user.permessi[0].toggle()
}
}
}
}
}
}
.onAppear() {
self.administratorManager.fetchData(collection: "Admins")
}
The data is constantly read from the database in this way and saved in a structure:
func fetchData(collection: String) {
DatabaseFirestore.collection(collection).addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No documents")
return
}
self.users = documents.map { (queryDocumentSnapshot) -> User in
let data = queryDocumentSnapshot.data()
let name = data["Nome"] as? String ?? ""
let surname = data["Cognome"] as? String ?? ""
let school = data["Scuola"] as? String ?? ""
let orari = data["Orari"] as? [String] ?? []
let permessi = data["Permessi"] as? [Bool] ?? []
let number = queryDocumentSnapshot.documentID
return User(name: name, surname: surname, school: school, orari: orari, permessi: permessi, number: number)
}
}
}
This is the structure:
struct User: Identifiable {
var id: String = UUID().uuidString
var name: String
var surname: String
var school: String
var orari: [String]
var permessi: [Bool]
var number: String
}
The data is correctly displayed like this:
The problem arises when the checkbox is pressed. when this is added I am told that user is a let constant:
.onTapGesture {
user.permessi[0].toggle()
}
How could I change the code to make it work?
Also I tried to shorten the code through a forEach:
ForEach(user.permessi) p in {
HStack{
Image(systemName: p ? "checkmark.square.fill" : "square")
.foregroundColor(p ? Color(UIColor.systemBlue) : Color.secondary)
.onTapGesture {
}
Text("0")
.onTapGesture {
}
.padding(1)
}
}
I know it is wrong but no error message is displayed. How do I resolve?
CodePudding user response:
To be able to mutate a value, the object and the element must be var. But the object it let.
UPDATE:
I tested it and the following approach works for me. Please keep in mind, the issue in your question is SwiftUI related and not firebase related, so instead of the firebase storage I am using some local data, but the array of User elements should be identical/similar and the implementation of the usage is the same.
In my example the UserViewModel is what you name the AdministratorManager. But even your AdministratorManager class should be an ObservableObject and the users array must contain @Published.
//
// ContentView.swift
// SwiftTest
//
// Created by Sebastian on 11.08.22.
//
import SwiftUI
struct ContentView: View {
@StateObject var userViewModel = UserViewModel()
var body: some View {
VStack() {
UserView(userViewModel: userViewModel)
}
}
}
struct UserView: View {
@ObservedObject var userViewModel: UserViewModel
var body: some View {
VStack(){
ForEach(userViewModel.users.indices, id: \.self){ id in
VStack(alignment: .leading) {
Text(userViewModel.users[id].number).font(.subheadline)
Text(userViewModel.users[id].name).font(.subheadline)
Text(userViewModel.users[id].surname).font(.subheadline)
ForEach(userViewModel.users[id].permessi.indices, id: \.self){ idp in
Button(action: {
userViewModel.users[id].permessi[idp].toggle()
}) {
Image(systemName: userViewModel.users[id].permessi[idp] ? "checkmark.square.fill" : "square")
.foregroundColor(userViewModel.users[id].permessi[idp] ? Color(UIColor.systemBlue) : Color.secondary)
}
}
}
.padding()
}
}
}
}
struct User: Identifiable, Hashable {
var id: String = UUID().uuidString
var name: String
var surname: String
var school: String
var permessi: [Bool]
var number: String
}
class UserViewModel: ObservableObject {
@Published var users = [
User(name: "Sebastian",
surname: "F",
school: "School",
permessi: [false, false, false],
number: "1"),
User(name: "Mickele",
surname: "M",
school: "School",
permessi: [true, true, true],
number: "2")
]
}
Best, Sebastian