【问题标题】:Array is updating before variables are updated in Swift在 Swift 中更新变量之前,数组正在更新
【发布时间】:2019-10-31 20:32:41
【问题描述】:

我正在尝试从 Firestore 获取玩具列表并将其放入数组中 但是当我调用函数时,它返回空数组,返回后打印玩具对象,所以顺序被破坏了。

我认为闭包会对我有所帮助,但我认为我不知道如何使用它们,而且来自 Google 的示例对我没有帮助

这是我的代码(我使用 SwiftUI,所以我创建了带有变量的 swift 文件)

let db = Firestore.firestore()
class DataLoade {
    func loadFirebase(completionHandler: @escaping (_ toys: [Toy]) -> ()){
        var toysar: [Toy] = []
        let toysRef = db.collection("Toys")
        toysRef.getDocuments() { (querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                for document in querySnapshot!.documents {
                    var name: String = document.get("name") as! String
                    var id: Int = document.get("id") as! Int
                    var description: String = document.get("description") as! String
                    var imageName: String = document.get("imageName") as! String
                    var price: String = document.get("price") as! String
                    var category: String = document.get("category") as! String
                    var timeToy = Toy(id: id, name: name, imageName: imageName, category: category, description: description, price: price)
                    toysar.append(timeToy)


                }




            }
        }

        completionHandler(toysar)
    //    print(toysar)

    }




}


就是这样打印出来的:

[] // it prints empty array, but it  is in the end of the code
Toy(id: 1001, name: "Pikachu", imageName: "pikachu-plush", category: "lol", description: "kek", price: "350₽") // and now it prints Toy object, however it is in the start of the code 

好的,所以我尝试为我的函数创建完成处理程序,就像在“重复”答案中一样,但这不起作用:数组在完成处理程序工作之前返回

ContentView.swift  
func updateArray() -> [Toy]{
    dl.loadFirebase() { toys in
            ll = toys

            }
    print("lol \(datas)") // prints «lol []»
    return ll
}

【问题讨论】:

    标签: ios arrays swift google-cloud-firestore swiftui


    【解决方案1】:

    您可以使用DispatchGroup 等待异步任务。但诀窍是不要将异步任务与return 语句相关联。而是在任务完成后使用closures 执行操作。 免责声明:我在 SO 上写了这篇文章,对于语法问题,我提前道歉。

    let toyData = loadFirebase( { (toys) in
        print(toys)
        //Do something with toys when done
        //You could add another completionHandler incase it fails. 
        //So 1 for pass and 1 for fail and maybe another for cancel. W/e u want
    } )
    let db = Firestore.firestore()
    
    func loadFirebase(completionHandler:@escaping ((toys: [Toy]?) -> Void)) {
        //Create Group
        let downloadGroup = DispatchGroup()
        var toysar: [Toy] = []
        let toysRef = db.collection("Toys")
        //If you had multiple items and wanted to wait for each, just do an enter on each.
        downloadGroup.enter()
        toysRef.getDocuments() { (querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                for document in querySnapshot!.documents {
                    var name: String = document.get("name") as! String
                    var id: Int = document.get("id") as! Int
                    var description: String = document.get("description") as! String
                    var imageName: String = document.get("imageName") as! String
                    var price: String = document.get("price") as! String
                    var category: String = document.get("category") as! String
                    var timeToy = Toy(id: id, name: name, imageName: imageName, category: category, description: description, price: price)
                    toysar.append(timeToy)
                    print(timeToy)
                }
            //We aren't done until AFTER the for loop, i.e., each item is grabbed.
            downloadGroup.leave()
            }
        }
        //Once the queue is empty, we notify the queue we are done
        downloadGroup.notify(queue: DispatchQueue.main) {
            completionHandler(toys)
        }
    }
    
    import SwiftUI
    var dl = DataLoade()
    var ll: [Toy] = []
    
    let semaphore = DispatchSemaphore(value: 1)
    struct ContentView: View {
        var items: [Toy]
        var body: some View {
    
            NavigationView{
            ScrollView(){
                VStack(alignment: .leading){
            ToyRow(category: "Наш выбор", toys: items)
    
                Spacer()
                ToyRow(category: "Акции", toys: items)
    
    
                }
                }.navigationBarTitle(Text("Игрушки г.Остров"))}
    
    
        }
    }
    func upe(completionHandler:@escaping ((toys: [Toy]?){
        dl.loadFirebase(completionHandler: { toy in
            ll.append(contentsOf: toy!)
            completionHandler(ll)
        } )
    }
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            upe(completionHandler: { (toys) in 
                DispatchQueue.main.async {
                    ContentView(items: toys)
                }
            })
        }
    }
    

    【讨论】:

    • 嗯,这不起作用所以在 ContentView 中我创建了新的函数 func 并在显示视图 ContentView(items: upe()) 时调用它,但这不起作用!它一直保持空数组我已经完成了你写的所有事情(复制粘贴)如果你愿意,你可以看到我的整个代码Data.swiftContentView.swift
    • 您现在在 upe() 方法中遇到了同样的问题。再次进行异步调用时不能return ll。和以前一样的问题
    • 本质上,你需要做同样的过程。创建一个完成处理程序作为 upe 的输入,并在完成获取数据后调用完成处理程序。然后,当upe 完成时,这是任意的,因为它是一个异步调用,您可以将该项目添加到 contentview。但是return 是即时的,它不会等待异步调用。因此,return ll 发生在您添加玩具之前。
    • 好的,所以现在我的 SceneDeleagate 有错误,但我会将您的答案标记为正确。如果你想帮助我code电报:@alx_io
    猜你喜欢
    • 2020-02-15
    • 1970-01-01
    • 2016-01-28
    • 2010-10-27
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 2021-04-03
    • 2018-10-20
    相关资源
    最近更新 更多