观察调用不更新字典值。异步的问题

Posted

技术标签:

【中文标题】观察调用不更新字典值。异步的问题【英文标题】:Observe call not updating dictionary value. Problem with async 【发布时间】:2019-12-14 18:04:20 【问题描述】:

我正在尝试从我的 firebase 数据库中获取图像列表。在观察方法中,如果我打印帖子的数量,它可以正常工作。如果我在观察函数之外打印帖子数,但仍在 fetchPosts() 函数内,我得到 0。如果我在 fetchPosts() 调用(使用观察的函数)之后打印帖子数,我得到 0。

如何在此异步调用中将值保存到我的字典帖子中?我已经尝试过完成和调度组。我可能没有正确实现它们,所以如果你看到一个简单的方法,那么请帮助我。代码如下:

import UIKit
import SwiftUI
import Firebase
import FirebaseUI
import SwiftKeychainWrapper

class FeedViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource


    @IBOutlet weak var collectionview: UICollectionView!

   //var posts = [Post]()


    var posts1 = [String]()
    didSet
    collectionview.reloadData()
    
    
    var following = [String]()
    //var posts1 = [String]()
    var userStorage: StorageReference!
    var ref : DatabaseReference!

    override func viewDidLoad() 
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        fetchPosts()


    

//    func lengthyTask(completionHandler: (Int) -> Int)
//    
//        let result = completionHandler(42)
//        print(result)
//    
//
//    lengthyTask(completionHandler:  number in
//        print(number)
//        return 101
//    )
//
    func  fetchPosts() 
        let uid = Auth.auth().currentUser!.uid
        let ref = Database.database().reference().child("posts")
        let uids = Database.database().reference().child("users")
        uids.observe(DataEventType.value, with:  (snapshot) in
            let dict = snapshot.value as! [String:NSDictionary]
            for (_,value) in dict 
                if let uid = value["uid"] as? String
                    self.following.append(uid)
                
            
        ref.observe(DataEventType.value, with:  (snapshot2) in
            let dict2 = snapshot2.value as! [String:NSDictionary]
            for(key, value) in dict
                for uid2 in self.following
                    if (uid2 == key)
                        for (key2,value2) in value as! [String:String]
                            //print(key2 + "this is key2")
                            if(key2 == "urlToImage")
                                let urlimage = value2
                                //print(urlimage)
                                self.posts1.append(urlimage)
                                self.collectionview.reloadData()
                                print(self.posts1.count)

                            
                        

                    
                
            
            )
            self.collectionview.reloadData()
        )
    //ref.removeAllObservers()
    //uids.removeAllObservers()
    print("before return")
    print(self.posts1.count)


    //return self.posts1
    
    func numberOfSections(in collectionView: UICollectionView) ->Int 
        return 1
    

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        return posts1.count
    

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PostCell", for: indexPath) as! PostCell
        cell.postImage.sd_setImage(with: URL(string: posts1[indexPath.row]))
        //creating the cell
        //cell.postImage.downloadImage(from: self.posts[indexPath.row])
//        let storageRef = Storage.storage().reference(forURL: self.posts[indexPath.row].pathToImage)
//
//
        print("im trying")

        //let stickitinme = URL(fileURLWithPath: posts1[0])
        //cell.postImage.sd_setImage(with: stickitinme)

        //cell.authorLabel.text = self.posts[indexPath.row].author
        //cell.likeLabel.text = "\(self.posts[indexPath.row].likes) Likes"

        return cell
    


    @IBAction func signOutPressed(_sender: Any)
           signOut()
           self.performSegue(withIdentifier: "toSignIn", sender: nil)
       

    @objc func signOut()
           KeychainWrapper.standard.removeObject(forKey:"uid")

           do
               try Auth.auth().signOut()
            catch let signOutError as NSError
               print("Error signing out: %@", signOutError)
           
           dismiss(animated: true, completion: nil)
       


    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) 
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
    
    */


【问题讨论】:

删除闭包外的(无意义的)print 行,并在闭包内做你必须做的事情。 我真的不知道如何使用闭包。我只是用谷歌搜索了它们,然后就出现了。我知道完成处理程序,但实际上没有人告诉你它是如何工作的。我只是想加载一个集合视图,但我仍然不知道一旦我填充了“帖子”字典,这是否会起作用。你能帮我解决一下闭包语法吗? 【参考方案1】:

您只需要稍作改动

    简单地声明posts1

    var posts1 = [String]()
    

    并删除属性观察者didSet

    self.posts1.append(..之后删除self.collectionview.reloadData()这一行

    将最后出现的self.collectionview.reloadData() 上移一级,将其包装在DispatchQueue 块中以更新主线程上的集合视图并删除外部闭包后的print

               
               DispatchQueue.main.async 
                   self.collectionview.reloadData()
               
            )
        )
    
    

    而且第二个闭包中有一个错字。应该是

    let dict2 = snapshot2.value as! [String:NSDictionary]
    for(key, value) in dict2 
    

    带有尾随索引的变量名很容易出错,最好是 userDictpostDict

编辑

这是执行顺序的代码

override func viewDidLoad() 
    super.viewDidLoad()
    collectionview.dataSource = self
    collectionview.delegate = self
    // 1
    fetchPosts()
    // 5 


func fetchPosts() 
    // 2       
    let uid = Auth.auth().currentUser!.uid
    let ref = Database.database().reference().child("posts")
    let uids = Database.database().reference().child("users")
    // 3
    uids.observe(DataEventType.value, with:  (snapshot) in
        // 6
        let dict = snapshot.value as! [String:NSDictionary]
        for (_,value) in dict 
            if let uid = value["uid"] as? String
                self.following.append(uid)
            
        
        // 7
        ref.observe(DataEventType.value, with:  (snapshot2) in
            // 9
            let dict2 = snapshot2.value as! [String:NSDictionary]
            for(key, value) in dict2  // TYPO!!!!
                for uid2 in self.following
                    if (uid2 == key)
                       for (key2,value2) in value as! [String:String]
                            //print(key2 + "this is key2")
                            if(key2 == "urlToImage")
                                let urlimage = value2
                                //print(urlimage)
                                self.posts1.append(urlimage)
                                print(self.posts1.count)

                            
                       

                    
                
            
            DispatchQueue.main.async 
               // 11
               self.collectionview.reloadData()
            
            // 10
        )
        // 8
    )
    // 4

【讨论】:

没用。返回后posts1仍然是空的。还有其他想法吗?无论如何感谢您的帮助。 如果闭包中的print(self.posts1.count) 行打印了正确数量的帖子,那么集合视图必须显示数据,至少"im trying"。不要在同一方法中运行代码 underneath 闭包是什么意思?您是在谈论“观察”电话吗?包装在调度队列中仍然不起作用。我也无法打印“我正在尝试”。你能解释一下其他集合视图方法是如何工作的吗?苹果开发文档根本没有帮助。 闭包是( ... )里面的代码。 在下面闭包意味着代码就像原始fetchPosts末尾的无用打印行一样。 有没有办法让程序在加载字典时停止?我正在关注一个教程,他们让它以这种方式工作,所以我真的很想了解哪里出了问题。我在学校里从来没有学过任何关于异步的知识:(正确的帖子数量只有在它被附加到 fetchPosts() 时才会打印出来。假设集合视图在 fetchPosts() 存在之前不会加载是否正确?

以上是关于观察调用不更新字典值。异步的问题的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 的异步调用中包含返回处理程序

ajax调用后kendo mvvm不更新

Android LiveData防止在观察时接收到最后一个值

在模板中使用异步管道可观察到的不适用于单个值

使用 RxSwift 的异步任务和多个观察者

如何使用来自html的异步管道获取可观察对象的嵌套值