【问题标题】:My completion handler always returns an empty array, why?我的完成处理程序总是返回一个空数组,为什么?
【发布时间】:2018-10-10 14:38:42
【问题描述】:

我有以下函数从 Firebase 获取数据,填充 compliments 数组,并假设通过完成 handler 将数组传回。

即使添加了对象,返回的数组也总是空的?

我是否将完成处理程序代码放置在错误的位置,我尝试在所有花括号内插入,但没有任何效果。

handler(compliments, true) 执行行的断点输出如下:

0x0000000102bb3fe8 vipeeps`partial apply forwarder for closure #1 (Swift.Array<Any>, Swift.Bool) -> () in vipeeps.ConversationsListVC.loadNewInvitesData() -> () at ConversationsListVC.swift

功能:

    func getComplimentsReceived(forUserId forId: String, handler: @escaping (_ complimentPack: [[String : Any]] ,_ success: Bool) -> ()){

    var compliments = [[String : Any]]()//empty array to hold compliments

    REF_USERS_COMPLIMENTS.child(forId).observe(.value) { (snapshot) in


       for item in snapshot.children{


        let itemSnap = item as! DataSnapshot


        let dict = itemSnap.value as! [String : Bool]


        for (key, status) in dict {

            switch status {

            case true:
                //print(status)

                self.REF_USERS.child(snapshot.key).observeSingleEvent(of: .value, with: { (snapshot) in

                    let uid = snapshot.key
                    let name = snapshot.childSnapshot(forPath: "name").value as! String
                    let email = snapshot.childSnapshot(forPath: "email").value as! String
                    let profilePictureURL = snapshot.childSnapshot(forPath: "profilePictureURL").value as! String

                    let birthday = snapshot.childSnapshot(forPath: "birthday").value as! String
                    let firstName = snapshot.childSnapshot(forPath: "firstName").value as! String
                    let lastName = snapshot.childSnapshot(forPath: "lastName").value as! String
                    let gender = snapshot.childSnapshot(forPath: "gender").value as! String
                    let discoverable = snapshot.childSnapshot(forPath: "discoverable").value as! Bool
                    let online = snapshot.childSnapshot(forPath: "online").value as! Bool

                    let discoveryPrefs = snapshot.childSnapshot(forPath: "discoveryPrefs").value as! [String : Any]

                    let dictionary: [String : Any] = ["uid": uid, "name": name, "email": email, "profilePictureURL": profilePictureURL, "birthday": birthday, "firstName": firstName, "lastName": lastName, "gender": gender, "discoverable": discoverable, "online": online, "discoveryPrefs": discoveryPrefs]

                    let user = User(uid: uid, dictionary: dictionary)

                    let complimentPack = [
                        "type": "compliment",
                        "complimentId": key,
                        "active": status,
                        "fromUser": user
                        ] as [String : Any]

                    compliments.append(complimentPack)

                    print("compliments.count: \(compliments.count)")


                })//end observer

            case false:
                print(status)

                break

            }//end switch

         }//end for dict

       }//end for snapshot.item

      handler(compliments, true)

    }//end observe

 }//end func

数据结构:

【问题讨论】:

  • 这段代码无论如何都不会按预期工作,因为您在调用内部(异步)闭包之前调用了最后的handler(..) 语句。顺便说一句:为什么您将compliments 和数组参数声明为未指定的[Any],尽管您清楚地知道这是更指定的[[String:Any]]? Swift 3+ 的闭包语法是 @escaping ([Any] , Bool) -&gt; () 没有下划线和参数标签。
  • 从 'end observe' 范围内调用处理程序,而不是在 'end for snapshot.item' 范围内并尝试!
  • 谢谢@vadian 我已经做出了你建议的改变。
  • 谢谢@vivekDas 试过了,但还是没有运气:(我用最新的代码编辑了我的问题。
  • observeSingleEvent 异步工作。循环立即执行,handler(..) 在第一个闭包通过其snapshot 之前被调用很长时间(就计算机速度而言)。

标签: swift firebase


【解决方案1】:

您可能想要使用调度组,一旦您的所有异步 observeSingelEvent 调用完成,就会收到通知:

  • 在您的observe 代码中,您创建一个新的DispatchGroup
  • 在调用异步 observeSingleEvent 之前,您需要 enter 组。
  • 在 asyc 关闭结束时,您 leave 群组
  • 一旦所有entersleave 调用匹配,notify 将被调用,然后调用handler

看这里:

func getComplimentsReceived(forUserId forId: String, handler: @escaping (_ complimentPack: [Any] ,_ success: Bool) -> ()){
    REF_USERS_COMPLIMENTS.child(forId).observe(.value) { (snapshot) in
        var compliments = [Any]()//empty array to hold compliments
        let dispatchGroup = DispatchGroup()
        for item in snapshot.children {

            for (key, status) in dict {

                switch status {
                    case true:
                        dispatchGroup.enter()
                        self.REF_USERS.child(snapshot.key).observeSingleEvent(of: .value, with: { (snapshot) in
                            // ...
                            compliments.append(complimentPack)
                            dispatchGroup.leave()
                    })//end observer
                    case false:
                        print(status)
                        break

                }//end switch

            }//end for dict

        } //end for snapshot.item

        dispatchGroup.notify(queue: .main) {
            handler(compliments, true)
        }
    } //end observe

}//end func

添加编辑功能(@Roggie):

func loadNewInvitesData(){


    guard let uid = Auth.auth().currentUser?.uid else { return }

    DataService.run.getInvitesAndCompliments(forUserId: uid) { (array, sucess) in


            for item in array {
                let user = item ["fromUser"] as! User
                let uid = user.uid
                print("user id: \(uid)")
                DataService.run.observeUser(forUserId: uid, handler: { (user) in
                    self.users.append(user)
                })
            }
        self.invites = array
        self.collectionView.reloadData()

    }

}//end func

【讨论】:

  • 感谢您的建议,我会试试这个并报告。
  • 是的 - 这很有效 - 非常感谢您对您和 @vadian 的帮助
  • 我知道这可以追溯到一点点,但我已经要求 self.REF_USERS.child(snapshot.key).observe 而不是 observeSingleEvent 但由于它在 Firebase 上发生更改时再次执行关闭,所以会发生崩溃因为我相信enterleave 已关闭。如果我删除所有dispatchGroup 代码,则不会读取所有节点。有什么建议我可以做什么?
  • 如果你想使用observe 而不是 observeSingleEvent,你将不得不重新考虑你的程序逻辑。正如您已经发现的那样,只要发生修改,就会调用第一个。因此,您只能尝试在此处更新 UI(添加/删除/删除),而不能尝试返回“所有”对象的数组。也许您可以在现有的observeSingleEvent 之外添加observe,然后在其中进行更新(例如,不涉及任何dispatchGroup)。
  • 感谢反馈,上面的函数返回一个字典数组compliments,其中包括获取的用户uid,然后我尝试循环遍历uid并将observe用户添加到一个单独的 users 数组用于 UI 显示,但我发现在重新加载 CollectionView 时出现 index out of bound 错误...请参阅上面的其他编辑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-21
  • 2014-05-25
  • 1970-01-01
  • 2017-07-25
  • 2022-01-09
  • 1970-01-01
相关资源
最近更新 更多