【问题标题】: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 行,并在闭包内做你必须做的事情。
  • 我真的不知道如何使用闭包。我只是用谷歌搜索了它们,然后就出现了。我知道完成处理程序,但实际上没有人告诉你它是如何工作的。我只是想加载一个集合视图,但我仍然不知道一旦我填充了“帖子”字典,这是否会起作用。你能帮我解决一下闭包语法吗?

标签: ios swift firebase firebase-realtime-database


【解决方案1】:

您只需要稍作改动

  1. 简单地声明posts1

    var posts1 = [String]()
    

    并删除属性观察者didSet

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

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

               }
               DispatchQueue.main.async {
                   self.collectionview.reloadData()
               }
            })
        })
    }
    
  4. 而且第二个闭包中有一个错字。应该是

    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() 存在之前不会加载是否正确?
猜你喜欢
  • 2019-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-26
  • 1970-01-01
  • 1970-01-01
  • 2016-03-04
  • 1970-01-01
相关资源
最近更新 更多