子视图中的ListView未正确刷新

时间:2020-06-06 21:17:02

标签: swift firebase google-cloud-firestore swiftui

有一个ListView。当我在列表中单击元素时,通过更改元素的字段在Cloud Firestore中进行交易。数据库中的数据会按其应有的方式进行更改,但是此操作之后,列表中的所有元素都会消失(尽管有.onAppear {fetchData})。重要一点:这是一个子视图,在父视图中没有这样的问题。

我还在列表底部添加了一个按钮来执行fetchData(),当我单击它时,数据将返回到列表

可能是什么问题?谢谢

import SwiftUI

struct SecondView: View {
    @ObservedObject var viewModel = BooksViewModel()

    var body: some View {
        VStack {
            List(viewModel.books) { book in
               VStack(alignment: .leading) {
                Button("Update data"){
                    let updBook = book
                    self.viewModel.myTransaction(book: updBook)
                }
                 Text(book.title)
                   .font(.headline)
                 Text(book.author)
                   .font(.subheadline)
                 Text("\(book.numberOfPages) pages")
                   .font(.subheadline)
               }
             }
             .navigationBarTitle("Books")
             .onAppear() {
               self.viewModel.fetchData()
            }
            Button("update list"){
                self.viewModel.fetchData()
            }
        }
    }
}

ViewModel:

import Foundation
import FirebaseFirestore
import FirebaseFirestoreSwift

class BooksViewModel: ObservableObject {
    @Published var books = [Book]()

    private var db = Firestore.firestore()

    func fetchData() {
        db.collection("books").addSnapshotListener { (querySnapshot, error) in
            guard let documents = querySnapshot?.documents else {
                print("No documents")
                return
            }

            self.books = documents.compactMap { queryDocumentSnapshot -> Book? in
                return try? queryDocumentSnapshot.data(as: Book.self)
            }
        }
    }

    func deleteBook(book: Book){
        if let bookID = book.id{
            db.collection("books").document(bookID).delete()
        }
    }

    func updateBook(book: Book) {
        if let bookID = book.id{
            do {
                try db.collection("books").document(bookID).setData(from: book) }
            catch {
                print(error)
            }
        }
    }

    func addBook(book: Book) {
        do {
            let _ = try db.collection("books").addDocument(from: book)
        }
        catch {
            print(error)
        }
    }

    func myTransaction(book: Book){
        let bookID = book.id

        let targetReference = db.collection("books").document(bookID!)

        db.runTransaction({ (transaction, errorPointer) -> Any? in
            let targetDocument: DocumentSnapshot
            do {
                try targetDocument = transaction.getDocument(targetReference)
            } catch let fetchError as NSError {
                errorPointer?.pointee = fetchError
                return nil
            }

            guard let oldValue = targetDocument.data()?["pages"] as? Int else {
                let error = NSError(
                    domain: "AppErrorDomain",
                    code: -1,
                    userInfo: [
                        NSLocalizedDescriptionKey: "Unable to retrieve population from snapshot \(targetDocument)"
                    ]
                )
                errorPointer?.pointee = error
                return nil
            }

            // Note: this could be done without a transaction
            //       by updating the population using FieldValue.increment()
            transaction.updateData(["pages": oldValue + 1], forDocument: targetReference)
            return nil
        }) { (object, error) in
            if let error = error {
                print("Transaction failed: \(error)")
            } else {
                print("Transaction successfully committed!")
            }
        }
    }

}

父视图:

import SwiftUI

struct ContentView: View {
     @ObservedObject var viewModel = BooksViewModel() 

     var body: some View {
       NavigationView {
        VStack {
            List(viewModel.books) { book in 
               VStack(alignment: .leading) {
                Button("Update"){
                    let delBook = book
                    self.viewModel.myTransaction(book: delBook)
                }
                 Text(book.title)
                   .font(.headline)
                 Text(book.author)
                   .font(.subheadline)
                 Text("\(book.numberOfPages) pages")
                   .font(.subheadline)
               }
             }
             .navigationBarTitle("Books")
             .onAppear() { 
               self.viewModel.fetchData()
            }
            NavigationLink(destination: SecondView()){
                Text("Second View")
            }
        }
       }
     }
}

1 个答案:

答案 0 :(得分:0)

一个可能的解决方案可能是您的View及其ViewModel相互干扰。好像您创建了一个相同的BookViewModel的两个实例:

struct ContentView: View {
     @ObservedObject var viewModel = BooksViewModel() 

struct SecondView: View {
    @ObservedObject var viewModel = BooksViewModel()

尝试创建一个BooksViewModel并将其在视图之间传递(您可以使用@EnvironmentObject)。