问题
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