SwiftUI Lists + Firebase Firestore,获取数据然后取消获取? (漏洞)

Posted

技术标签:

【中文标题】SwiftUI Lists + Firebase Firestore,获取数据然后取消获取? (漏洞)【英文标题】:SwiftUI Lists + Firebase Firestore, fetching data and then unfetching? (Bug) 【发布时间】:2020-08-06 21:35:02 【问题描述】:

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

我有一些模型类,然后是一些 ViewModel 来获取数据、过滤、将文档添加到 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") 行,它确实显示了。所以基本上,视图加载(这一切都在来自父级的导航视图中),它获取数据,显示一秒钟(列表填充),然后出于某种原因卸载该数据,只留下“没有类找到”。

有关更多上下文,这是 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
        
        
    


以及该视图的 FURTHER 父级(实际上是应用程序的起始视图):

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(这里出现BUG)

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

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

【问题讨论】:

【参考方案1】:

修好了! 问题是在导航视图中传递了用于演示模式的 EnvironmentObject。这似乎会导致很多不良行为。 小心传递presentationMode,因为它似乎会导致数据重新加载(因为它正在更新)。

【讨论】:

以上是关于SwiftUI Lists + Firebase Firestore,获取数据然后取消获取? (漏洞)的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 和 Firebase:当前用户是不是持续存在?

SwiftUI - 在 Firebase 上更新密码

SwiftUI - 与 Microsoft 的 Firebase 身份验证

SwiftUI Firebase - 在 Firebase 中添加或减去整数

Firebase 身份验证 swiftui

Firebase+SwiftUI。无法转换价值