ndduong
ndduong

Reputation: 439

How to retrieve firebase database properly?

I am trying to retrieve the data from firebase database. However, I cannot get my local variables assigned to the values of the database. I am using the following classes and methods.

class Episode {

var title: String?
var description: String?
var location: String?
var discount: String?
var star: Int?

init() {
    self.title = ""
    self.description = ""
    self.location = ""
    self.discount = ""
    self.star = 0
}

This is my method for pulling the data from the databse

func getValues() -> Episode {
    let rootRef = FIRDatabase.database().reference().child("Restaurants").child("The Kafe")
    let descriptionRef = rootRef.child("Description")
    let discountRef = rootRef.child("Discount")
    let locationRef = rootRef.child("Location")
    let starRef = rootRef.child("Star")
    let episode = Episode()

    descriptionRef.observeEventType(.Value) { (snap: FIRDataSnapshot) in
        episode.description = snap.value as? String
    }
    discountRef.observeEventType(.Value) { (snap: FIRDataSnapshot) in
        episode.discount = snap.value as? String
    }
    locationRef.observeEventType(.Value) { (snap: FIRDataSnapshot) in
        episode.location = snap.value as? String
    }
    starRef.observeEventType(.Value) { (snap: FIRDataSnapshot) in
        episode.star = snap.value as? Int
        print(episode.description!)
    }
    return episode
}

When I print out the values of the returned episode, they are all empty. However, when I print the values within the closure itself (Eg. if I do print(episode.description) within the obserEventType closure, it works fine. But if I print it outside it is empty.

I think I am missing something fundamental about swift or firebase. I am new to iOS programming so any help would be greatly appreciated.

Upvotes: 1

Views: 4296

Answers (2)

Ymmanuel
Ymmanuel

Reputation: 2533

Only inside the first observer you will have the value the return will always be nil, that is because only the return is trying to work in a sync way while firebase will always work in an async way

rootRef.observeEventType(.Value, withBlock: {(snap) in
        let ep: Dictionary<String,AnyObject?> = [
            "title": snap.childSnapshotForPath("Title").value as? String,
            "description": snap.childSnapshotForPath("Description").value as? String,
            "location": snap.childSnapshotForPath("Location").value as? String,
            "discount": snap.childSnapshotForPath("Discount").value as? String,
            "star": (snap.childSnapshotForPath("Star").value as? NSNumber)?.integerValue,
        ]
        //Here you have your data in your object
        episode = Episode(key: snap.key, dictionary: ep)
    })

rootRef.observeEventType(.Value) { (snap: FIRDataSnapshot) in
    print(snap.childSnapshotForPath("Title").value as? String)
}

return episode!

Also if you want to get it from a function like that you should probably use observeSingleEventType.

You need to rethink flow of your code because you are expecting firebase to work synchronously when it is always asynchronous. The way you have your getValues function will never work.

To solve this issue you should read about async execution and callbacks in swift.

Upvotes: 4

Ymmanuel
Ymmanuel

Reputation: 2533

All Firebase events are asynchronous so they are executed in a non-sequential way, that is why you only have access to the data inside the context of the callback...if you put a print outside the callback it is executed in a synchronous way so it gets executed before the callback, that is why it is in its initial status

1) You only need the rootRef, delete the rest

 let ref = FIRDatabase.database().reference().child("Restaurants").child("The Kafe")

2) You only need one observer

var episode:Episode? = nil

rootRef.observeEventType(.Value,withBlock: {(snap) in

   let ep:Dictionary<String,AnyObject?> = [
       "title":snap.childSnapshotForPath("title").value as? String,
       //Etc...
       "star":(snap.childSnapshotForPath("price").value as? NSNumber)?.integerValue,
    ]

    //Here you have your data in your object
    episode = Episode(key:snap.key,dictionary:ep)


}

3) your episode class can be like this

class Episode {

    private var _key:String!
    private var _title:String?
    //Etc.....
    private var _star:Int?

    var key:String!{
        return _key
    }
    var title:String?{
        return _title
    }
    //Etc....
    var star:Int?{
        return _star
    }

    init(key:String!, title:String?,//etc...., star:Int?){
        self._key = key
        self._title = title
        //Etc....
    }

    init(key:String,dictionary:Dictionary<String,AnyObject?>){

        _key = key

        if let title = dictionary["title"] as? String{
            self._title = title
        }
        //Etc...
        if let star = dictionary["star"] as? Int{
            self._star = star
        }
        ..
    }
}

Upvotes: 3

Related Questions