Swift Firebase: Appropriate place to reloadData() - swift

I am attempting to reloadData() after fetching data in an async operation from Firebase. I am having difficulty finding the appropriate location to do the reloadData() as at different location, it either refreshes one section of the tableView and not the other, or it does not refresh at all, or worse, the refresh jumbles up the dataset.
Here is my code so far, and a description of the outcome when I reloadData() at that particular location:
func getData() {
DispatchQueue.main.async {
self.titleView.setTitle(viewController: self, animate: true, title: "Updating...")
}
ref.child("conversations").child(userUID).observeSingleEvent(of: .value) { (snapshot) in
for snap in snapshot.children {
let snapDatasnapshot = snap as! DataSnapshot
let snapValues = snapDatasnapshot.value as! [String: AnyObject]
let recipientUID = snapDatasnapshot.key
for (key, value) in snapValues {
let conversationStartTime = value["conversationStartTime"] as! Double
if calculateMinutesLeft(conversationStartTime) >= 0 {
let messagesDatasnapshot = snapDatasnapshot.childSnapshot(forPath: shoutoutID).childSnapshot(forPath: "messages")
let messagesArray = messagesDatasnapshot.value as! [String: AnyObject]
self.startTimeDictionary[shoutoutID] = conversation
for message in messagesArray {
let messageID = message.key
self.ref.child("messages").child(messageID).observeSingleEvent(of: .value, with: { (messagesSnapshot) in
let dictionary = messagesSnapshot.value as! [String: AnyObject]
let timestamp = dictionary["timestamp"] as! Double
let readReceipt = dictionary["readReceipt"] as! Bool
if let messageText = dictionary["message"] {
let message = messageText as! String
let m = Message(fromUserUID: self.userUID, message: message, timestamp: timestamp, recipientUserUID: recipientUID, imageUrl: nil, readReceipt: readReceipt)
self.messagesDictionary[conversation] = m
self.messages = Array(self.messagesDictionary.values)
}
//Location 1
DispatchQueue.main.async {
self.tableView.reloadData()
self.titleView.setTitle(viewController: self, animate: false, title: "Chats")
}
})
}
} else {
//Expired Convos
self.expiredConversations.append(conversation)
//Location 2
DispatchQueue.main.async {
self.tableView.reloadData()
self.titleView.setTitle(viewController: self, animate: false, title: "Chats")
}
}
}
}
if self.messages.isEmpty && self.expiredConversations.isEmpty {
self.titleView.setTitle(viewController: self, animate: false, title: "Chats")
}
//Location 3
DispatchQueue.main.async {
self.tableView.reloadData()
self.titleView.setTitle(viewController: self, animate: false, title: "Chats")
}
}
//Location 4
DispatchQueue.main.async {
self.tableView.reloadData()
self.titleView.setTitle(viewController: self, animate: false, title: "Chats")
}
}
The messages will be displayed in section 0 of the tableView while expiredConversations will be displayed in section 1 of the tableView.
My current implementation is at location 1 and 2. Doing so correctly reloads both sections of the table but will tend to jumble the data up when refreshed multiple times, or when tableView size gets too large. Otherwise:
At location 1 only, will refresh just section 0
At location 2 only, will refresh just section 1
At location 3 and 4, will refresh all sections, but give empty messages and expiredConversations
Any advice where is the best or ideal location to reloadData()? Or could my implementation be wrong totally?
EDIT: tableViewDelegate and tableViewDatasource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
if messages.isEmpty {
return 1
} else {
return messages.count
}
case 1:
return expiredConversations.count
default:
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
if messages.isEmpty {
let noConversationsCell = tableView.dequeueReusableCell(withIdentifier: "noConversationsCell", for: indexPath) as! NoActiveConversationsTableViewCell
noConversationsCell.isUserInteractionEnabled = false
return noConversationsCell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ChatLogTableViewCell
let message = messages[indexPath.row]
let conversation = conversations[indexPath.row]
//All the data model thing to populate the cells
return cell
}
case 1:
let expiredConversationsCell = tableView.dequeueReusableCell(withIdentifier: "expiredConversationsCell", for: indexPath) as! ExpiredConversationsTableViewCell
let conversation = expiredConversations[indexPath.row]
//All the data model thing to populate the cells
return expiredConversationsCell
default:
return UITableViewCell()
}
}

Related

Why my TableView not updating cells which are fetched from database?

I have TableView and every cell contains one reservation fetched from Firestore. I added refresh controller but no data reloads. I don't know where can be a problem. When I changed one reservation status for eg. canceled in Firebase it changes but in tableview after reloads no UI label change to "canceled". I must to display another controller and go back to update UI label in cell.
override func viewDidLoad()
{
super.viewDidLoad()
reservationsTableView.delegate = self
reservationsTableView.dataSource = self
navigationItem.hidesBackButton = true
searchReservations = Reservations
refreshControl.addTarget(self, action: #selector(refresh(send: )), for: UIControl.Event.valueChanged)
reservationsTableView.addSubview(refreshControl)
loadReservations()
}
#objc func refresh(send: UIRefreshControl)
{
DispatchQueue.main.async {
self.loadReservations()
self.refreshControl.endRefreshing()
self.reservationsTableView.reloadData()
}
}
}
func loadReservations()
{
Reservations = []
guard let uid = Auth.auth().currentUser?.uid else { return }
let userCollection = Firestore.firestore().collection("Users")
let thisUserDoc = userCollection.document(uid)
let snapshot = thisUserDoc.collection("Reservations").getDocuments { (querySnapshot, error) in
if let e = error {
print(e)
}
else
{
if let snapshotDocuments = querySnapshot?.documents
{
for doc in snapshotDocuments
{
let data = doc.data()
// let rid = doc.documentID
if let Time = data["Time"] as? String, let Date = data["Date"] as? String, let Guests = data["Guests"] as? String, let RestaurantName = data["RestaurantName"] as? String, let Name = data["Name"] as? String, let Status = data["Status"] as? String, let Phone = data["Phone"] as? String, let Id = data["Id"] as? String, let RestaurantId = data["RestaurantID"] as? String, let Id2 = data["Id2"] as? String, let guestId = data["GuestId"] as? String
{
let newReservation = Reservation(id: Id, userId: guestId, id2: Id2,restaurantId: RestaurantId ,restaurantName: RestaurantName, name: Name, phone: Phone, guests: Guests, time: Time, date: Date, status: Status)
self.Reservations.append(newReservation)
DispatchQueue.main.async {
self.reservationsTableView.reloadData()
}
}
}
}
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchReservations.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: K.reusableCellReservationsC, for: indexPath) as! ReservationCellC
cell.guestName.text = searchReservations[indexPath.row].name
cell.contact.text = searchReservations[indexPath.row].phone
cell.reservationTime.text = searchReservations[indexPath.row].time
cell.reservationDate.text = searchReservations[indexPath.row].date
cell.reservationGuets.text = searchReservations[indexPath.row].guests
cell.reservationStatus.text = searchReservations[indexPath.row].status
cell.cancelBtn.tag = indexPath.row
cell.cancelBtn.addTarget(self, action: #selector(btnCanceledCellTapped(sender: )), for: .touchUpInside)
cell.confirmBtn.addTarget(self, action: #selector(btnConfirmedCellTapped(sender: )), for: .touchUpInside)
cell.lateBtn.addTarget(self, action: #selector(btnLateCellTapped(sender: )), for: .touchUpInside)
return cell
}
What's this line
searchReservations = Reservations
By the way you are appending reservations as below
self.Reservations.append(newReservation)
But you are having row count as
searchReservations.count
This is wrong. It has to be return Reservations.count or you must append values as self.searchReservations.append(newReservation)

Swift - Search in UITableView [duplicate]

This question already has answers here:
Searchbar filtering issue
(3 answers)
Closed 3 years ago.
I'm trying to be able to search my Firebase data using a UISearchBar in my app & I am stuck. I am successfully receiving data from Firebase in a table view. I have a memories-writing app that the user can create memories (that are shown in a tableview from the firebase). memories has a title, description, a pic and a date and I want it to be able to search memories by the title.
I have a code here that doesn't work for some reason... il'l be glad if you could help me find out what's wrong in the code or find a replacement for this code :)
MemoryTitles class:
class MemoryTitles {
var title : String
init(withTitle: String) {
self.title = withTitle
}
}
MemoryViewController:
class MemoryViewController: UIViewController,UITableViewDelegate,UITableViewDataSource
// the filtered memories array for the search bar
var memoriesTitlesArr: [String] = []
var filteredDataa: [String] = []
// connections from storyboard to the code
#IBOutlet weak var tbl: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
// an array of memories
var memories : [Memory] = []
var ref = Database.database().reference()
let sref = Storage.storage().reference()
var lastIndex : Int = 0
var strMode : String = ""
// TableView functions
// Return the number of rows in section
// section - an index number identifying a section in tableView.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return filteredDataa.count
} else {
return memories.count
}
// return memories.count
}
// Return Cell for row function : an object inheriting from UITableViewCell
// indexPath - an index path locating a row in tableView.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "iden")
if searching {
cell?.textLabel?.text = filteredDataa[indexPath.row]
} else {
var cell : UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: "iden", for: indexPath)
if cell == nil
{
cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "iden")
}
let temp = memories[indexPath.row]
cell?.textLabel?.text = temp.title
cell?.imageView?.image = temp.image
return cell!
}
return cell!
}
// Can edit row : asks the data source to verify that the given row is editable.
// indexPath - an index path locating a row in tableView.
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true // true if the row indicated by indexPath is editable; otherwise, false.
}
// Asks the data source to commit the insertion or deletion of a specified row.
// indexPath - an index path locating a row in tableView.
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete // editingStyle - the cell editing style corresponding to a insertion or deletion requested for the row specified by indexPath.
{
let temp = self.memories[indexPath.row]
self.memories.remove(at: indexPath.row)
self.ref.child("MEmories/\(temp.key)").removeValue()
tableView.deleteRows(at: [indexPath as IndexPath], with: .fade)
}
}
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
let rightButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(MemoryViewController.barButtonItemClicked(_:)))
self.navigationItem.setRightBarButton(rightButton, animated: true)
// Do any additional setup after loading the view.
self.loadMemories()
self.tbl.delegate = self
self.tbl.dataSource = self
}
// click on bar-Button function
#objc func barButtonItemClicked(_ sender:UIBarButtonItem)
{
print("+ clicked") // writes "+ clicked"
let addMemoryViewController = self.storyboard?.instantiateViewController(withIdentifier: "AddMemoryViewController") as! AddMemoryViewController
self.strMode = "newMemory"
self.navigationController?.pushViewController(addMemoryViewController, animated: true)
}
// Reading from NSUserDefault (A class that provides simple storage of different data types solution.)
func readFromNSUserDefault()-> Memory?
{
let d : UserDefaults = UserDefaults.standard
let strTitle = d.object(forKey: "title") as? String
let strBody = d.object(forKey: "body") as? String
let strImageRef = d.object(forKey: "imageRef") as? String
let uid = d.object(forKey: "uid") as? String
let imageData = d.object(forKey: "imageData") as? Data
let key = d.object(forKey: "key") as? String
let date = d.object(forKey: "date") as? NSNumber
let m = Memory(title: strTitle!, body: strBody!, key: key!, uid: uid!, imageRef: strImageRef!, date: date!) // A variable from type memory
m.image = UIImage(data: imageData!)
m.key = key!
return m
}
override func viewDidAppear(_ animated: Bool) {
let d = UserDefaults.standard
let newMemory = readFromNSUserDefault()
let userAdded = d.bool(forKey: "userAdded") //key new user = true
let userEdited = d.bool(forKey: "userEdited")//key user edited = true
if self.strMode == "newMemory" && userAdded
{
self.memories.append(newMemory!)
self.tbl.reloadData()
}
else if self.strMode == "edit" && userEdited
{
memories[lastIndex] = newMemory!
self.tbl.reloadData()
}
d.set(false, forKey: "userAdded")
d.set(false, forKey: "userEdited")
d.synchronize()
self.strMode = " "
}
// loading the memories from the Database
func loadMemories()
{
let UID = Auth.auth().currentUser!.uid
self.ref.child("MEmories").queryOrdered(byChild: "uid").queryEqual(toValue: UID).observeSingleEvent(of: .value, with: {
snapShot in
if let dict = snapShot.value as? NSDictionary
{
for d in (dict as? Dictionary<String,AnyObject>)!
{
let title = d.value["title"] as?String
let body = d.value["body"] as? String
let uid = d.value["uid"] as? String
let imageRef = d.value["imageRef"] as? String
let date = d.value["date"] as? NSNumber
let m = Memory(title: title!, body: body!, uid: uid!,imageRef:imageRef!, date: date!)
m.key = d.key
let tempImageRef = self.sref.child(m.imageRef)
tempImageRef.getData(maxSize: 500*1024*1024, completion: {(data,error) in
if error == nil
{
if let imageData = data
{
m.image = UIImage(data: imageData)
self.memories.append(m)
self.tbl.reloadData()
}
}
})
self.memoriesTitlesArr.append(title!)
}
}//end of if
})
}
// Notifies the view controller that a segue is about to be performed.
// segue - The segue object containing information about the view controllers involved in the segue.
// senderThe object that initiated the segue.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier
{
if identifier == "goToEdit"
{
let indexPath = self.tbl.indexPathForSelectedRow
let addMemoryViewController = segue.destination as! AddMemoryViewController
self.strMode = "edit"
self.lastIndex = (indexPath?.row)!
addMemoryViewController.mode = self.strMode
addMemoryViewController.current = memories[(indexPath?.row)!]
}
}
}
var searching = false
}
extension MemoryViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredDataa = memoriesTitlesArr.filter({ $0.prefix(searchText.count)==searchText.lowercased()})
searching = true
tbl.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searching = false
searchBar.text = ""
tbl.reloadData()
}
}
Here is how you can get that working.
1. First of all, there is no need for searching to maintain the state of searching and not searching.
2. Secondly, use filteredData as the dataSource for tableView instead of memories. filteredData will initially contain all the objects from memories, i.e.
var memories : [Memory] = []
lazy var filteredData = self.memories
The UITableViewDataSource methods will be like,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.filteredData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "iden") {
cell.textLabel?.text = self.filteredData[indexPath.row].title
return cell
}
return UITableViewCell()
}
Now, while searching update the filteredData with filtered memories using the relevant condition, i.e.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
self.filteredData = self.memories.filter({ $0.title.hasPrefix(searchText) })
//change the condition as per your requirement......
self.tbl.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.text = nil
self.filteredData = self.memories
self.tbl.reloadData()
}
When cancelled, refill the filteredData with the whole memories data.

Firebase observe new added data even when the function isn't called

I have a function that observes data from my Firebase database. This data will be inserted in an array, so it can be send to my tableviewcell Viewcontroller. All the data will be put correct in the tabelviewcell, but I have a problem when I update my Firebase database. Everytime I change a value in the database it will immediately update my tableView even when my function is not called. I am not sure what I am doing wrong and how to prevent this.
This is my function observe:
Database.database().reference().child("posts").child("\(postId)").child("comments").observe(.value, with: { snapshot in
if let snapshots = snapshot.children.allObjects as? [DataSnapshot] {
for snap in snapshots {
if let postDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let post = Comment.transformComment(dict: postDict)
self.comments.insert(post, at: 0)
self.tableView.reloadData()
}
}
}
})
Array:
var comments: [Comment] = []
extension Comment {
static func transformComment(dict: [String: Any]) -> Comment {
let comment = Comment()
comment.commentText = dict["commentText"] as? String
comment.uid = dict["uid"] as? String
comment.timestamp = dict["timestamp"] as? Int
comment.likeCount = dict["likeCount"] as? Int
comment.childByAutoId = dict["childByAutoId"] as? String
comment.id = dict["postId"] as? String
return comment
}
}
Tablevieww Functions:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if comments.count == 0 {
self.tableView.setEmptyMessage("No comments yet!")
} else {
self.tableView.restore()
}
return comments.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Comment", for: indexPath) as! CommentTableViewCell
let comment = comments[indexPath.row]
cell.comment = comment
cell.delegate = self
return cell
}
To listen once replace
.child("comments").observe(.value, with: { snapshot in
With
.child("comments").observeSingleEvent(of: .value) { snapshot in
Or
.child("comments").observe(.childChanged) { snapshot in
to listen to added childs

Index out of range custom cell TableViewController

I'm trying to populate 3 custom cells into a TableViewController.
but I always get index out of range error. I`m not sure whats wrong with my code. anyone can help me, I'm newbie in swift.
but when i use 0 for numberOfRowsInSection return, the output is the first cell.
here's my code :
class testResize: UITableViewController {
#objc var comments = [AnyObject]()
#objc var images = [UIImage]()
var getImg = [String]()
override func viewDidLoad() {
super.viewDidLoad()
loadPosts()
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let getCom = comments[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeHeadCell.self), for: indexPath) as! testResizeHeadCell
let user = getCom["nickname"] as! String
let ava = getCom["ava"] as! String
if ava != "" {
let resource = ImageResource(downloadURL: URL(string: ava)!, cacheKey: ava)
cell.avaImg.kf.setImage(with: resource)
}
cell.username.text = user
return cell
}else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeCell.self), for: indexPath) as! testResizeCell
cell.setCustomImage(image: images[indexPath.row])
return cell
}else {
let getCom = comments[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testRezieTextCell.self), for: indexPath) as! testRezieTextCell
let text = getCom["text"] as! String
cell.explaination.text = text
return cell
}
}
here is my load function :
#objc func loadPosts() {
let uuid = "959D1073"
let url = URL(string: "some/url.php")!
self.tableView.reloadData()
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "uuid=\(uuid)"
//print(body)
request.httpBody = body.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
self.comments.removeAll(keepingCapacity: false)
self.images.removeAll(keepingCapacity: false)
self.tableView.reloadData()
guard let parseJSON = json else {
print("Error While Parsing")
return
}
guard let posts = parseJSON["posts"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
self.comments = posts.reversed()
print(self.comments)
for i in 0 ..< self.comments.count {
let path = self.comments[i]["path"] as? String
self.getImg = [path!]
if !path!.isEmpty {
let url = NSURL(string: path!)!
let imageData = try? Data(contentsOf: url as URL)
let image = UIImage(data: imageData! as Data)!
self.images.append(image)
} else {
let image = UIImage()
self.images.append(image)
}
}
self.tableView.reloadData()
//print(posts)
} catch {
print(error)
}
}else{
print(error!)
}
})
}.resume()
}
i think you have a single comment and 3 cell type and when you use indexPath.row happen some thing like this :
for example :
comments = {[{nickname : "mahdi" , ava : "url"} ]}
if indexPath.row == 0 {
let getCom = comments[0]
let user = getCom["nickname"] as! String
let ava = getCom["ava"] as! String
}else if indexPath.row == 1 {
images[1]
}else {
let getCom = comments[2]
let text = getCom["text"] as! String
}
but you have just one comment and when you call comments[1] or commens [2] , you get index out of range error
please try this code :
class testResize:UITableViewController {
#objc var comments = [AnyObject]()
#objc var images = [UIImage]()
var getImg = [String]()
override func viewDidLoad() {
super.viewDidLoad()
loadPosts()
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (self.comments.count == 0 ? 0 : self.comments.count + 2)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let getCom = comments[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeHeadCell.self), for: indexPath) as! testResizeHeadCell
let user = getCom["nickname"] as! String
let ava = getCom["ava"] as! String
if ava != "" {
let resource = ImageResource(downloadURL: URL(string: ava)!, cacheKey: ava)
cell.avaImg.kf.setImage(with: resource)
}
cell.username.text = user
return cell
}else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeCell.self), for: indexPath) as! testResizeCell
cell.setCustomImage(image: images[indexPath.row - 1])
return cell
}else {
let getCom = comments[indexPath.row - 2]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testRezieTextCell.self), for: indexPath) as! testRezieTextCell
let text = getCom["text"] as! String
cell.explaination.text = text
return cell
}
}
and change your numberOfRowInSection :
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return (self.comments.count == 0 ? 0 : self.comments.count + 2)
}
I am assuming that you load your posts asynchronously.
But you do not check if there are actually enough elements in the array. You should check if there are actually enough elements in the array before you access it with a fixed index.
Additionally, you should change your numberOfRows method to the following:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.comments.count
}
After you have loaded your posts, you can then call
self.tableView.reloadData()

Swift, Accessing data for a table view

So, I load my firebase node, and then append the data into an array to use in a table view, but for some reason i cannot access the data inside of planitTitles, unless i am within this closure. Please, any workaround ? I feel like i have achieved this before. Thanks
func loadFirebase(){
let ref = Database.database().reference()
let planits = ref.child("planits")
planits.observe( .value, with: { (planitsSnapshot) in
for child in planitsSnapshot.children {
let planSnap = child as! DataSnapshot
let planDict = planSnap.value as! [String: Any]
if self.keyForThisPlanit.contains(planSnap.key){
let title = planDict["Planit Title"] as! String
self.planitTitles.append(title)
}
}
})
print(self.planitTitles)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "friendUpdatesCell") as? ViewUpdatesTVCell else { return UITableViewCell() }
cell.randomPlanitNumber.text = planitTitles[indexPath.row]
// CRASHES HERE WITH ERROR OUT OF INDEX
return cell
}
You need to reload the table after the for loop
var planitTitles = [String]()
//
func loadFirebase(){
let ref = Database.database().reference()
let planits = ref.child("planits")
planits.observe( .value, with: { (planitsSnapshot) in
self.planitTitles.removeAll() // you may want to clear here to avoid duplications
for child in planitsSnapshot.children {
let planSnap = child as! DataSnapshot
let planDict = planSnap.value as! [String: Any]
if self.keyForThisPlanit.contains(planSnap.key){
let title = planDict["Planit Title"] as! String
self.planitTitles.append(title)
}
}
print(self.planitTitles)
self.tableView.reloadData()
})
}
func numberOfRows(inSection section: Int) -> Int {
return planitTitles.count // to prevent cellForRowAt indexOutOfBounds crash
}