1

I don't have a storyboard. I'm doing everything programmatically.

The loadData() method takes Firebase data, put it into a Company object, and loads the object into the companies array. In the didFinishLaunchingWithOptions method in the App Delegate, I instantiated the class and called loadData()

When I run breakpoint at the line indicated by the comment and type "po companies" in the console, I get 0 companies. The print statements inside .observe are printed to the console and I can see that the company's properties are non-null, but anything outside .observe, including the for loop and the print statement called after the load data method in the App Delegate are not printed.

class informationStateController {    
func loadData() {
    //Set firebase database reference
    ref = FIRDatabase.database().reference()

    //Retrieve posts and listen for changes
    databaseHandle = ref?.child("companies").observe(.childAdded, with: { (snapshot) in
        //Code that executes when child is added
        let company = Company()
        company.name = snapshot.childSnapshot(forPath: "name").value as! String
        print(company.name)
        company.location = snapshot.childSnapshot(forPath: "location").value as! String
        print(company.location)
        self.companies.append(company)
        print("databaseHandle was called")
    })

    for company in companies {
        print(company)
    }
    //breakpoint inserted here

}
}

Why is my array empty and why are print statements outside .observe NOT printing to the console? The output for the console is set to "All Output". I called import FirebaseDatabase in the class and import Firebase in the App Delegate.

4
  • observe observes changes to your firebase table, it doesn't automatically fetch new ones. You probably want to GET them first, then establish the observer. Commented Jan 12, 2017 at 20:32
  • Could you show me what the GET method is? According to the firebase documentation for reading data for ios (firebase.google.com/docs/database/ios/read-and-write), to read data, there is only .observeSingleEvent and .observe Commented Jan 12, 2017 at 20:42
  • 1
    Even if you would be doing this right, your array would probably still be empty as the child listener gets fired after your break point gets executed. Commented Jan 12, 2017 at 20:51
  • True that @hotrod didn't even notice that. Breatpoint should be inside the listener callback. Commented Jan 12, 2017 at 20:56

2 Answers 2

2

Data is loaded from the Firebase Database asynchronously. This means that by the time you print the companies, they won't have loaded yet.

You can easily see this by also printing the companies as they're loaded:

//Set firebase database reference
ref = FIRDatabase.database().reference()

//Retrieve posts and listen for changes
databaseHandle = ref?.child("companies").observe(.childAdded, with: { (snapshot) in
    //Code that executes when child is added
    let company = Company()
    company.name = snapshot.childSnapshot(forPath: "name").value as! String
    print(company.name)
    company.location = snapshot.childSnapshot(forPath: "location").value as! String
    print(company.location)
    self.companies.append(company)
    print("databaseHandle was called")
    for company in companies {
        print(company)
    }
})

Now you'll first see one company printed (when childAdded fires for the first time), then two companies (when childAdded fires again), then three companies, etc.

Sign up to request clarification or add additional context in comments.

8 Comments

If data is loaded asynchronously, does that mean after I call loadData() in my question, there isn't be any data loaded into the array? Then how do I get an array with all the data loaded in, so I can pass it to other view controllers?
For the initial data, the client always first fires all .childAdded events and only then fires .value. So you can listen for a .value event to detect when all initial data have been loaded.
I tried .observe(.value) in loadData() and iterated through the snapshot.children but the print statement after loadData() still showed that the array was empty- am I doing something wrong?
The data is loaded asynchronously. By the time loadData() returns, the data hasn't loaded yet. And you cannot return something that hasn't loaded yet. The solution is to move the code that needs the data into the callback block of the .value event in loadData(). So right where the print statements now are
I highly recommend reading some more on asynchronous data loading on iOS, because you'll find this to be a very common pattern when accessing modern web/cloud APIs. See for example [this answer](stackoverflow.com/q/34211787], this answer or [this answer for Swift[(stackoverflow.com/questions/25203556/…).
|
0

Per the docs (emphasis mine)

Important: The FIRDataEventTypeValue event is fired every time data is changed at the specified database reference, including changes to children. To limit the size of your snapshots, attach only at the highest level needed for watching changes. For example, attaching a listener to the root of your database is not recommended.

In your case, you're observing changes to the database, but no changes are happening, so you won't bet getting new data. I think the docs make this unnecessarily confusing, if you want to pull records that already exist, you have to query for it:

https://firebase.google.com/docs/database/ios/lists-of-data#sort_data

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
let recentPostsQuery = (ref?.child("companies").queryLimited(toFirst: 100))!

Once you have that queried data, you can then deal with the observer and append data as required when new data is pushed.

All of this aside, Frank's answer is the reason you'll never see the print when a company is added even if you set the listener up right — you need to write that inside the completion block of the observer or query.

2 Comments

Is this (postimg.org/image/rvrpth9m9) what you're recommending I do based on your post? How is that different from what I did before? I'm also not sure why I should be querying since base on youtube videos (youtube.com/watch?v=RMudKhNY0sI&t=732s) for retrieving data from firebase, a lot of them use .observe (.childAdded).
Not sure if that syntax is right — doesn't look quite like it. Point is, if you create an observer, you won't actually see the data until you create an entry in the database. Observe listens for changes, it doesn't query for existing records. If you only want to use observe, you'll need to run the app and then insert records in order to see it working. And -- regardless, you still have the same problem that Frank's answer points out no matter which solution you use.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.