1

I am working with swift in Xcode 7. I am totally new to Swift, Xcode, and Firebase. I would like to have three UITableViewControllers in my iOS app. The first two TableView controllers will need dynamic content and the third TableView controller will need static content. I would like for the second and third TableView controllers to display data based on what is pressed on the previous TableView controller. All of the data will come from my Firebase. I have no idea where to start. Please point me in the right direction! Thank you!

jithin
  • 459
  • 8
  • 21
Will Jackson
  • 335
  • 1
  • 4
  • 8
  • @DavidEast answer is correct. Before doing any troubleshooting on your follow up issue(s), have you gone through the [Firebase QuickStart Guide](https://www.firebase.com/docs/ios/quickstart.html) and see if you can read/write data in Firebase from clean, fresh app? Once you have a base app working, adding tableViews and populating them from Firebase is trivial but if it's not working to start with, that has to be addressed first. – Jay Dec 13 '15 at 14:01

2 Answers2

8

This question is broad, in that it asks how to do three different tasks.

I think you'll be better off getting answers if you only ask for one thing at a time.

I can help you with populating a UITableViewController with Firebase.

class MyTableViewController: UITableViewController {
  // your firebase reference as a property
  var ref: Firebase! 
  // your data source, you can replace this with your own model if you wish
  var items = [FDataSnapshot]() 

  override func viewDidLoad() {
    super.viewDidLoad()
    // initialize the ref in viewDidLoad
    ref = Firebase(url: "<my-firebase-app>/items")
  }

  override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    // listen for update with the .Value event
    ref.observeEventType(.Value) { (snapshot: FDataSnapshot!) in

      var newItems = [FDataSnapshot]()

      // loop through the children and append them to the new array
      for item in snapshot.children {
        newItems.append(item as! FDataSnapshot)
      }

      // replace the old array
      self.items = newItems
      // reload the UITableView
      self.tableView.reloadData()
    }
  } 
}

This technique uses the .Value event, you can also use .ChildAdded, but then you have to keep track of .ChildChanged, '.ChildRemoved', and .ChildMoved, which can get pretty complicated.

The FirebaseUI library for iOS handles this pretty easily.

dataSource = FirebaseTableViewDataSource(ref: self.firebaseRef, cellReuseIdentifier: "<YOUR-REUSE-IDENTIFIER>", view: self.tableView)

dataSource.populateCellWithBlock { (cell: UITableViewCell, obj: NSObject) -> Void in
  let snap = obj as! FDataSnapshot

  // Populate cell as you see fit, like as below
  cell.textLabel?.text = snap.key as String
}
David East
  • 31,526
  • 6
  • 67
  • 82
  • Thank you so much for your help @DavidEast , and I will definitely ask only one question at a time. I tried using your code and it worked except for one thing: there was an error at the line `for item in snapshot.children {` and the error was `Use of unresolved identifier 'snapshot'` – Will Jackson Dec 13 '15 at 03:10
  • @WillJackson, sorry I had a typo for the parameter name `snapshot`. Try it now – David East Dec 13 '15 at 03:25
  • No problem about the typo @DavidEast. I tried running it without the second part of code and I didn't have any data populating my TableViewController. When I added the second part, I got the errors `Use of unresolved identifier 'dataSource'` and `Use of unresolved identifier 'FirebaseTableViewDataSource'` – Will Jackson Dec 13 '15 at 03:39
  • The second part is a code sample. It was just to give you an idea of how easy it is. You'd have to install the library and read the docs to get that running. Consider it a piece of advice. – David East Dec 13 '15 at 03:42
  • Okay @DavidEast that makes more sense. I still have no data though when I use the first part of code. If it helps I get this error in my debug window `2015-12-12 22:43:31.669 The Lighthouse App[3753:170217] CFNetwork SSLHandshake failed (-9807)` – Will Jackson Dec 13 '15 at 03:45
  • That's another issue entirely which can be solved here: http://stackoverflow.com/questions/31345273/ios-9-ats-and-firebase-rest – David East Dec 13 '15 at 03:51
  • Sorry to bother you again @DavidEast . I tried adding that key to Info.plist and my problem is still there. – Will Jackson Dec 13 '15 at 04:20
  • I can't really help you on this thread, @WillJackson. This question is about UITableViewControllers and Firebase, and the comments section is not a good place for troubleshooting. See this post before asking your next question too http://stackoverflow.com/help/mcve – David East Dec 13 '15 at 05:04
  • Just FYI, I've started a new question @DavidEast . It's http://stackoverflow.com/questions/34248722/cfnetwork-sslhandshake-failed-9807 – Will Jackson Dec 13 '15 at 14:55
2

I do it slightly different when I have a UITableViewController, especially for those that can push to another detail view / or show a modal view over the top.

Having the setObserver in ViewDidAppear works well. However, I didnt like the fact that when I looked into a cells detail view and subsequently popped that view, I was fetching from Firebase and reloading the table again, despite the possibility of no changes being made.

This way the observer is added in viewDidLoad, and is only removed when itself is popped from the Nav Controller stack. The tableview is not reloaded unnecessarily when the viewAppears.

var myRef:FIRDatabaseReference = "" // your reference

override func viewDidLoad() {
    super.viewDidLoad()
    setObserver()
}

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)

    // only called when popped from the Nav Controller stack
    // if I push to another detail view my observer will remain active
    if isBeingDismissed() || isMovingFromParentViewController() {
        myRef.removeAllObservers()
    }
}

func setObserver() {

    myRef.observeEventType(.Value, withBlock: { snapshot in

        var newThings = [MyThing]()

        for s in snapshot.children {
            let ss = s as! FIRDataSnapshot
            let new = MyThing(snap: ss) // convert my snapshot into my type
            newThings.append(new)
        }

        self.things = newThings.sort{ $0.name < $1.name) } // some sort
        self.tableView.reloadData()
    })
}

I also use .ChildChanged .ChildDeleted and .ChildAdded in UITableViews. They work really well and allow you use UITableView animations. Its a little more code, but nothing too difficult.

You can use .ChildChanged to get the initial data one item at a time, then it will monitor for changes after that.

If you want all your data at once in the initial load you will need .Value, I suggest you use observeSingleEventOfType for your first load of the tableview. Just note that if you also have .ChildAdded observer you will also get an initial set of data when that observer is added - so you need to deal with those items (i.e. don't add them to your data set) otherwise your items will appear twice on the initial load.

DogCoffee
  • 19,820
  • 10
  • 87
  • 120