Home > other >  Check box with the data taken from the database - Swift - SwiftUi - Firebase
Check box with the data taken from the database - Swift - SwiftUi - Firebase

Time:08-13

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:

Phone of the screen

Photo of the database

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

  • Related