SwiftUI列表+ Firebase Firestore,获取数据然后取消获取? (错误)

时间:2020-08-06 21:35:02

标签: ios firebase google-cloud-firestore swiftui swiftui-list

我不确定我的项目出了什么问题。基本上,我有一个非常典型的数据结构:我将Firestore用于基于文档的实时数据库,并且有很多不同的集合,文档和字段。只是非常简单,没有什么疯狂的。

我有一些模型类,然后有一些ViewModels来获取数据,过滤,将文档添加到Firestore等。

应用程序本身几乎是100%SwiftUI,我想保持这种状态,这对我自己的开发来说是一个挑战。不过我碰到了墙。

在应用程序中,我具有一系列带有NavigationView列表的视图,这些列表将在导航时向其中传递少量数据。一个示例(这是针对教育机构的)可能是“学校列表”>“班级列表”>“班级学生列表”>“学生成绩列表”。基本上,是导航视图和一堆列表的完美用法。

错误: 当我从堆栈中的一个列表移到下一个列表时,我获取了Firestore数据,该数据会加载一秒钟(足以填充列表),然后“卸载”为空。这是发生这种情况的一些代码(我将其清理为尽可能简单):

struct ClassListView: View {
    let schoolCode2: String
    let schoolName2: String
    @ObservedObject private var viewModelThree = ClassViewModel()
    
    var body: some View {
        VStack{
            List{
                if viewModelThree.classes.count > 0{
                    ForEach(self.viewModelThree.classes) { ownedClass in
                        NavigationLink(destination: StudentListView()){
                            Text(ownedClass.className)
                        }
                        
                    }
                } else {
                    Text("No Classes Found for \(schoolName2)")
                }
            }
        }
            .navigationBarTitle(schoolName2)
            .onAppear(){
                print("appeared")
                self.viewModelThree.fetchData(self.schoolCode2)
        } 
    }
}

这就是我一直遇到问题的ClassListView。为了调试,我添加了else Text(“ No Classes Found”)行,它的确显示了。因此,基本上,视图加载(全部在父级的Nav视图中进行),它获取数据,显示一秒钟(列表填充),然后出于某种原因卸载该数据,只剩下“无类”找到”。

有关更多上下文,这是ClassViewModel的代码(也许这就是我要去的地方吗?):

struct Classes: Identifiable, Codable {
    @DocumentID var id: String? = UUID().uuidString
    var schoolCode: String
    var className: String
}

enum ClassesCodingKeys: String, CodingKey {
    case id
    case schoolCode
    case className
}

class ClassViewModel: ObservableObject {
    @Published var classes = [Classes]()
    private var db = Firestore.firestore()
    
    func fetchData(_ schoolCode: String) {

        db.collection("testClasses")
            .order(by: "className")
            .whereField("schoolCode", isEqualTo: schoolCode)
            .addSnapshotListener{ (querySnapshot, error) in
                guard let documents = querySnapshot?.documents else {
                    print("no docs found")
                    return
                }
                self.classes = documents.compactMap{ queryDocumentSnapshot -> Classes? in
                    return try? queryDocumentSnapshot.data(as: Classes.self)
                }
        }
    }
    
    func addClass(currentClass: Classes){
        do {
            let _ = try db.collection("testClasses").addDocument(from: currentClass)
        }
        catch {
            print(error)
        }
    }
    
}

最相关的位是上面的fetchData()函数。

问题可能出在此之前的视图中(父视图?)。在这里:

struct SchoolUserListView: View {
    @State private var userId: String?
    @EnvironmentObject var session: SessionStore
    @ObservedObject private var viewModel = UserTeacherViewModel()
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    @State private var counter = 0
    @State private var showingAddSchool: Bool = false
    @Environment(\.presentationMode) var presentationMode

    func getUser() {
        session.listen()
    }
    var body: some View {
        VStack{
            List{
                if viewModel.teachers.count > 0{
                    ForEach(viewModel.teachers) { ownedClass in
                        NavigationLink(destination: ClassListView(schoolName2: ownedClass.schoolName, schoolCode2: ownedClass.schoolCode)){
                            Text(ownedClass.schoolName)
                        }
                    }
                } else {
                    Button(action:{
                        self.presentationMode.wrappedValue.dismiss()
                    }){
                        Text("You have no schools, why not add one?")
                    }
                }
            }
            .navigationBarTitle("Your Schools")
             
        }
        .onAppear(){
            self.getUser()
            self.userId = self.session.session?.uid
        }

        .onReceive(timer){ time in
            if self.counter == 1 {
                self.timer.upstream.connect().cancel()
            } else {
                print("fetching")
                self.viewModel.fetchData(self.userId!)
            }
            self.counter += 1
        }
        
    }
}

该视图(实际上是应用程序的起始视图)的另一个父级:

struct StartingView: View {

    @EnvironmentObject var session: SessionStore
    
    func getUser() {
        session.listen()
    }
    var body: some View {
        NavigationView{
            VStack{
                Text("Welcome!")
                Text("Select an option below")
                Group{
                NavigationLink(destination:
                    SchoolUserListView()
                ){
                    Text("Your Schools")
                }
                NavigationLink(destination:
                    SchoolCodeAddView()
                ){
                    Text("Add a School")
                }
                Spacer()
                Button(action:{
                    self.session.signOut()
                }){
                    Text("Sign Out")
                }
                

            }
            }
        }
        .onAppear(){
            self.getUser()
        }
    }
    
}

我知道很多,但每个人都有父母的命令:

StartingView> SchoolUserListView> ClassListView(此处出现错误)

顺便说一句,SchoolUserListView使用的fetchData方法就像ClassListView(错误所在的地方)一样,并将其输出到foreach列表,同样的事情,但是没有问题吗?我只是不确定问题是什么,我们将不胜感激任何帮助!

以下是该问题的视频: https://imgur.com/a/kYtow6G

1 个答案:

答案 0 :(得分:1)

解决了! 问题是在导航视图中传递了presentationmode的EnvironmentObject。这似乎导致了很多不良行为。 小心传递presentationMode,因为它似乎会导致数据重新加载(因为正在更新)。