Ios 检索和更新存储在Firestore中的数据

Ios 检索和更新存储在Firestore中的数据,ios,swift,firebase,google-cloud-firestore,swiftui,Ios,Swift,Firebase,Google Cloud Firestore,Swiftui,我正在iOS上开发一个移动应用程序,需要在用户单击按钮时跟踪一些数据。但是,当我尝试根据数据获取数据时,没有显示任何内容。以下是我的片段: import SwiftUI import FirebaseStorage import FirebaseCore import FirebaseFirestore struct Station_View: View { @State private var showingAlert = false var ref: Firestore!

我正在iOS上开发一个移动应用程序,需要在用户单击按钮时跟踪一些数据。但是,当我尝试根据数据获取数据时,没有显示任何内容。以下是我的片段:

import SwiftUI
import FirebaseStorage
import FirebaseCore
import FirebaseFirestore

struct Station_View: View {
    @State private var showingAlert = false
    var ref: Firestore!
    
    var station_ : station
    var food : [food] = []
    var body: some View {
        VStack(alignment: .leading, spacing: 10, content: {
            VStack {
                ForEach(station_.menu_items, id: \.self) { i in
                    Divider()
                    .frame(width: 400, height: 1)
                    .background(Color("Black"))
                    .padding(.vertical,0)
                    HStack {
                        VStack (alignment: .leading) {
                            Text(i.name + ", " + i.calories + "cal, protein: " + i.protein)
                            .font(.headline)
                            .foregroundColor(Color("Black"))
                                
                        }.padding(.leading, 8)
                        Spacer()
                        if (Int(i.protein)! > 10) {
                            Button(action: {
//                                print("Button action")
////////// I retrieved data here //////////////////                      
                                let docRef = ref?.collection("users").document("7lqIqxc7SGPrbRhhQWZ0rdNuKnb2")
                                docRef?.getDocument { (document, error) in
                                    if let document = document, document.exists {
                                        let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
                                        print("Document data: \(dataDescription)")
                                    } else {
                                        print("Document does not exist")
                                    }
                                }
////////// I retrieved data here //////////////////  
                                self.showingAlert = true
                            }) {
                                HStack {
                                    Image(systemName: "p.circle")
                                    Text("+50xp")
                                }.padding(10.0)
                                .overlay(
                                    RoundedRectangle(cornerRadius: 6.0)
                                        .stroke(lineWidth: 2.0)
                                )
                            }
                            .alert(isPresented: $showingAlert) {
                            () -> Alert in
                            Alert(title: Text("Congratulations!"), message: Text("You had a protein meal, XP+50!"), dismissButton: .default(Text("OK")))
                                }
                        }
                        if (i.is_vegan) {
                            Button(action: {
//                                print("Button action")
////////// I retrieved data here //////////////////  
                                let docRef = ref?.collection("users").document("7lqIqxc7SGPrbRhhQWZ0rdNuKnb2")
                                docRef?.getDocument { (document, error) in
                                    if let document = document, document.exists {
                                        let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
                                        print("Document data: \(dataDescription)")
                                    } else {
                                        print("Document does not exist")
                                    }
                                }
////////// I retrieved data here //////////////////  
                                self.showingAlert = true
                            }) {
                                HStack {
                                    Image(systemName: "leaf")
                                    Text("+50xp")
                                }.padding(10.0)
                                .overlay(
                                    RoundedRectangle(cornerRadius: 6.0)
                                        .stroke(lineWidth: 2.0)
                                )
                            }
                            .alert(isPresented: $showingAlert) {
                                () -> Alert in
                                Alert(title: Text("Congratulations!"), message: Text("You had a vegan meal, XP+50!"), dismissButton: .default(Text("OK")))
                                    }
                        }
                            
                       
                    }
                    .padding(.init(top: 12, leading: 0, bottom: 12, trailing: 0))
                    
                    
                    
                }
            }
        } )
    }
}

我能做些什么使它成为现实?我希望在收集数据时只更新一个键值对,而其他键值对保持不变。

首先,使用SwiftUI时,应始终使用ViewModel。起初这是一个奇怪的转换,但它将使您的代码更容易理解和跟踪。这是基本结构

视图模型 请注意,这里发生了一些事情,
observateObject
@Published
本质上这意味着可以使用已发布的属性观察对象
YourViewModel
,或者可以绑定到视图的属性。要在视图中使用它,您可以这样做

看法 这是MVVM模式的基本结构,将在视图中节省大量空间,使其更易于阅读和维护。通常,您将为
视图
视图模型
创建一个单独的
.swift
文件,尽量不要将它们组合在一起,并尽可能地进行抽象

要回答最终的问题,您如何从Firebase检索数据并更新相同的数据?好的,答案如下,我将演示如何在
ViewModel
中使用函数和属性,您可以
绑定到视图以更新它们

获取Firebase数据 更新Firebase数据 这是更新firebase数据的简单方法。这是通过传递一个字典来处理的,字典中有一个键,该键是字段和一个与其关联的值。警告:不要使用
setData(…)
它将清除您在其中的所有其他内容
setData(…)
对于首次创建数据非常有用,例如注册帐户、创建新条目等

func updateFirebaseData(firstName: String) {
    if let user = Auth.auth().currentUser {
         let db = Firestore.firestore()   
         db.collection("users").document(user.uid).updateData(["first_name": firstName])
        }
    }
用法
请注意,在SwiftUI中使用MVVM结构时,它是多么干净,一旦您使用几天,它将成为第二天性。

有很多事情在进行,我通常不使用Firebase,但将Firebase代码移动到ViewModel中是一个好的开始。我看到了几个执行asyc任务的闭包,从某种角度看,它们肯定会有问题。作为一种标准做法,视图从不做任何工作,它只用于用户交互,所有工作都是在后台完成的。谢谢您的建议!我只是一个快速发展的新手,这应该是一个有价值的改变。非常感谢!您关于模块更改的建议非常有用!顺便说一句,我可以问一下:
let db=Firestore.Firestore()的声明与
var-ref:Firestore有什么区别?因此将属性声明为
let
是一个常量值,这意味着它已设置且不会更改<代码>变量将更改。创建类似
var-ref:Firestore的东西基本上是创建一个属性,您可以保证在需要时该属性将存在。本质上,它为
Firestore
类型的变量创建内存分配。它是在某个地方设置的,否则您将导致
nil
值崩溃。这就是<代码>
是for,它告诉编译器允许将名为
ref
的变量展开,希望能够安全地处理。通常,如果你是新来的,我会避免使用任何
var someString:String是一个零值。如果我试图
打印(someString)
它将崩溃。除非我先设定那个值
var someString=“Hello World”
和I
print(someString)
它不会崩溃。这非常有用,只要您想下载数据,就可以在继续之前检查它是否为零。它有助于打字安全。
struct YourView: View {
    //This is your ViewModel reference.
    //Use is to bind all your details to the view with
    //something like yourViewModel.firstName or $yourViewModel.firstName
    @observedObject var yourViewModel = YourViewModel()
    var body: some View {
        Button("Change Bool") {
             yourViewModel.isTrue.toggle()
             yourViewModel.isValueTrue()
        }
    }
}
//These properties are a part of the VIEWMODEL and can be bound to the view
//Using yourViewModel.someProperty
@Published var firstName = ""
@Published var lastName = ""
@Published var email = ""

func fetchFirebaseData() {
            guard let uid = Auth.auth().currentUser?.uid else {
            print("Handle Error")
            return
        }

        //Create Database Reference
        let db = Firestore.firestore()

        //Reference the collection and document. In this example
        //I'm accessing users/someUserId
        let fsUserProfile = db.collection("users").document(uid)
        
        //Request the document 
        fsUserProfile.getDocument { (snapshot, err) in
            if err != nil { return }
            self.fetchImageFromURL(url: URL(string: snapshot?.get("profile_image_url") as? String ?? "")!)
            self.firstName = snapshot?.get("first_name") as? String ?? ""
            self.lastName = snapshot?.get("last_name") as? String ?? ""
            self.email = snapshot?.get("email") as? String ?? ""
        }
}
func updateFirebaseData(firstName: String) {
    if let user = Auth.auth().currentUser {
         let db = Firestore.firestore()   
         db.collection("users").document(user.uid).updateData(["first_name": firstName])
        }
    }
struct YourView: View {
    @observedObject var yourViewModel = YourViewModel()
    
    var body: some View {
        
        VStack {
            
            //Fetching Example
            VStack {
                Button("Fetch Data") {
                    yourViewModel.fetchFirebaseData()
                }
                Text(yourViewModel.firstName)
            }
            
            //Setting Example
            VStack {
                Button("Update Data") {
                    //You could have this "John" value, a property
                    //of your ViewModel as well, or a text input, or whatever
                    //you want.
                    yourViewModel.updateFirebaseData(firstName: "John")
                }
            }
        }
    }
}