Change in firebase doesn't refresh table - Tables not displaying user nodes from arrays

℡╲_俬逩灬. 提交于 2021-02-05 11:55:54

问题


I am trying to have two arrays, used as dataSources for two tableViews. One array contains user type: macro and the other array contains user type: micro.

If a user type is changed from macro to micro (and vice-versa) I want to remove the user from one table and add it to the other.

Currently I am able to update the user's tag if changed in Firebase Database and have it appear in the proper array when the app is restarted. The observe function only collects it once and if it is changed, it doesn't update the table until the user quits the app and reopens it. The function I am using to observe.childChanged doesn't seem to update the arrays immediately on the user's app unless they do what was mentioned previously.

My main problem is that my tables are not displaying the users to the table. I am able to access their node and user but they are not appearing in my tables.

Here is the code for my user class:

import UIKit
import Firebase

    class User: NSObject {
        var Name: String?
        var Email: String?
        var UID: String?
        var Tag: String?

init?(from snapshot: DataSnapshot) {

        let dictionary = snapshot.value as? [String: Any]
    
        self.Name = dictionary!["Name"] as? String
        self.Email = dictionary!["Email"] as? String
        self.UID = dictionary!["UID"] as? String
        self.Tag = dictionary!["Tag"] as? String
        
        }
}

Here is my code for loading and populating my allUsersArray the info appends:

func loadAllUsersAndPopulateArray() {
    let ref = Database.database().reference().child("Users")
    ref.observeSingleEvent(of: .value, with: { snapshot in
        let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
        for userSnap in allUsersSnapshot {
            let user = User(from: userSnap)
            self.allUsersArray.append(user!)
            
            self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
            self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }

            self.observeChangeInUserProperty()
        }
    })
}

Here is my code for observing the change from Firebase:

 func observeChangeInUserProperty() {
    let ref = Database.database().reference().child("Users")
    ref.observe(.childChanged, with: { snapshot in
        let key = snapshot.key

        let tag = snapshot.childSnapshot(forPath: "Tag").value as! String // ! = never optional
        //get the user from the allUsersArray by its key
        if let user = self.allUsersArray.first(where: { $0.Tag == key }) {
            if user.Tag != tag { //if the tag changed, handle it
                user.Tag = tag //update the allUsersArray
                if tag == "Macro" { //if the new tag is Macro remove the user from the Micro array
                    if let userIndex = self.microUsersArray.firstIndex(where: { $0.Tag == key }) {
                        self.microUsersArray.remove(at: userIndex)
                        self.macroUsersArray.append(user) //add user to macro array
                    }
                } else { //new type is micro so remove from macro array
                    if let userIndex = self.macroUsersArray.firstIndex(where: { $0.Tag == key }) {
                        self.macroUsersArray.remove(at: userIndex)
                        self.microUsersArray.append(user)
                    }
                }
                //reload the tableviews to reflect the changes
                self.tableView.reloadData()
                self.microTableView.reloadData()

            }
        }
    })
}

Here is my code for the controller:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

var allUsersArray = [User]()
var macroUsersArray = [User]()
var microUsersArray = [User]()

override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.delegate = self
        tableView.dataSource = self
        searchBar.delegate = self
        tableView.register(UserCell.self, forCellReuseIdentifier: networkCell)
        
        microTableView.delegate = self
        microTableView.dataSource = self
        microTableView.register(microCell.self, forCellReuseIdentifier: microCell)
        
        loadAllUsersAndPopulateArray()
        
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        if (tableView === self.tableView) {
        
            return 1
            
          }


        else if (tableView === self.microTableView) {
            // Do something else
        
            return 1
        
        }
        
        fatalError("Invalid table")
        
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        if (tableView === self.tableView) {
            
                let cell = tableView.dequeueReusableCell(withIdentifier: networkCell, for: indexPath) as! UserCell
            
                let user = macroUsersArray[indexPath.row]
                cell.textLabel?.text = user.Name
                    
                    return cell
                    
                } else if (tableView === self.microTableView) {
                    
                    let cell = tableView.dequeueReusableCell(withIdentifier: microCell, for: indexPath) as! microInfluencerCell
                
                    let user = microUsersArray[indexPath.row]
                    cell.textLabel?.text = user.Name
                
                        return cell
            
        } else {
            fatalError("Invalid table")
        }
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        if (tableView === self.tableView) {
            return macroUsersArray.count
        }
        else if (tableView === self.microTableView) {
            return microUsersArray.count
        }
        
        else {
            fatalError("Invalid table")
        }

    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        
        if (tableView === self.tableView) {
            return 72
        }
        else if (tableView === self.microTableView) {
            return 72
        }
        
        else {
            fatalError("Invalid table")
        }
    }

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    
    if (tableView === self.tableView) {
        
        dismiss(animated: true) {
            let userName = self.macroUsersArray[indexPath.row]
            self.showSecondViewController(user: userName)

            print("Dismiss completed")
        }
        
    }
    else if (tableView === self.microTableView) {
        
        dismiss(animated: true) {
            let userName = self.microUsersArray[indexPath.row]
            self.showSecondController(user: userName)

            print("Dismiss completed")
        }
        
    }
    
    else {
        fatalError("Invalid table")
    }
  }
}

func showSecondViewController(user: User) {

    let SecondViewController = SecondViewController()
    SecondViewController.user = user
    let navigationController = UINavigationController(rootViewController: SecondViewController)
    self.present(navigationController, animated: true, completion: nil)
  }

func loadAllUsersAndPopulateArray() {
let ref = Database.database().reference().child("Users")
ref.observeSingleEvent(of: .value, with: { snapshot in
    let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
    for userSnap in allUsersSnapshot {
        let user = User(from: userSnap)
        self.allUsersArray.append(user!)

        self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
        self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }

        self.observeChangeInUserProperty()
     }
   })
 }
func observeChangeInUserProperty() {
let ref = Database.database().reference().child("Users")
ref.observe(.childChanged, with: { snapshot in
    let key = snapshot.key

    let tag = snapshot.childSnapshot(forPath: "Tag").value as! String // ! = never optional
    //get the user from the allUsersArray by its key
    if let user = self.allUsersArray.first(where: { $0.Tag == key }) {
        if user.Tag != tag { //if the tag changed, handle it
            user.Tag = tag //update the allUsersArray
            if tag == "Macro" { //if the new tag is Macro remove the user from the Micro array
                if let userIndex = self.microUsersArray.firstIndex(where: { $0.Tag == key }) {
                    self.microUsersArray.remove(at: userIndex)
                    self.macroUsersArray.append(user) //add user to macro array
                }
            } else { //new type is micro so remove from macro array
                if let userIndex = self.macroUsersArray.firstIndex(where: { $0.Tag == key }) {
                    self.macroUsersArray.remove(at: userIndex)
                    self.microUsersArray.append(user)
                }
            }
            //reload the tableviews to reflect the changes
            self.tableView.reloadData()
            self.microTableView.reloadData()

        }
       }
    })
  }
}

I am so close to getting this problem solved. Any help would be amazing.

EDIT:

func loadAllUsersAndPopulateArray() {
    let ref = Database.database().reference().child("Users").child("Talent")
    ref.observeSingleEvent(of: .value, with: { snapshot in
        let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
        for userSnap in allUsersSnapshot {
            let user = User(from: userSnap)
            self.allUsersArray.append(user!)
            self.allUsersNames.append(user!.Name!)

        }

        self.observeChangeInUserProperty()
    })

    self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
    self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }
    
    self.tableView.reloadData()
    self.microTableView.reloadData()
    
}

回答1:


With the help from Jay I was able to figure out the solution!

I needed to be setting my arrays info from within the closure. Whenever a person's tag is updated, the app needs to be closed and reopened to show the new change made in firebase.

Thank you @Jay for all the help!!!

here is the new function code:

func loadAllUsersAndPopulateArray() {
    let ref = Database.database().reference().child("Users")
    ref.observeSingleEvent(of: .value, with: { snapshot in
        let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
        for userSnap in allUsersSnapshot {
            let user = User(from: userSnap)
            self.allUsersArray.append(user!)
            self.allUsersNames.append(user!.Name!)

            self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
            self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }

            self.tableView.reloadData()
            self.microTableView.reloadData()
            
        }

        self.observeChangeInUserProperty()
    })
    



回答2:


The issue you're running into is that Firebase is asynchronous - firebase data is only valid within the closure following the Firebase function.

Code after that closure will execute before the code within the closure so you need to plan to handle that data in an asynchronous way.

Additionally, since you're populating an array with data from firebase and the other arrays as based on the first, there's no reason to filter them over and over; populate the main array and then when that's done, populate the other arrays.

Lastly, tableViews are sensitive and loading them over and over may cause flicker. Again, populate your main array and once that's done and the other arrays as populated, reload the tableViews - once.

Here's how that section of code should look

func loadAllUsersAndPopulateArray() {
    let ref = Database.database().reference().child("Users")
    ref.observeSingleEvent(of: .value, with: { snapshot in
        let allUsersSnapshot = snapshot.children.allObjects as! [DataSnapshot]
        for userSnap in allUsersSnapshot {
            let user = User(from: userSnap)
            self.allUsersArray.append(user!)
            self.allUsersNames.append(user!.Name!)            
        }

        self.macroUsersArray = self.allUsersArray.filter { $0.Tag == "Macro" }
        self.microUsersArray = self.allUsersArray.filter { $0.Tag == "Micro" }

        self.tableView.reloadData()
        self.microTableView.reloadData()

        self.observeChangeInUserProperty()
    })
}

I will also suggest getting rid of the allUsersNames array since those names are also stored in the allUsersArray.



来源:https://stackoverflow.com/questions/65136645/change-in-firebase-doesnt-refresh-table-tables-not-displaying-user-nodes-from

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!