Перенос значений массива, содержащих карту, в Firestore с использованием Swift

Я разрабатываю приложение для своего школьного проекта, но у меня возникла проблема, и я не могу ее решить. Я пытаюсь получить значения midTerm, finalTerm и lectureGradeId из моего Firebase Cloud Firestore. За исключением этих значений, я могу получить все значения из Firestore.

Чтобы было понятно, я привожу свои коды и структуру Firestore ниже:

Студенты.swift

import Foundation
import FirebaseFirestore

struct Grades: Codable, Hashable {
var lectureGradeId: String
var midTerm: String
var endTerm: String
}

struct Students: Identifiable, Codable {
var id: String
var email: String
var studentName: String
var studentSurname: String
var studentTerm: String
var studentBirthday: String
var studentClass: Int
var studentDept: String
var studentFaculty: String
var studentIsActive: Bool
var studentIsGraduated: Bool
var studentLectures: [String]?
var studentGrades: [Grades]?
}

СтудентыViewModel.swift

import Foundation
import Firebase

class StudentsViewModel: ObservableObject {
@Published var students = [Students]()

private var db = Firestore.firestore()

func fetchData() {
    db.collection("Students").whereField("id", isEqualTo: "20704029").addSnapshotListener { querySnapshot, error in
        guard let documents = querySnapshot?.documents else {
            print("No documents")
            return
        }
        
        self.students = documents.map { (queryDocumentSnapshot) -> Students in
            let data = queryDocumentSnapshot.data()
            
            let id = data["id"] as? String ?? ""
            let email = data["email"] as? String ?? ""
            let studentName = data["studentName"] as? String ?? ""
            let studentSurname = data["studentSurname"] as? String ?? ""
            let studentTerm = data["studentTerm"] as? String ?? ""
            let studentBirthday = data["studentBirthday"] as? String ?? ""
            let studentClass = data["studentClass"] as? Int ?? 0
            let studentDept = data["studentDept"] as? String ?? ""
            let studentFaculty = data["studentFaculty"] as? String ?? ""
            let studentIsActive = data["studentIsActive"] as? Bool ?? true
            let studentIsGraduated = data["studentIsGraduated"] as? Bool ?? false
            let studentLectures = data["studentLectures"] as? Array ?? [""]
            
            let student = Students(id: id, email: email, studentName: studentName, studentSurname: studentSurname, studentTerm: studentTerm, studentBirthday: studentBirthday, studentClass: studentClass, studentDept: studentDept, studentFaculty: studentFaculty, studentIsActive: studentIsActive, studentIsGraduated: studentIsGraduated, studentLectures: studentLectures)
            //print(student)
            return student
        }
    }
}
}

ОценкиPage.swift

import SwiftUI

struct GradesPage: View {

@ObservedObject private var viewModel = StudentsViewModel()

var body: some View {
    NavigationView {
        List(viewModel.students) { student in
            VStack(alignment: .leading) {
                Text("Student Name: ") + Text(" ") + Text(student.studentName) + Text(" ") + Text(student.studentSurname)
                //I want to add the grades to see in here but don't know how to handle it
            }
        }
    }.onAppear {
        self.viewModel.fetchData()
    }
}
}

Структура пожарного склада:

Решения, которые я пробовал:

Документация Питера Фризе

Официальная документация Firestore

Заранее спасибо, всякая помощь приветствуется!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
78
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Обратите внимание: вы должны использовать @StateObject private var viewModel = StudentsViewModel(). Также обратите внимание: в вашем коде нет finalTerm и NavigationView устарел, используйте NavigationStack

Чтобы отобразить studentGrades в вашем GradesPage представлении, просто используйте ForEach, например:

struct GradesPage: View {
    @StateObject private var viewModel = StudentsViewModel()
    
    var body: some View {
        NavigationStack {  // <--- here
            List(viewModel.students) { student in
                VStack(alignment: .leading) {
                    Text("Student Name: ") + Text(" ") + Text(student.studentName) + Text(" ") + Text(student.studentSurname)

                    ForEach(student.studentGrades ?? []) { grade in  // <--- here
                       Text(grade.lectureGradeId)
                       Text(grade.midTerm)
                       Text(grade.endTerm)
                    }
                }
            }
        }
        .onAppear {
            self.viewModel.fetchData()
        }
    }
}

struct Grades: Identifiable, Codable, Hashable {  //<--- here
    let id = UUID()
    var lectureGradeId: String
    var midTerm: String
    var endTerm: String
    
    enum CodingKeys: String, CodingKey {
        case lectureGradeId, midTerm, endTerm
    }
}

struct Students: Identifiable, Codable {
    var id: String
    var email: String
    var studentName: String
    var studentSurname: String
    var studentTerm: String
    var studentBirthday: String
    var studentClass: Int
    var studentDept: String
    var studentFaculty: String
    var studentIsActive: Bool
    var studentIsGraduated: Bool
    var studentLectures: [String]?
    var studentGrades: [Grades]?
}


class StudentsViewModel: ObservableObject {
    @Published var students = [Students]()

    private var db = Firestore.firestore()
    
    func fetchData() {
        db.collection("Students").whereField("id", isEqualTo: "20704029").addSnapshotListener { querySnapshot, error in
            guard let documents = querySnapshot?.documents else {
                print("No documents")
                return
            }
            
            self.students = documents.map { (queryDocumentSnapshot) -> Students in
                let data = queryDocumentSnapshot.data()
                
                let id = data["id"] as? String ?? ""
                let email = data["email"] as? String ?? ""
                let studentName = data["studentName"] as? String ?? ""
                let studentSurname = data["studentSurname"] as? String ?? ""
                let studentTerm = data["studentTerm"] as? String ?? ""
                let studentBirthday = data["studentBirthday"] as? String ?? ""
                let studentClass = data["studentClass"] as? Int ?? 0
                let studentDept = data["studentDept"] as? String ?? ""
                let studentFaculty = data["studentFaculty"] as? String ?? ""
                let studentIsActive = data["studentIsActive"] as? Bool ?? true
                let studentIsGraduated = data["studentIsGraduated"] as? Bool ?? false
                let studentLectures = data["studentLectures"] as? Array ?? [""]
                
                // <--- something like this, adjust as needed, don't remember how it works
                let studentGrades = data["studentGrades"] as? [Grades] ?? []

                let student = Students(id: id, email: email, studentName: studentName, studentSurname: studentSurname, studentTerm: studentTerm, studentBirthday: studentBirthday, studentClass: studentClass, studentDept: studentDept, studentFaculty: studentFaculty, studentIsActive: studentIsActive, studentIsGraduated: studentIsGraduated, studentLectures: studentLectures,
                    studentGrades: studentGrades   //<--- here
                )

                //print(student)
                return student
            }
        }
    }
}

Обратите внимание: вы можете попробовать сопоставить результаты запроса Firebase непосредственно с вашу кодируемую структуру Students, см., например: https://firebase.google.com/docs/firestore/solutions/swift-codable-data-mapping и https://firebase.google.com/docs/firestore/query-data/get-data

Например, из: https://firebase.google.com/docs/firestore/solutions/swift-codable-data-mapping

func fetchData() {
    let docRef = db.collection("Students").whereField("id", isEqualTo: "20704029")
    docRef.getDocument(as: Students.self) { result in
        switch result {
        case .success(let students):
            self.students = students
        case .failure(let error):
            print("Error decoding document: \(error)")
            // todo deal with errors
        }
    }
}
 

Спасибо за ответ, но есть некоторые проблемы. Во-первых; Я все записал и ничего не изменилось. Я все еще не могу получить значения. И второе; во втором фрагменте кода я получаю сообщение об ошибке docRef.getDocument(as: Students.self) { result in, в котором говорится: «Значение типа «Query» не имеет члена «getDocument»», поэтому я не могу его использовать. Можете ли вы просветить меня?

caneraltuner 31.05.2024 10:59

Хорошо, поскольку у меня нет вашей базы данных, я не могу попробовать код, однако вы можете попробовать использовать что-то вроде let docRef = db.collection("Students").document("20704029"). Если необходимо, попробуйте использовать @DocumentID var id: String? в своем struct Students, как указано в документации по ссылке.

workingdog support Ukraine 31.05.2024 14:19

Проведя множество исследований, я наконец решил свою проблему. Во-первых, я попробовал каждый код в ответе службы поддержки @workingdog Ukraine. Но мне не удалось выполнить второй фрагмент кода. Обычно метод getDocument используется для одного документа, но я использую в своем коде много документов. Итак, я изменил свой код следующим образом:

let query = db.collection("Students").whereField("id", isEqualTo: "20704029")
query.getDocuments { snapshot, error in
if let error = error {
    print("Error getting documents: \(error)")
    // todo deal with errors
} else {
    self.students = snapshot?.documents.compactMap { document in
        try? document.data(as: Students.self)
} ?? []
}
}

После изменений мое приложение начало работать так, как я хотел, и вот скриншот:

Теперь моя проблема решена.

Другие вопросы по теме