Я разрабатываю приложение для своего школьного проекта, но у меня возникла проблема, и я не могу ее решить. Я пытаюсь получить значения 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
Заранее спасибо, всякая помощь приветствуется!
Обратите внимание: вы должны использовать
@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
}
}
}
Хорошо, поскольку у меня нет вашей базы данных, я не могу попробовать код, однако вы можете попробовать использовать что-то вроде let docRef = db.collection("Students").document("20704029")
. Если необходимо, попробуйте использовать @DocumentID var id: String?
в своем struct Students
, как указано в документации по ссылке.
Проведя множество исследований, я наконец решил свою проблему. Во-первых, я попробовал каждый код в ответе службы поддержки @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)
} ?? []
}
}
После изменений мое приложение начало работать так, как я хотел, и вот скриншот:
Теперь моя проблема решена.
Спасибо за ответ, но есть некоторые проблемы. Во-первых; Я все записал и ничего не изменилось. Я все еще не могу получить значения. И второе; во втором фрагменте кода я получаю сообщение об ошибке
docRef.getDocument(as: Students.self) { result in
, в котором говорится: «Значение типа «Query» не имеет члена «getDocument»», поэтому я не могу его использовать. Можете ли вы просветить меня?