Taylor Simpson Taylor Simpson - 5 months ago 59
iOS Question

How to reload tableView data after data is passed by a Segue

I have two table views. One which the user clicks on and one where data is displayed. When the user clicks on a cell in the first table view a query is made to my firebase database and the query is stored in an Array. I then pass the data through a segue. I used a property observer so I know that the variable is being set. By using break points I was able to determine that my variable obtains its value right before the cellForRowAtIndexPath method. I need help displaying the data in my table view. I do not know where to reload the data to get the table view to update with my data. I am using Swift.

EDIT 2: I have solved my problem. I will post my first and second table views so that you can see my solution.

FirstTableView
`import UIKit
import Firebase
import FirebaseDatabase

class GenreTableViewController: UITableViewController {

let dataBase = FIRDatabase.database()


var genreArray = ["Drama","Classic,Comic/Graphic novel","Crime/Detective","Fable,Fairy tale","Fantasy","Fiction narrative", "Fiction in verse","Folklore","Historical fiction","Horror","Humour","Legend","Magical realism","Metafiction","Mystery","Mythology","Mythopoeia","Realistic fiction","Science fiction","Short story","Suspense/Thriller","Tall tale","Western,Biography","Autobiography","Essay","Narrative nonfiction/Personal narrative","Memoir","Speech","Textbook","Reference book","Self-help book","Journalism", "Religon"]

var ResultArray: [NSObject] = []
var infoArray:[AnyObject] = []



override func viewDidLoad() {
super.viewDidLoad()

// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false

// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

// MARK: - Table view data source


override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections

return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows



return genreArray.count
}


override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)

// Configure the cell...

cell.textLabel?.text = genreArray[indexPath.row]

return cell
}


override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {


}


override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {


let DestViewController: ResultTableViewController = segue.destinationViewController as! ResultTableViewController



if segue.identifier == "letsGo" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let tappedItem = self.genreArray[indexPath.row]


DestViewController.someString = tappedItem



}



}

}


}
`

`import UIKit
import Firebase
import FirebaseDatabase

class ResultTableViewController: UITableViewController {

let dataBase = FIRDatabase.database()

var SecondResultArray: [FIRDataSnapshot]! = []

var someString: String?{

didSet{


print("I AM A LARGE TEXT")


print(someString)




}






}





override func viewDidLoad() {

let bookRef = dataBase.reference().child("books")

bookRef.queryOrderedByChild("Genre")
.queryEqualToValue(someString)
.observeSingleEventOfType(.Value, withBlock:{ snapshot in
for child in snapshot.children {


self.SecondResultArray.append(child as! FIRDataSnapshot)
//print(self.ResultArray)


}



dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}



})


super.viewDidLoad()

// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false

// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}





override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return SecondResultArray.count
}


override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath)

// Configure the cell...

let bookSnapShot: FIRDataSnapshot! = self.SecondResultArray[indexPath.row]

let book = bookSnapShot.value as! Dictionary<String, String>

let Author = book["Author"] as String!
let Comment = book["Comment"] as String!
let Genre = book["Genre"] as String!
let User = book["User"] as String!
let title = book["title"] as String!





cell.textLabel?.numberOfLines = 0
cell.textLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping

cell.textLabel?.text = "Author: " + Author + "\n" + "Comment: " + Comment + "\n" + "Genre: " + Genre + "\n" + "User: " + User + "\n" + "Title: " + title


let photoUrl = book["bookPhoto"], url = NSURL(string:photoUrl!), data = NSData(contentsOfURL: url!)
cell.imageView?.image = UIImage(data: data!)



return cell
}


}
`

For better context and troubleshooting here is my current code for the tableView which is supposed to display data:

import UIKit

class ResultTableViewController: UITableViewController {

var SecondResultArray: Array<NSObject> = []{
willSet(newVal){
print("The old value was \(SecondResultArray) and the new value is \(newVal)")
}
didSet(oldVal){
print("The old value was \(oldVal) and the new value is \(SecondResultArray)")
self.tableView.reloadData()
}
}
override func viewDidLoad() {
print ("I have this many elements\(SecondResultArray.count)")
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return SecondResultArray.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath)
cell.textLabel?.text = SecondResultArray[indexPath.row] as? String
return cell
}
}


Edit:

Here is my first table view controller. I have tried using the completion handler, but I can't call it correctly and I am constricted by the fact that my query happens in the didSelectRowAtIndexPath method. Please help.

import UIKit
import Firebase
import FirebaseDatabase

class GenreTableViewController: UITableViewController {


let dataBase = FIRDatabase.database()

var genreArray = ["Drama","Classic,Comic/Graphic novel","Crime/Detective","Fable,Fairy tale","Fantasy","Fiction narrative", "Fiction in verse","Folklore","Historical fiction","Horror","Humour","Legend","Magical realism","Metafiction","Mystery","Mythology","Mythopoeia","Realistic fiction","Science fiction","Short story","Suspense/Thriller","Tall tale","Western,Biography","Autobiography","Essay","Narrative nonfiction/Personal narrative","Memoir","Speech","Textbook","Reference book","Self-help book","Journalism", "Religon"]

var ResultArray: [NSObject] = []
var infoArray:[AnyObject] = []

override func viewDidLoad() {
super.viewDidLoad()

// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false

// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return genreArray.count
}


override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)

cell.textLabel?.text = genreArray[indexPath.row]
return cell
}


override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

typealias CompletionHandler = (result:NSObject?, error: NSError?) -> Void

func getData(completionHandeler: CompletionHandler){
let bookRef = self.dataBase.reference().child("books")
let GenreSelector = self.genreArray[indexPath.row]
bookRef.queryOrderedByChild("Genre")
.queryEqualToValue(GenreSelector)
.observeSingleEventOfType(.Value, withBlock:{ snapshot in
for child in snapshot.children {
print("Loading group \((child.key!))")

self.ResultArray.append(child as! NSObject)
}
print(self.ResultArray)

self.performSegueWithIdentifier("letsGo", sender: self)
self.tableView.reloadData()
})
}
}


override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var DestViewController: ResultTableViewController = segue.destinationViewController as! ResultTableViewController
DestViewController.SecondResultArray = self.ResultArray
}

Jay Jay
Answer

How about this:

Assume you have an array (myArray) populated from Firebase and stored in the first tableViewController. There's a second tableViewController and a segue connecting them.

We want to be able to tap on an item in the first tableviewController, have the app retrieve detailed data for the item from Firebase (a 'data' node) and display the detailed data in the second tableViewController.

Firebase structure

some_node
   child_node_0
     data: some detailed data about child_node_0
   child_node_1
     data: some detailed data about child_node_1

Within the second tableViewContoller:

var passedObject: AnyObject? {
    didSet {
      self.configView() // Update the view.
     }
}

Tapping an item in the first tableView calls the following function

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
   if segue.identifier == "showListInSecondTable" {
          if let indexPath = self.tableView.indexPathForSelectedRow {
              let tappedItem = myArray[indexPath.row] as! String
              let keyOfTappedItem = tappedItem.firebaseKey //child_node_0 for example
              doFirebase(keyOfTappedItem)
            }
        }
    }

and the prepareForSegue then calls the following which loads the data from firebase and when the snapshot returns within the block, it populates the passedObject property in the second tableView

func doFirebase(firebaseKey: String) {

  ref = myRootRef.childByAppendingPath("\(firebaseKey)/data")
  //if we want the detailed data for child_node_0 this would resolve
  //  to    rootRef/child_node_0/data
  ref.observeSingleEventOfType(.Value, { snapshot in
     let detailObjectToPass = snapshot.Value["data"] as! NSArray or string etc

     let controller = (segue.destinationViewController as! UINavigationController).myViewController as! SecondViewController
     controller.passedObject = detailObjectToPass
}

and of course in secondController, setting the passedArray calls didSet and sets up the view, and tells the tableView to reload itself, displaying the passed array.

func configView() {
   //set up the view and buttons
   self.reloadData()
}

I did this super quick so ignore the typos's. The pattern is correct and satisfies the question. (and eliminates the need for an observer to boot!)

P.S. this is way over coded but I wanted to demonstrate the flow and leveraging the asynchronous call to firebase to load the second tableView when the data was valid within the block.