斯威夫特,如何让完成处理程序按我的意愿工作?

Posted

技术标签:

【中文标题】斯威夫特,如何让完成处理程序按我的意愿工作?【英文标题】:Swift, How to Get Completion Handler working as I Wish? 【发布时间】:2019-04-10 18:00:32 【问题描述】:

我正在从 Firebase 获取用户信息,我的目标是获取一批用户,然后在卡片堆栈中一次一个地显示它们。为此,我使用完成处理程序。但是,我在完成处理程序中的代码在所有用户的获取完成之前运行。

感谢您的帮助。

这是我的代码。我希望在“fetchAllUsers”完成后运行“fetchOneUser()”:

fetchAllUsers(completion:  message in
   print(message)
   print("FetchOneUser")
   self.fetchOneUser()
)

这里是 fetchAllUser 函数:

func fetchAllUsers(completion: @escaping (_ message: String) -> Void)
        //User or advertiser?
        Database.database().reference(withPath: "Advertiser").child(uid).observeSingleEvent(of: .value, with:  (snapshot) in

            if snapshot.exists()
                myAdvertiserVar.advertiser = true
                self.currentUserKind = "Advertiser"
                self.otherUserKind = "Users"
            
            else
                self.currentUserKind = "Users"
                self.otherUserKind = "Advertiser"
            

            // Fetch
            let query = self.ref?.child(self.otherUserKind).queryOrdered(byChild: "email")
            query?.observeSingleEvent(of: .value) 
                (snapshot) in
                for child in snapshot.children.allObjects as! [DataSnapshot] 
                    let id = child.key
                    //If Already Accepted, don't fetch
                    Database.database().reference(withPath: self.currentUserKind).child(self.uid).child("Accepted").child(id).observeSingleEvent(of: .value, with: (accepted) in
                        if accepted.exists()
                            print("\(id) är redan Accepted")
                        
                        else
                            if myAdvertiserVar.advertiser == true
                                let value = child.value as? NSDictionary
                                let username = value?["Username"] as? String
                                let occupation = value?["Occupation"] as? String
                                let age = value?["Age"] as? String
                                let bio = value?["Bio"] as? String
                                let email = value?["email"] as? String
                                let user = User(id: id, username: username, occupation: occupation, age: age, bio: bio, email: email)
                                self.usersArray.append(user)
                            
                            else
                                let value = child.value as? NSDictionary
                                let username = value?["Owner"] as? String
                                let occupation = value?["Location"] as? String
                                let age = value?["Rent"] as? String
                                let bio = value?["About"] as? String
                                let email = value?["email"] as? String
                                let user = User(id: id, username: username, occupation: occupation, age: age, bio: bio, email: email)
                                self.usersArray.append(user)
                            
                        
                    )
                
                print(self.usersArray.count)
                completion("Users list fetched")
            
        )
    

【问题讨论】:

尝试从 for 循环中创建数据库引用,Database.database().reference 将异步连接到实时数据库,当 for 循环完成时返回结果,如果这有帮助,请通知我,我无法测试,因为Firebase 限制伊朗语 【参考方案1】:

你需要使用DispatchGroup,因为内部调用是异步的

func fetchAllUsers(completion: @escaping (_ message: String) -> Void)
    //User or advertiser?
    Database.database().reference(withPath: "Advertiser").child(uid).observeSingleEvent(of: .value, with:  (snapshot) in

        if snapshot.exists()
            myAdvertiserVar.advertiser = true
            self.currentUserKind = "Advertiser"
            self.otherUserKind = "Users"
        
        else
            self.currentUserKind = "Users"
            self.otherUserKind = "Advertiser"
        

        // Fetch
        let query = self.ref?.child(self.otherUserKind).queryOrdered(byChild: "email")
        query?.observeSingleEvent(of: .value) 
            (snapshot) in

            let g = DispatchGroup()

            for child in snapshot.children.allObjects as! [DataSnapshot] 
                let id = child.key
                //If Already Accepted, don't fetch

                g.enter()

                Database.database().reference(withPath: self.currentUserKind).child(self.uid).child("Accepted").child(id).observeSingleEvent(of: .value, with: (accepted) in
                    if accepted.exists()
                        print("\(id) är redan Accepted")
                    
                    else
                        if myAdvertiserVar.advertiser == true
                            let value = child.value as? NSDictionary
                            let username = value?["Username"] as? String
                            let occupation = value?["Occupation"] as? String
                            let age = value?["Age"] as? String
                            let bio = value?["Bio"] as? String
                            let email = value?["email"] as? String
                            let user = User(id: id, username: username, occupation: occupation, age: age, bio: bio, email: email)
                            self.usersArray.append(user)
                        
                        else
                            let value = child.value as? NSDictionary
                            let username = value?["Owner"] as? String
                            let occupation = value?["Location"] as? String
                            let age = value?["Rent"] as? String
                            let bio = value?["About"] as? String
                            let email = value?["email"] as? String
                            let user = User(id: id, username: username, occupation: occupation, age: age, bio: bio, email: email)
                            self.usersArray.append(user)
                        
                    

                    g.leave()
                )
            


            g.notify(queue: .main, execute: 

                print(self.usersArray.count)
                completion("Users list fetched")

            )
        
    )

【讨论】:

【参考方案2】:

基于Firebase documentation:

Firebase 使用 refrence() 方法为您的实时数据库异步的根获取数据库引用。

这意味着获取结果比 for 循环花费更多时间,在这种情况下,您的 for 循环完成并完成块调用并将您带出方法,然后您的请求结果将返回。

你的代码应该是这样的

var firebaseDatabaseRefrence: DatabaseReference!

override func viewDidLoad() 
   super.viewDidLoad()
   Database.database().reference(withPath: self.currentUserKind)


func someMethod() 

  self.firebaseDatabaseRefrence
  .child(self.uid)
  .child("Accepted")
  .child(id).observeSingleEvent(of: .value, with: (accepted) in 


【讨论】:

以上是关于斯威夫特,如何让完成处理程序按我的意愿工作?的主要内容,如果未能解决你的问题,请参考以下文章

presentViewController 没有按我的意愿工作

无法按我的意愿设计 uicollectionview

尝试使用加入、计数和分组,但没有按我的意愿工作

未在实例上定义,但在渲染期间引用,但可以按我的意愿工作

让别人的程序按自己的意愿运行

更改页面时出现 PdfBox 问题