Списки SwiftUI + Firebase Firestore, получение данных, а затем извлечение? (Ошибка) - PullRequest
0 голосов
/ 07 августа 2020

Я не уверен, в чем проблема с моим проектом. В принципе, у меня довольно типичная структура данных: я использую Firestore для базы данных на основе do c в реальном времени, и у меня есть множество различных коллекций, документов и полей. Просто очень просто, ничего сумасшедшего.

У меня есть несколько классов моделей, а затем несколько 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 («Классы не найдены»), и она действительно отображается. Итак, в основном, просмотр загрузок (это все в представлении Nav от родителя), он извлекает данные, которые отображаются в течение секунды (список заполняется), а затем выгружает эти данные по какой-то причине, оставляя мне только "Нет классов 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
        }
        
    }
}

И ДАЛЬНЕЙШИЙ родительский элемент этого представления (и фактически начального представления приложения):

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 Ответ

1 голос
/ 07 августа 2020

Исправлено! Проблема заключалась в том, что объект EnvironmentObject для режима презентации передавался в представлении навигации. Похоже, это вызывает МНОГО нежелательного поведения. Будьте осторожны, передавая режим presentationMode, так как он вызывает перезагрузку данных (поскольку он обновляется).

...