Retrieve firebase data - ios

Here is the firebase data tree
There are two parents and each having two child each. How to retrieve all the data for "sex".
Here's is my code.
ref.child("Doctor").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if let result = snapshot.children.allObjects as? [FIRDataSnapshot] {
for child in result {
print("Here 1")
print(child)
let gender = child.value!["sex"] as? String
print("Here 2")
//print("Sex")
print(gender)
}
} else {
print("no results")
}
}) { (error) in
print(error.localizedDescription)
}
When I am printing the value of gender, it is showing nil value.

You're trying to skip a level in your code. You listen to the value of the root node and then loop over its children. This gets you snapshots of the nodes Msm... and eqn.... If you check inside those nodes, neither of them has a child property sex.
To solve this, add one more loop in your code to get into the push IDs (the keys starting with -K):
ref.child("Doctor").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if let result = snapshot.children.allObjects as? [FIRDataSnapshot] {
for child in result {
for child2 in child.children {
let gender = child2.value!["sex"] as? String
print(gender)
}
}
} else {
print("no results")
}
}) { (error) in
print(error.localizedDescription)
}

Related

How do you check if a firebase snapshot child has value true or false?

JSON
switch
uid
switch : true
uid2
switch : false
What I tried that doesn't work
#objc func didLongPress() {
let databaseRef = Database.database().reference().child("switch").child(self.postID)
databaseRef.queryOrdered(byChild: "switch").queryEqual(toValue: "true").observeSingleEvent(of: .value) { (snapshot) in
print(snapshot)
if snapshot.exists() {
print("Address is in DB")
} else {
print("Address doesn't exist")
}
}}
//////note it picks up self.postID successfully so error is after that. print snapshot gives null
also tried
let databaseRef = Database.database().reference().child("switch").child(self.postID).child("switch/\true")
databaseRef.observeSingleEvent(of: .value) { (snapshot) in
print(snapshot)
if snapshot.exists() {
print("Address is in DB")
} else {
print("Address doesn't exist")
}
}
}
So in theory this should be a really easy, elementary task. The first of the above mentioned attempts should work. There are no permission errors, but console simply produce null as the snapshot print. That is so weird.
If I test a simple snapshot to see what prints with .child(switch).child(self.postID).child(switch) - it prints either 1 for true and 0 for false. So that works.
EDIT: I think this is because it is in an objective c function. I just realized that. I need it in objective C, because if gets called on long press, which uses objective c function
Try:
(Change database child path as per your need.)
Database.database().reference().child("switch").child(self.postID).queryOrdered(byChild: "switch").queryEqual(toValue: true).observeSingleEvent(of: .value, with: { (snapshot) in
if let dict = snapshot.value as? [String: AnyObject] {
print("Address is in DB")
} else {
print("Address doesn't exist")
}
}) { (error) in
print(error.localizedDescription)
}

FirebaseDatabase -How to Paginate when using a UISearchController

As the user types text into the searchBar the UISearchController has a delegate method to update search results:
func updateSearchResults(for searchController: UISearchController) {
guard let searchText = searchController.searchBar.text?.lowercased() else { return }
Database...usersRef
.queryOrdered(byChild: "username")
.queryStarting(atValue: searchText)
.queryEnding(atValue: searchText+"\u{f8ff}")
.observe( .childAdded, with: { [weak self](snapshot) in
let key = snapshot.key
guard let dict = snapshot.value as? [String: Any] else { return }
let user = User(userId: key, dict: dict)
self?.datasource.append(user)
})
}
That works fine.
When I normally paginate I use this procedure:
var startKey: String?
func handlePaginationForPosts() {
if startKey == nil {
Database...PostsRef
.queryOrderedByKey()
.queryLimited(toLast: 10)
.observeSingleEvent(of: .value, with: { [weak self] (snapshot) in
guard let children = snapshot.children.allObjects.first as? DataSnapshot else { return }
if snapshot.childrenCount > 0 {
for child in snapshot.children.allObjects as! [DataSnapshot] {
let postId = child.key
if child.key != self?.startKey {
guard let dict = child.value as? [String:Any] else { return }
let post = Post(postId: postId, dict: dict)
self?.datasource.insert(post, at: 0)
}
}
self?.startKey = children.key
}
})
} else {
let lastIndex = datasource.count
Database...PostsRef
.queryOrderedByKey()
.queryEnding(atValue: startKey!)
.queryLimited(toLast: 11)
.observeSingleEvent(of: .value, with: { [weak self] (snapshot) in
guard let children = snapshot.children.allObjects.first as? DataSnapshot else { return }
if snapshot.childrenCount > 0 {
for child in snapshot.children.allObjects as! [DataSnapshot] {
let postId = child.key
if child.key != self?.startKey {
guard let dict = child.value as? [String:Any] else { return }
let post = Post(postId: postId, dict: dict)
// I run a check to make sure the datasource doesn't contain the post before adding it
self?.datasource.insert(post, at: lastIndex)
}
}
self?.startKey = children.key
}
})
}
}
The problem here is when running a search I use:
.queryStarting(atValue: searchText)
.queryEnding(atValue: searchText+"\u{f8ff}")
But when paginating a post I use:
.queryOrderedByKey()
.queryEnding(atValue: startKey!) ...
self?.datasource.insert(post, at: lastIndex)
The startKey is the first key in the snapshot.children.allObjects.first and the lastIndex is the datasource.count.
Considering the search query is based on the search text and not a key, how can I paginate when I'm already using .queryEnding(atValue: searchText+"\u{f8ff}") instead of .queryEnding(atValue: startKey!)?
I need to track the key that was pulled from the db so that when I paginate I can run the next set of results from that particular key.
Firebase Database queries can only order/filter on a single property.
So what you can do is filter for the search criteria, and then limit to the firsts N results.
What you can't do is filter for the search criteria, skip the first N results, and get the next page.
The closest you can get, and something regularly done for cases such as this, is retrieve the first 2*N results when you need to show page 2. This wastes some bandwidth though, so you'll have to trade that off against how useful the pagination is.

how to delete child record from firebase in swift?

I wanted to delete this record in firebase, where i don't have the name of the key. so, I don't know how to do it. can anybody help me with this?
The code for getting array is as follows
var databaseRefer : DatabaseReference!
let userID = Auth.auth().currentUser?.uid
databaseRefer = Database.database().reference(withPath: userID!)
databaseRefer.observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.exists() { return }
if snapshot.value is NSNull {
print("not found")
} else {
for child in snapshot.children {
let snap = child as! DataSnapshot
print(dict)
dict.forEach { item in
print(item.value)
}
}
}
})
https://firebase.google.com/docs/database/ios/read-and-write
"The simplest way to delete data is to call removeValue on a reference to the location of that data."

Firebase update child values is removing children

I'm trying to handle following and unfollowing in my social media app using Firebase. I have a bar button item entitled "Follow". When tapped, it checks the current follow status(retrieved in viewDidLoad), and calls the follow/unfollow methods accordingly. user represents the owner of the page, and the person the currentUser wants to follow/unfollow.
Unexpected behavior: When following a user a second time, you can watch the proper child nodes in the database appear, then disappear. They should not be disappearing. I have refreshed the page to ensure that the nodes are in fact being deleted somehow. It works properly on the first try after every app launch.
Here is my viewDidLoad(responsible for retrieving currentUserIsFollowing). I suspect the issue lies here:
override func viewDidLoad() {
super.viewDidLoad()
let userDogRef = Database.database().reference().child("users").child(user.uid!).child("dogs")
let followingRef = Database.database().reference().child("users").child((Auth.auth().currentUser?.uid)!).child("following")
followingRef.observeSingleEvent(of: .childAdded) { (snapshot) in
if snapshot.value == nil {
print("no following found")
return
}
let value = snapshot.value as? NSDictionary
let followingUserUID = String(describing: value!["uid"]!)
if self.user.uid == followingUserUID {
self.currentUserIsFollowing = true
DispatchQueue.main.async {
self.followBarButtonItem.title = "Unfollow"
}
}
}
}
Here is the action called when the Follow/Unfollow button is tapped:
#IBAction func followUserButtonPressed(_ sender: Any) {
if !currentUserIsFollowing {
followUser()
return
}
if currentUserIsFollowing {
unfollowUser()
return
}
}
Here is the followUser() method:
fileprivate func followUser() {
let followingRef = Database.database().reference().child("users").child((Auth.auth().currentUser?.uid)!).child("following")
let followersRef = Database.database().reference().child("users").child(user.uid!).child("followers")
followingRef.childByAutoId().updateChildValues(["uid": user.uid as Any]) { (error, ref) in
if error != nil {
print(String(describing: error?.localizedDescription))
}
}
followersRef.childByAutoId().updateChildValues(["uid": Auth.auth().currentUser?.uid as Any]) { (error, ref) in
if error != nil {
print(String(describing: error?.localizedDescription))
}
}
}
Here is the unfollowUser() method:
fileprivate func unfollowUser() {
let followingRef = Database.database().reference().child("users").child((Auth.auth().currentUser?.uid)!).child("following")
let followersRef = Database.database().reference().child("users").child(user.uid!).child("followers")
followingRef.observeSingleEvent(of: .childAdded, with: { (snapshot) in
if snapshot.value == nil {
print("no following found")
}
let value = snapshot.value as? NSDictionary
let followingUserUID = String(describing: value!["uid"]!)
if self.user.uid == followingUserUID {
snapshot.ref.removeValue()
}
})
followersRef.observeSingleEvent(of: .childAdded, with: { (snapshot) in
if snapshot.value == nil {
print("no followers found")
}
let value = snapshot.value as? NSDictionary
let followerUserUID = String(describing: value!["uid"]!)
if Auth.auth().currentUser?.uid == followerUserUID {
snapshot.ref.removeValue()
}
})
}
Here is a photo of my JSON tree:
There's quite a bit going on here to unpack, but I tried my best to follow along and come up with a solution. For one, instead of having two functions, create a single function that handles following and unfollowing:
#IBAction func followUserButtonPressed(_ sender: Any) {
followOrUnfollow()
}
In that function, listen once to the value of the child you need. Instead of using childByAutoId, use the uid as the key and anything as the value. I just used true. This means you can observe the reference directly instead of having to iterate through all the children looking for the one follower. If the child's data is nil, then the user isn't following yet, so the database is updated to follow. If the child's data is not nil, the data is removed.
func followOrUnfollow() {
let followingRef = Database.database().reference().child("users/\(Auth.auth().currentUser?.uid)!/following/\(user.uid!)")
let followersRef = Database.database().reference().child("users/\(user.uid)!/followers/\(Auth.auth().currentUser?.uid)!")
followingRef.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.value == nil {
print("no following found")
followingRef.updateChildValues([user.uid: "true"]) { (error, ref) in
if error != nil {
print(String(describing: error?.localizedDescription))
}
}
} else {
print("unfollowing")
snapshot.ref.removeValue()
}
})
followersRef.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.value == nil {
print("no followers found")
followersRef.updateChildValues([Auth.auth().currentUser?.uid: "true"]) { (error, ref) in
if error != nil {
print(String(describing: error?.localizedDescription))
}
}
} else {
print("unfollowing")
snapshot.ref.removeValue()
}
})
}
Now there may be some syntactical errors because I'm working on this blindly, but this is the gist of what I would recommend. You will likely have to tweak this to meet your needs.
I'll select Jen's answer as the correct one, but I want to add my working code. I had to make some changes to implement my vision. You can't compare a snapshot.value to nil, so instead you should use if snapshot.exists(). In order to avoid adding a whole new child at the reference point using ref.updateChildValues(), I used .setValue("true"). This just adds a new key-value pair to the "following" and "followers" nodes at the ref.
func followOrUnfollow() {
let followingRef = Database.database().reference().child("users/\(Auth.auth().currentUser!.uid)/following/\(self.user.uid!)")
let followersRef = Database.database().reference().child("users/\(user.uid!)/followers/\(Auth.auth().currentUser!.uid)")
followingRef.observeSingleEvent(of: .value, with: { (snapshot) in
if !snapshot.exists() {
print("no following found")
followingRef.setValue("true") { (error, ref) in
if error != nil {
print(String(describing: error?.localizedDescription))
}
}
} else {
print("unfollowing")
snapshot.ref.removeValue()
}
})
followersRef.observeSingleEvent(of: .value, with: { (snapshot) in
if !snapshot.exists() {
print("no followers found")
followersRef.setValue("true") { (error, ref) in
if error != nil {
print(String(describing: error?.localizedDescription))
}
DispatchQueue.main.async {
self.followBarButtonItem.title = "Unfollow"
}
}
} else {
print("unfollowing")
snapshot.ref.removeValue()
DispatchQueue.main.async {
self.followBarButtonItem.title = "Follow"
}
}
})
}
Here's a picture of my tree:

Trouble understanding array appending

func EmptySearchBarList(){
self.PersonalSearchesList = []
currentUserFirebaseReference.child("rooms").observeSingleEvent(of: .value) { (snapshot: FIRDataSnapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
print("snap")
print(self.PersonalSearchesList.count) // Calling 0 every single time
DataService.ds.REF_INTERESTS.child(interestKey).observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
if snapshot.value != nil {
if let users = (snapshot.value! as? NSDictionary)?["users"] as? Dictionary<String,AnyObject> {
DataService.ds.REF_USERS.child(topUser).child("pictureURL").observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
self.PersonalSearchesList.append(eachSearch)
print("first one")
print(self.PersonalSearchesList.count)
})
}
}
})
}
print("Second one")
print(self.PersonalSearchesList.count)
print("Calling to set my sorted PersonalSearchesList")
self.mySortedList = self.PersonalSearchesList.sorted{ $0.users > $1.users }
}
}
initialLoadOver = true
}
What code I'm trying to ultimately run is this :
var mySortedList = [Interest](){
didSet{
print("this was called")
let myCount = mySortedList.count
print(self.PersonalSearchesList.count)
print(myCount)
self.tableView.reloadData()
}
}
The attempt is to load up my PersonalSearchesList array, and once the snap in snapshots is done running, I'm setting MySortedList equal to PersonalSearchesList and reloading the tableview.
What I don't understand is why the prints are coming out like they are. The snap/ 0's are coming from the top of my for snap in snapshots. It seems like it should instead be snap / 1 , snap / 2, snap / 3.
The code to be called when the snaps are done is correct in the timeline, once the snaps have gone through that code runs. What doesn't make sense is why it's not until after that the items are actually being appended to PersonalSearchesList. Becuase of how everything is Im' setting my filtered array to an empty personal searches array and then afterwards I'm filling it up.
Any ideas here?
edit:
var dispatchGroup = DispatchGroup()
dispatchGroup.enter()
dispatchGroup.leave()
dispatchGroup.notify(queue: DispatchQueue.global(), execute: {
})
DataService.ds.REF_INTERESTS.child(interestKey).observeSingleEvent is running asynchronous so all of those callbacks (where you actually fill you list) will run when they are called from the DataService.
You could use a dispatch group to do what you want.
http://commandshift.co.uk/blog/2014/03/19/using-dispatch-groups-to-wait-for-multiple-web-services/
func EmptySearchBarList(){
var dispatchGroup = DispatchGroup()
self.PersonalSearchesList = []
currentUserFirebaseReference.child("rooms").observeSingleEvent(of: .value) { (snapshot: FIRDataSnapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
dispatchGroup.enter()
single time
DataService.ds.REF_INTERESTS.child(interestKey).observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
if snapshot.value != nil {
if let users = (snapshot.value! as? NSDictionary)?["users"] as? Dictionary<String,AnyObject> {
DataService.ds.REF_USERS.child(topUser).child("pictureURL").observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
self.PersonalSearchesList.append(eachSearch)
print("snap")
print(self.PersonalSearchesList.count)
})
}
}
dispatchGroup.leave()
})
}
dispatchGroup.notify(queue: DispatchQueue.global(), execute: {
print("Second one")
print(self.PersonalSearchesList.count)
print("Calling to set my sorted PersonalSearchesList")
self.mySortedList = self.PersonalSearchesList.sorted{ $0.users > $1.users }
})
}
}
initialLoadOver = true
}
I'm not completely sure you can enter and leave the group several times from the same thread so you might have to wrap the code from enter until the end of the callback in another thread and call it async

Resources