Fetch Firebase inside another fetch - ios

So I am creating a forum inside my app and when fetching the topics on the database I also need to fetch some user info like picture and username of the original poster to put it on the table view cells.
To keep the recommendation of keeping the Firebase database flat I have the messages on a separate ref searchable by key and NOT as a child of topics.
I can't keep something like opImage and opUsername as childs of topics because if a user changes its username or profile image I would need to change it on every topic he ever participated as well.
What is the best way to handle this?
The method to fetch from Firebase would look something like this. The problem with that implementation is that the Firebase calls are asynchronous and there would be no guarantee that the image would be attached to the correct topic.
DataService.ds.Topics_Base.child(cat.key!).observeSingleEvent(of:.value, with: { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {//snapshots = all topics
for top in snapshots{
if let dict = top.value as? Dictionary<String,AnyObject>{
guard let title = dict["title"] as? String,let text = dict["text"] as? String,let time = dict["time"] as? String,let username = dict["username"] as? String,let timeSimple = dict["time-simple"] as? String,let lastPost = dict["last-post"] as? String,let open = dict["open"] as? Bool else{
continue
}
let newTopic = Topic(subject: title, text: text, time: time, topicKey: top.key, username: username)
self.allTopics.append(newTopic)
print(self.allTopics.count)
if let email = dict["email"] as? String{
//FETCH USER INFO FROM EMAIL FETCHED
let validEmail = HelperMethods.removeSpecialCharactersFromEmail(email: email)
DataService.ds.Users_Base.child(validEmail).observeSingleEvent(of: .value, with: {(snapshot) in
if let userDict = snapshot.value as? Dictionary<String,AnyObject>{
if let imgUrl = userDict["profile_image_url"] as? String{
self.allTopics[currentIndex].opImageUrl = imgUrl
}
self.allTopics.append(newTopic)
if (totalTopics == snapshots.count){
DispatchQueue.main.async {
self.allTopics.sort { $0.lastPostTime! > $1.lastPostTime!}
self.tableView.reloadData()
self.refreshControl.endRefreshing()
}
}
}
})
}
} }
}
}
)
}
Thanks in advance.

I ended up deciding to fetch the user info AFTER fetching all topics and then looping through the topics array and attaching user info for each topic.
EXAMPLE:
1 - fetch all topics and create array
2 - for each topic in array{
fetch user on Firebase using a property like topic.email
set something like:
topic.imageUrl = user.imageUrl
topic.username = user.username
}
reload table view

Related

Showing Manually Entered Data Based on TableView Click Using Firebase

I have an app where I have a list of data that I manually enter into Firebase. Here is an image of the Firebase Database:
When the user selects an index row, it goes into another view controller. Inside that view controller there is a tableview, which I want to populate with the data from the “name” that I selected.
For example, if I select the “name” from [acct0], I want the “name” and “genre” to also appear in the new tableview.
Currently, I am trying to print the data based on the selection in the previous view controller.
let user = User()
ref?.child("FeaturedAccounts/AccountNames").child(user.name!).observeSingleEvent(of: .value, with: { (snapshot) in
let userDict = snapshot.value as! [String: Any]
let genre = userDict["genre"] as! String
print("Genre: \(genre)")
print(user.name!)
})
Your query looks correct to me. It's hard to say exactly what is going wrong without more information. Here is some code you can use to help debug your issue. If you post some more of your code or explain exactly what is currently happening with your query I can help more.
let user = User()
let ref = Database.database().reference()
ref.child("FeaturedAccounts").child("AccountNames").child(user.name!).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
print(snapshot.value)
}
else {
print("Query returned no data")
}
if let userDict = snapshot.value as? [String: AnyObject] {
if let genre = userDict["genre"] as? String {
print("Genre: \(genre)")
}
else {
print("could not cast genre to string")
}
}
else {
print("Could not cast userDict")
}
})

Unable to node specific values in firebase using swift 3

I am able to fetch all users but I want need user ID specific data.
For example you can see here are lots of users are registered but I need details only for 3 nodes:
323QGP6qryTWs7EnnXRX1stgocP2
iy5ssz0ALphtgViALEOG0N4TeGd2
OlA0rhAVfsNvixe8KEsUmdCfuN42
Please help to get these records.
Thanks.
let ref = Database.database().reference().child("users").observeSingleEvent(of: .value, with : { snapshot in {
if snapshot is NSNull{
//handles errors
}
else{
let dict = snapshot.value as? NSDictionary{
let firstDict = dict["323QGP6qryTWs7EnnXRX1stgocP2"] as? NSDictionary
let secondDict = dict["iy5ssz0ALphtgViALEOG0N4TeGd2"] as? NSDictionary
let thirdDict = dict["OlA0rhAVfsNvixe8KEsUmdCfuN42"] as? NSDictionary
//Then to gather whichever node you want inside these users:
let requestedNode = THEDICTIONARYYOUARELOOKINGAT["THE_NAME_OF_THE_NODE"] as? String //String, Int, Dictionary, array, boolean, ect.
}
}
})

Unable to get keys from firebase database

I have been pondering for the longest time in my student programmer life. I would like to know
I added the keys using autoChildId.
How to get keys from firebase database swift 2? I know how to get from Android using .getKeys()
My best friend, Google, taught me to use allKeys. However, my friendship is on the verge of in despair right now as I received the following msg that our relationship with .allKeys will always fail ( see image below). Haish...
I need this in order to show the data from Firebase Database into my tableview cos I believe this is the issue to a empty table just like how my heart is for my project. No heart.
Here is how my firebase database looks like:
Here is my code:
func findPlaceToEat(){
print("inside findPlaceToEat()")
print("Plan price level")
print(planPriceLevel)
print("End of price level")
ref = FIRDatabase.database().reference()
ref.child("places_detail").child("price_level").child(planPriceLevel).observeEventType(.ChildAdded, withBlock:{
(snapshot) in
if let dictionary = snapshot.value?.allKeys! as? [String: AnyObject]{
let PlaceObj = placeObj(place_name: dictionary["place_name"] as! String, place_type: dictionary["place_type"] as! String, price_range: dictionary["price_range"] as! String, vegan_type:dictionary["vegan_type"] as! String , website: dictionary["website"] as! String)
print("Whatever")
print(PlaceObj);
//self.tableView.reloadData()
}
}, withCancelBlock: nil)
}
to get key from snapshot
snapshot.key
I got a workaround for my project, everyone please pray that my lecturer don't see this. :
What I did was inside the save button I retrieve the value from database and then save it back into Firebase Database.
ref = FIRDatabase.database().reference()
ref.child("hello").child("google! I need a part time job").child(planPriceLevel).observeEventType(FIRDataEventType.ChildAdded, withBlock:{
(snapshot: FIRDataSnapshot) in
if let dictionary = snapshot.value as? [String: AnyObject]{
let getPlaceObj = placeObj()
getPlaceObj.setValuesForKeysWithDictionary(dictionary)
self.PlaceObj.append(getPlaceObj)
print("Name " ,getPlaceObj.place_name)
}
let place_name = snapshot.value?.objectForKey("place_name") as! String
let place_type = snapshot.value?.objectForKey("place_type") as! String
let price_range = snapshot.value?.objectForKey("price_range") as! String
let vegan_type = snapshot.value?.objectForKey("vegan_type") as! String
let website = snapshot.value?.objectForKey("website") as! String
print(place_name, place_type, price_range, vegan_type, website)
let savePlan : [String: AnyObject] = ["place_name":place_name, "place_type":place_type, "price_range":price_range, "vegan_type":vegan_type, "website":website]
self.ref.child("can you place hire me as your intern? I am from Singapore!!!").child(self.user!.uid).childByAutoId().setValue(savePlan)
}, withCancelBlock: nil)
You need to define query orderbykey like bellow:
this.afd.list('/yourItems/', {query:{orderByKey :true}}).subscribe((elements) => {
elements.map(element=>{
console.log(element.$key);
})
});

How to ensure that the data is not retrieved and appended as a whole each time a new entry is added?

func generateDataForRecents() {
if URLArrayStringThisSeason.count == 0 {
self.activityIndicator2.isHidden = false
self.activityIndicator2.startAnimating()
}
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("palettes").queryLimited(toFirst: 100).observe(.value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict as [String:AnyObject]{
let URL = each.value["URL"] as! String
self.URLArrayStringRecents.append(URL)
//print(self.URLArrayString.count)
//print(snapshot)
//let pictureTitle = each.value["title"] as! String
print(self.URLArrayStringRecents.count)
}
}
self.whatsNewCollectionView?.reloadData() //Reloads data after the number and all the URLs are fetched
self.activityIndicator2.stopAnimating()
self.activityIndicator2.isHidden = true
})
}
The following code does a retrieval of data each time the function is called, or when a new data is added.
This is extremely useful when the app is first started up or closed and then restarted. However, when the app is running, whenever a new entry is added, the code seemed to run again and thus appending twice the amount of new data.
For example, when there are already 15 entries identified and then suddenly a new entry is added, the array of the URL would contain 15+16 thus amounting to a total of 31.
How do I make it such that the new data is added to the array instead of adding the entire snapshot in?
You do that by listening for .childAdded events, instead of listening for .value:
var query = databaseRef.child("palettes").queryLimited(toFirst: 100)
query.observe(.childAdded, with: { (snapshot) in
let URL = snapshot.childSnapshot(forPath/: "URL").value as! String
self.URLArrayStringRecents.append(URL)
}
Since you have a limit-query, adding a 101st item means that one item will be removed from the view. So you'll want to handle .childRemoved too:
query.observe(.childRemoved, with: { (snapshot) in
// TODO: remove the item from snapshot.key from the araay
})
I recommend that you spend some time in the relevant documentation on handling child events before continuing.
Please check below method. I have use this method not getting any duplicate entry.
func getallNotes()
{
let firebaseNotesString: String = Firebase_notes.URL
let firebaseNotes = FIRDatabase.database().referenceFromURL(firebaseNotesString).child(email)
firebaseNotes.observeEventType(.Value, withBlock: { snapshot in
if snapshot.childSnapshotForPath("Category").hasChildren()
{
let child = snapshot.children
self.arrNotes = NSMutableArray()
self.arrDictKeys = NSMutableArray()
for itemsz in child
{
let childz = itemsz as! FIRDataSnapshot
let AcqChildKey : String = childz.key
if AcqChildKey == AcqIdGlobal
{
if (childz.hasChildren() == true)
{
let dictChild = childz.value as! NSMutableDictionary
self.arrDictKeys = NSMutableArray(array: dictChild.allKeys)
for i in 0..<self.arrDictKeys.count
{
let _key = self.arrDictKeys.objectAtIndex(i).description()
print(_key)
let dictData : NSMutableDictionary = NSMutableDictionary(dictionary: (dictChild.valueForKey(_key)?.mutableCopy())! as! [NSObject : AnyObject])
dictData.setObject(_key, forKey: "notesId")
self.arrNotes.addObject(dictData)
}
}
}
}
self.tableviewNote.reloadData()
}
})
}
As for the query for removed child,
query.observe(.childRemoved, with: { (snapshot) in
print(snapshot)
let URL = snapshot.childSnapshot(forPath: "URL").value as! String
self.URLArrayStringThisSeason = self.URLArrayStringThisSeason.filter() {$0 != URL}
self.thisSeasonCollectionView.reloadData()
})
it will obtain the URL of the removed child and then update the array accordingly.

How to add one post at a time from Firebase?

I followed a tutorial to get a timeline going. In the tutorial he uses .Value when retrieving data from Firebase. I later learned that it isn't very efficient because it re-downloads everything whenever a post is added. This causes all user's timelines to flash and auto-scroll.
I'm trying to convert this to use .childAdded but can't figure out how to get it to work. This is the current code:
DataService.ds.REF_POSTS.child("\(self.loggedInUser!.uid)").observeEventType(.Value, withBlock: { postDictionary in
if postDictionary.exists() {
if let snapshots = postDictionary.children.allObjects as? [FIRDataSnapshot] {
self.posts = []
for snap in snapshots {
if let postDict = snap.value as? NSDictionary {
for(name, value) in postDict {
let interval = postDict.objectForKey("timePosted") as! Double
let formattedDate = NSDate(timeIntervalSince1970: interval)
let timeAgo = self.getDate(formattedDate)
if name as! String == "postedBy" {
DataService.ds.REF_USERS.child(value as! String).observeSingleEventOfType(.Value, withBlock: { (userDictionary) in
let userDict = userDictionary.value as! NSDictionary
let username = userDict.objectForKey("username")!
let profileThumbUrl = userDict.objectForKey("profileThumbUrl")!
let key = snap.key
let post = Post(postKey: key, dictionary: postDict, username: username as! String, profileThumbUrl: profileThumbUrl as! String, timeAgo: timeAgo)
self.posts.append(post)
self.collectionView?.reloadData()
})
}
}
}
}
}
}
})
The 2nd Firebase call is just grabbing user info for the user thumbnail and username. When I change .Value at the top to .ChildAdded, nothing shows up on the timeline. I have tried messing around with filetypes etc. and can't get it to work.
Update
Okay so after reading another Firebase question on here it looks like .ChildAdded gets just the child added. I was thinking it got the same data, just that the whole thing would run when a child was added.
So it looks like I'm going to have to change the loops here and run a loop adding each child added one by one. Going to redo the whole thing and will post the difference when I finish it if I can get it working.

Resources