首页 文章

如何从Firebase正确加载信息?

提问于
浏览
2

我会尽力解释我在使用firebase工作的时间吗?截至目前,我在 override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 方法中调用了firebase

经过一些测试后,似乎它正在加载firebase中的所有内容,但它并没有更新UI . 这导致我相信它在出于某种原因加载信息之前创建了单元格?我将尝试弄清楚如何以某种方式使其“阻止” .

The image

1 回答

  • 1

    您应该将加载委托给单元格本身,而不是collectionView:cellForItemAtIndexPath方法 . 原因是委托方法将异步挂起并用于FireBase网络任务的回调 . 虽然后者通常很快(根据经验),但您可能在此处遇到一些UI加载问题 . 根据视图中的方块数判断..

    理想情况下,你想要这样的东西:

    import FirebaseDatabase
    
    
    class FirebaseNode {
    
        //This allows you to set a single point of reference for Firebase Database accross your app
        static let ref = Database.database().reference(fromURL: "Your Firebase URL")
    
    }
    
    class BasicCell : UICollectionViewCell {
    
        var firPathObserver : String { //This will make sure that as soon as you set the value, it will fetch from firebase
            didSet {
                let path = firPathObserver
                FirebaseNode.ref.thePathToYouDoc(path) ..... {
                    snapshot _
                    self.handleSnapshot(snapshot)
                }
            }
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            setupSubViews()
        }
    
        func setupSubViews() {
            //you add your views here..
        }
    
        func handleSnapshot(_ snapshot: FIRSnapshot) {
            //use the content of the observed value
            DispatchQueue.main.async {
                //handle UI updates/animations here
            }
        }
    
    }
    

    你会用它:

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let path = someWhereThatStoresThePath(indexPath.item)//you get your observer ID according to indexPath.item.. in whichever way you do this
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Your Cell ID", for: indexPath) as! BasicCell
            cell.firPathObserver = path
            return cell
        }
    

    如果这不起作用,很可能您可能遇到一些Firebase限制..这是不太可能的imo .

    Update .. with some corrections and with local cache .

    class FirebaseNode {
    
        //This allows you to set a single point of reference for Firebase Database accross your app
        static let node = FirebaseNode()
    
        let ref = Database.database().reference(fromURL: "Your Firebase URL")
    
        //This is the cache, set to private, since type casting between String and NSString would add messiness to your code
        private var cache2 = NSCache<NSString, DataSnapshot>()
    
        func getSnapshotWith(_ id: String) -> DataSnapshot? {
            let identifier = id as NSString
            return cache2.object(forKey: identifier)
        }
    
        func addSnapToCache(_ id: String,_ snapshot: DataSnapshot) {
            cache2.setObject(snapshot, forKey: id as NSString)
        }
    
    }
    
    class BasicCell : UICollectionViewCell {
    
        var firPathObserver : String? { //This will make sure that as soon as you set the value, it will fetch from firebase
            didSet {
                handleFirebaseContent(self.firPathObserver)
            }
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            setupSubViews()
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func setupSubViews() {
            //you add your views here..
        }
    
        func handleFirebaseContent(_ atPath: String?) {
            guard let path = atPath else {
                //there is no content
                handleNoPath()
                return
            }
            if let localSnap = FirebaseNode.node.getSnapshotWith(path) {
                handleSnapshot(localSnap)
                return
            }
            makeFirebaseNetworkTaskFor(path)
        }
    
    
        func handleSnapshot(_ snapshot: DataSnapshot) {
            //use the content of the observed value, create and apply vars
            DispatchQueue.main.async {
                //handle UI updates/animations here
            }
        }
    
        private func handleNoPath() {
            //make the change.
        }
    
        private func makeFirebaseNetworkTaskFor(_ id: String) {
            FirebaseNode.node.ref.child("go all the way to your object tree...").child(id).observeSingleEvent(of: .value, with: {
                (snapshot) in
    
                //Add the conditional logic here..
    
                //If snapshot != "<null>"
                FirebaseNode.node.addSnapToCache(id, snapshot)
                self.handleSnapshot(snapshot)
                //If snapshot == "<null>"
                return
    
            }, withCancel: nil)
        }
    
    }
    

    然而,有一点,使用NSCache:这适用于中小型列表或内容范围;但是它有一个内存管理功能,如果内存变得稀缺,可以对内容进行去内核处理 . 所以当处理像你这样的大集合时,你可能会选择使用分类字典,因为它的内存不会自动解除分配 . 使用它就像交换东西一样简单:

    class FirebaseNode {
    
        //This allows you to set a single point of reference for Firebase Database accross your app
        static let node = FirebaseNode()
    
        let ref = Database.database().reference(fromURL: "Your Firebase URL")
    
        //This is the cache, set to private, since type casting between String and NSString would add messiness to your code
        private var cache2 : [String:DataSnapshot] = [:]
    
        func getSnapshotWith(_ id: String) -> DataSnapshot? {
            return cache2[id]
        }
    
        func addSnapToCache(_ id: String,_ snapshot: DataSnapshot) {
            cache2[id] = snapshot
        }
    
    }
    

    此外,始终确保通过 node 强引用 Firebasenode ,这可确保您始终使用 Firebasenode 的ONE实例 . 即,这没关系: Firebasenode.node.refFirebasenode.node.getSnapshot.. ,这不是: Firebasenode.refFirebasenode().ref

相关问题