init() 中的 Swift Array.append 不能与 Firestore (Google Cloud/FIrebase) 结合使用

Posted

技术标签:

【中文标题】init() 中的 Swift Array.append 不能与 Firestore (Google Cloud/FIrebase) 结合使用【英文标题】:Swift Array.append in init() not working in combination with Firestore (Google Cloud/FIrebase) 【发布时间】:2020-04-24 18:13:57 【问题描述】:

我已经尝试了我的第一个 SwiftUI 项目。 我只想显示存储在 Firestore (Google Firebase) 中的一些数据。 这是我的代码:

import SwiftUI
import FirebaseFirestore
import FirebaseFirestoreSwift

struct MonsterObj: Identifiable, Equatable, Hashable 
    var id = UUID()
    var name: String
    var element: String
    var immune: String
    var size: String
    var twoStarWeakness: String
    var threeStarWeakness: String

    #if DEBUG
    static let exampleMonster = MonsterObj(id: UUID(), name: "Test Monster", element: "Test Element", immun: "Test immun", groesse: "Test groesse", twoStarWeakness: "Test 2 Weakness", threeStarWeakness: "Test3 Weakness")

    #endif



class MonsterC: ObservableObject 

    @Published var monsters = [MonsterObj]()

    init() 
        let db = Firestore.firestore()
        var monsterNames: [String] = []

        db.collection("Monster").getDocuments()  (querySnapshot, err) in
            if let err = err 
                print(err)
             else 
                for document in querySnapshot!.documents 
                    monsterNames.append("\(document.documentID)")
                    print("document: \(document.documentID)")

                
            
        

            for monsterName in monsterNames 
                print(monsterName)
                db.collection("Monster").document(monsterName).getDocument  (document, error) in
                    if let document = document, document.exists 
                        let elementGetter = document.get("element") as! String
                        let immuneGetter = document.get("immune") as! String
                        let sizeGetter = document.get("size") as! String
                        let twoStarWeaknessGetter = document.get("2 star weakness") as! String
                        let threeStarWeaknessGetter = document.get("3 star weakness")as! String

                        self.monsters.append(MonsterObj(name: monsterName, element: elementGetter, immune: immuneGetter, size: sizeGetter, twoStarWeakness: twoStarWeaknessGetter, threeStarWeakness: threeStarWeaknessGetter))

                        
                
            
     

这是我的观点:

import SwiftUI  

struct ContentView: View   
    @EnvironmentObject var monsterT: MonsterC  

    var body: some View   
        List(monsterT.monsters, id: \.self)  monster in  
            Text(monster.name)  
          
      
 

我对 SceneDelegate.swift 做了以下操作:

var window: UIWindow?  
var monsterT = MonsterC()  
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions 


    // Create the SwiftUI view that provides the window contents.  
    let contentView = ContentView().environmentObject(monsterT)  


    if let windowScene = scene as? UIWindowScene   
        let window = UIWindow(windowScene: windowScene)  
        window.rootViewController = UIHostingController(rootView: contentView)  
        self.window = window  
        window.makeKeyAndVisible()  
      

所以我的问题是列表是空的。 我在 MonsterC 类的 init 中发现 monsterNames.append("\document.documentID)") 行不会将任何内容附加到 MonsterNames。 但是print("document: \(document.documentID)") 正在打印所有怪物名称。

我的 google Firestore 结构如下所示:

    Collection -> Document -> Fields
-------------------------------------------
    Monster -> Anjanath -> immune: fire,
                           element: fire

等等。 只有一个集合(“Monster”)。

谁能向初学者解释为什么 .append 在这里不起作用,但 print 一切正常?

【问题讨论】:

如果您打印 print("After monstersNames.append: \monsterNames)") 会不会是因为您缺少异步概念? 【参考方案1】:

您需要了解调用异步函数。我的建议是重构你的代码,在你的 init() 中进行这些异步调用不是一个好主意。

你的函数 "db.collection("Monster").getDocuments() (querySnapshot, err) in ..." 是异步的。您必须等到它完成才能使用结果,或者在函数内执行您需要的操作。请注意,您还有另一个异步函数 "db.collection("Monster").document(monsterName).getDocument "

所以 .append 不起作用,因为执行 .append 时函数“db.collection("Monster").getDocuments() (querySnapshot, err) in ...”的结果不可用。

所以如果你必须使用这个狡猾的代码,试试这个来解决你的数组问题:

class MonsterC: ObservableObject 

@Published var monsters = [MonsterObj]()

init() 
    let db = Firestore.firestore()

    db.collection("Monster").getDocuments()  (querySnapshot, err) in
        if let err = err 
            print(err)
         else 
            var monsterNames: [String] = []
            for document in querySnapshot!.documents 
                monsterNames.append("\(document.documentID)")
                print("document: \(document.documentID)")

            

            for monsterName in monsterNames 
                print(monsterName)
                db.collection("Monster").document(monsterName).getDocument  (document, error) in
                    if let document = document, document.exists 
                        let elementGetter = document.get("element") as! String
                        let immuneGetter = document.get("immune") as! String
                        let sizeGetter = document.get("size") as! String
                        let twoStarWeaknessGetter = document.get("2 star weakness") as! String
                        let threeStarWeaknessGetter = document.get("3 star weakness")as! String

                        self.monsters.append(MonsterObj(name: monsterName, element: elementGetter, immune: immuneGetter, size: sizeGetter, twoStarWeakness: twoStarWeaknessGetter, threeStarWeakness: threeStarWeaknessGetter))

                    
                
            

        
    


【讨论】:

感谢有关异步函数的信息。您称其为“狡猾的代码”,所以我想这样做很糟糕。你能告诉我如何做得更好吗? 很抱歉称代码不可靠。好问题,如何使代码更好。但是,在不了解您要达到的目标的情况下,这超出了我的范围。对我来说 init() 是用于创建/准备类和初始化任何存储的属性。要更新/填充属性,我倾向于在 init() 之外进行,尤其是在涉及异步函数的情况下。类似于带有回调的静态函数 createMonsterClass(...) 以防获取错误。当您在 init() 中执行所有这些操作时,您永远不知道是否存在错误,或者当您想使用它时对象是否已准备好。 由于您已经导入了FirebaseFirestoreSwift,您可以使用queryDocumentSnapshot.data(as: MonsterObj.self) 使映射更加轻松

以上是关于init() 中的 Swift Array.append 不能与 Firestore (Google Cloud/FIrebase) 结合使用的主要内容,如果未能解决你的问题,请参考以下文章

Swift基础-init详解

什么是 Swift 中的部分申请闭包#1

json.swift 错误是 Self.init 在委托初始化程序中的所有路径上都没有调用

init() 中的 Swift Array.append 不能与 Firestore (Google Cloud/FIrebase) 结合使用

Swift 中的多个初始化器

Swift中的多个初始值设定项