Как получить файл Swift, который запускает запросы к БД и возвращает переменные из представлений
Я пытаюсь создать класс, который запускает запросы Firebase и сохраняет результат в переменных. Затем с разных точек зрения я обращаюсь к переменным. Каждая переменная относится к одной категории.
Я создал класс в том же представлении, в котором показываю данные в приложении, и он работает. Но когда я перемещаю его в отдельный быстрый файл, он выдает следующую ошибку:
struct ContentView: View {
var body: some View {
Home()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Home : View {
@ObservedObject var categories = getCategoriesData()
var body: some View {
// Error: Unable to infer complex closure return type; add explicit type to disambiguate
VStack {
List(categories.datas) { i in
Text(i.name)
}
}
}
}
Swift файл:
class getCategoriesData : ObservableObject {
@Published var datas = [category]()
init () {
let db = Firestore.firestore()
db.collection("ingredients").addSnapshotListener{ (snap, err) in
if err != nil {
print((err?.localizedDescription)!)
return
}
for i in snap!.documentChanges {
let id = i.document.documentID
let name = i.document.get("name") as! String
self.datas.append(category(id: id, name: name))
}
}
}
}
struct category : Identifiable {
var id : String
var name : String
}
Чтобы быть ясным, сейчас я получаю данные из ingredients
категория в Firebase, я хочу повторно запустить класс и сохранить результат каждой категории в массиве / карте datas
в этом случае я могу получить к ним доступ в разных представлениях. ИЛИ иметь его в другом классе, поэтому для каждого представления я запускаю запрос БД и получаю данные для одной из категорий.
Есть идеи, как заставить это работать?
Благодарность
1 ответ
Мне удалось скомпилировать ваш код, но на панели предварительного просмотра говорилось, что не удалось отобразить представление из-за сбоя компилятора.
Основная проблема с вашим кодом, похоже, заключается в том, что код доступа к данным находится в вашем инициализаторе. Я бы не рекомендовал это делать. Вместо этого извлеките этот фрагмент кода в методfetchData
и вызовите это, как только появится ваше представление. Например так:
import SwiftUI
import Firebase
struct ContentView: View {
var body: some View {
Home()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Home : View {
@ObservedObject var viewModel = CategoryViewModel()
var body: some View {
VStack {
List(viewModel.categories) { category in
Text(category.name)
}
.onAppear() {
self.viewModel.fetchData()
}
}
}
}
import Foundation
import FirebaseFirestore
struct Category : Identifiable {
var id : String
var name : String
}
class CategoryViewModel: ObservableObject {
@Published var categories = [Category]()
func fetchData() {
let db = Firestore.firestore()
db.collection("ingredients").addSnapshotListener{ (snapshot, err) in
guard let documents = snapshot?.documents else {
print("No documents")
return
}
self.categories = documents.map { documentSnapshot in
let id = documentSnapshot.documentID
let name = documentSnapshot.get("name") as! String
return Category(id: id, name: name)
}
}
}
}
Вы заметите, что я также очистил именование (я бы порекомендовал прочитать руководства по стилю Рэя Вендерлиха и Google для Swift) и преобразовал ваш код сопоставления вmap
- это упрощает чтение и делает его немного безопаснее.
Что касается вашего вопроса о том, как подписаться на более чем одну коллекцию, я бы рекомендовал использовать выделенную модель представления для каждого семантически значимого представления.