简体   繁体   中英

Firebase Firestore doesn't update UI after deletion SwiftUI

I'm trying to delete an object from my Firestore database through SwiftUI and the data deletes successfully. However it doesn't update in my UI, unless I reload it manually.

Right now, I'm using @Environment(\.presentationMode) var presentation to dismiss the view after deletion completion.

Here's my Firebase code:

func deleteData(category: String,completion: @escaping (success) -> Void){
    db.collection("passwords").document("\(Auth.auth().currentUser!.uid)").getDocument{(snapshot, error) in
        guard let snapshot = snapshot, error == nil else {
            return
        }
        let documentID = snapshot.data()!["date"] as! String
        db.collection("passwords").document("\(Auth.auth().currentUser!.uid)").collection(documentID).document("\(category)").delete(){ err in
            if let err = err {
                print("Error removing document: \(err)")
                completion(false)
            } else {
                print("Document successfully removed!")
                completion(true)
            }
        }
        
    }
}

And here's the SwiftUI code:

@Environment(\.presentationMode) var presentation
var body: some View {
    ForEach(self.data){item in
        HStack{
            Text(item.category)
            Spacer()
            Button(action: {
                deleteData(category: item.category!){
                    (success) -> Void in
                    if success {
                        print("deleted successfully")
                        self.presentation.wrappedValue.dismiss()
                    }
                }
            }){
                Text("Delete")
            }
        }
            .onAppear{
                fetchData()
            }
    }

}



class FirebaseData: ObservableObject {
    @State var id: String?
    @State var category: String
    @State var password: String
    
    init(id: String?, password: String, category: String) {
        self.id = id
        self.password = password
        self.category = category
    }
}

self.data comes from @State var data: [FirebaseData] = [] .

There are a few problems here:

  1. In general, when doing asynchronous work, you probably want to do them in an ObservableObject , and not in the View itself -- I've moved the code into a view model.

  2. Your model shouldn't be an ObservableObject -- it should just be a struct. Using @State on an ObservableObject doesn't do anything anyways -- use @Published values when using ObservableObject

  3. You're using getDocument which gets the data once , which is why your UI isn't updating. If you want the UI to update, use snapshot listeners: https://firebase.google.com/docs/firestore/query-data/listen

struct FirebaseData {
    var id: String?
    var category: String
    var password: String
}

class ViewModel : ObservableObject {
    @Published var data : [FirebaseData] = []
    private let db = Firestore.firestore()
    private var listener : ListenerRegistration?
    
    func fetchData(){
        db.collection("passwords").document("\(Auth.auth().currentUser!.uid)")
            .getDocument{(snapshot, error) in
                guard let snapshot = snapshot, error == nil else {
                    return
                }
                let documentID = snapshot.data()!["date"] as! String
                
                self.listener = self.db.collection("passwords").document("\(Auth.auth().currentUser!.uid)").collection(documentID)
                    .addSnapshotListener(){ (querySnapshot, err) in //<-- Here
                        if let err = err {
                            print("Error getting documents: \(err)")
                        } else {
                            self.data = (querySnapshot?.documents ?? []).map {
                                FirebaseData(id: $0.documentID,
                                             category: $0.data()["category"] as? String ?? "",
                                             password: $0.data()["password"] as? String ?? "")
                            }
                        }
                    }
            }
    }
    
    func deleteData(category: String,completion: @escaping (success) -> Void){
        db.collection("passwords").document("\(Auth.auth().currentUser!.uid)").getDocument{(snapshot, error) in
            guard let snapshot = snapshot, error == nil else {
                return
            }
            let documentID = snapshot.data()!["date"] as! String
            self.db.collection("passwords").document("\(Auth.auth().currentUser!.uid)").collection(documentID).document("\(category)").delete(){ err in
                if let err = err {
                    print("Error removing document: \(err)")
                    completion(false)
                } else {
                    print("Document successfully removed!")
                    completion(true)
                }
            }
        }
    }
}

struct ContentView: View {
    
    @Environment(\.presentationMode) var presentation
    @StateObject private var viewModel = ViewModel()
    
    
    var body: some View {
        NavigationView{
            ForEach(viewModel.data, id: \.self.id) { item in
                HStack{
                    Text(item.category)
                    Spacer()
                    Button(action: {
                        viewModel.deleteData(category: item.category){
                            (success) -> Void in
                            if success {
                                print("deleted successfully")
                            }
                        }
                    }){
                        Text("Delete")
                    }
                }
            }
            .onAppear{
                viewModel.fetchData()
            }
        }
    }
}

There's some more refactoring I'd do, such as:

  1. Make sure you're not using ! anywhere to force unwrap -- this can cause crashes
  2. Store the document ID so that you're not doing another query inside deleteData

But, this should get you started for now.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM