Retrieve data stored by childByAutoID in firebase - swift

In firebase i'm trying to retrieve data stored in the firebase realtime database by using the childByAutoID() to give the data a unique identifier but the problem is i'm not entirely sure how to achieve this?
func retData(){
rootRef.child("users").child(userID).ChildByAutoID().observeEventType(.Value){
(snap: FIRDataSnapshot) in
self.simpleLabel.text = snap.value?.description
}
}
Json data structure
-userID
-childByAutoID
-player1
-email

If your JSON structure is:-
-users
-childByAutoID
-playerID
-email
Use :-
rootRef.child("users").observeEventType(.Value, withBlock: {(snap) in
if let userDict = snap.value as? [String:AnyObject]{
for each in userDict as [String: AnyObject] {
let autoID = each.0
//Here you retrieve your autoID
rootRef.child("users").child(autoID).child("player1").observeEventType(.Value, withBlock: {(playersDict) in
if let playerDictionary = playerDict.value as? [String:AnyObject]{
let emailID = playerDictionary["email"] as! String
//print(emailID)
}
})
}
}
})

Related

Not able to read data from Firebase realtime database

I have stored the comments under a post in firebase realtime database. The problem i have is that when i try to parse out the data from firebase i get an error that says Unexpectedly found nil while unwrapping an Optional value. So for example if i try to pront the data stored under degree, i get this nil error. But when i print "comments" instead of the "degree" i successfully fetch the data. My database structure looks like this.
func obeserveComments() {
// get auto-id of post
let commentKey = self.keyFound
let postRef = Database.database().reference().child("posts").child(commentKey)
var tempComments = [Comments]()
postRef.observe(.value, with: {(snapshot) in
if let dict = snapshot.value as? [String:Any] {
if let comments = dict["comments"] as? [String:Any] {
let degree = comments["reply degree"] as! String
// let name = comments["reply name"] as! String
// let text = comments["reply text"] as! String
// let university = comments["reply university"] as! String
// let photoURL = comments["reply url"] as! String
// let url = URL(string: photoURL)
// let timestamp = comments["timestamp"] as! Double
print(degree)
}
}
})
}
The answer by #aytroncb is a good answer, I prefer to leave Firebase data 'Firebasy' as long as possible. In other words coverting to dictionaries looses ordering and and find code like this
[String: [String: [String: Any]]]
To be very hard to read.
I prefer
let snap = snapshot.childSnapshot("comments") //snap becomes a DataSnapshot
So my solution maintains the order and leverages .childSnapshot to leave data in it's DataSnapshot form.
func readPostComments() {
let postRef = self.ref.child("posts") //self.ref points to my firebase
postRef.observeSingleEvent(of: .value, with: { snapshot in
let allPosts = snapshot.children.allObjects as! [DataSnapshot]
for postSnap in allPosts {
print("postId: \(postSnap.key)")
let commentsSnap = postSnap.childSnapshot(forPath: "comments") //will be a DataSnapshot
let allComments = commentsSnap.children.allObjects as! [DataSnapshot]
for commentSnap in allComments {
print(" commentId: \(commentSnap.key)")
let replyDegree = commentSnap.childSnapshot(forPath: "reply_degree").value as? String ?? "No Degree"
let replyName = commentSnap.childSnapshot(forPath: "reply_name").value as? String ?? "No Name"
print(" degree: \(replyDegree) by: \(replyName)")
}
}
})
}
EDIT
For a single post, remove the top part of the code that reads in and iterates over all posts.
func readCommentsForOnePost() {
let postRef = self.ref.child("posts")
let postCommentRef = postRef.child("post_0")
postCommentRef.observeSingleEvent(of: .value, with: { snapshot in
print("postId: \(snapshot.key)")
let commentsSnap = snapshot.childSnapshot(forPath: "comments") //will be a DataSnapshot
let allComments = commentsSnap.children.allObjects as! [DataSnapshot]
for commentSnap in allComments {
print(" commentId: \(commentSnap.key)")
let replyDegree = commentSnap.childSnapshot(forPath: "reply_degree").value as? String ?? "No Degree"
let replyName = commentSnap.childSnapshot(forPath: "reply_name").value as? String ?? "No Name"
print(" degree: \(replyDegree) by: \(replyName)")
}
})
}
Its because firebase is returning your data like this
{
"MAKFW244kdL)Cw;1": [Array of data],
"LOPSw!35pa3flAL4": [Array of data],
"ALV34VR4_A6Vn1a": [Array of data]
}
So change your initial casting of snapshot.value to this:
if let dict = snapshot.value as? [String: [String: Any]]
then loop through that new dictionary like this:
for objectJson in dict.values {
if let comments = objectJson["comments"] as? [String: [String: Any]] {
for commentJson in comments.values {
let degree = commentJson["reply_degree"] as? String
}
}
}
Update
Just read through your post again and noticed your trying to access the comments directly with a key, your first going to need to provide the PostId. Then you can use the above code to loop through the objects
let postRef = Database.database().reference().child("posts").child(postID)
alternatively I believe you can have the comments returned as a normal list by doing something like this:
let postRef = Database.database().reference().child("posts").child("\(postID)/{id}")

swift firebase nested children dictionary delete

firebase structure
In the firebase structure you can see i have to delete specific user (currentUserId) in all the groups:
it's what i try to do:
###########################UPDATED###################################
let groupsRef = self.root.child("groups")
groupsRef.observeSingleEvent(of: .value, with: { snapshot in
for groupChild in snapshot.children {
let groupSnap = groupChild as! DataSnapshot
var dict = groupSnap.value as! [String: Any]
let uid = dict["utenti"] as! [String: Bool]
for each in uid {
if each.key == self.currentUserID{
print(each.key)
//i now need a way to remove this key:value
}
}
}
})
I'm new so i'm not able to go further in extracting every key of ditcionary, than i will compare to the one i have to delete and if it's the same i will delete.
Can someone help?
let groupsRef = self.root.child("groups")
groupsRef.observeSingleEvent(of: .value, with: { snapshot in
for groupChild in snapshot.children {
let groupSnap = groupChild as! DataSnapshot
let groupKey = groupSnap.key
//added a groupKey to track the id of each group
var dict = groupSnap.value as! [String: Any]
var uid = dict["utenti"] as! [String: Bool]
//then for each key:value in uid check if is there a key = to currentuserID
for each in uid {
if each.key == self.currentUserID{
uid.removeValue(forKey: each.key)
//here you remove currentuserId from the dictionary and below
//finally you set back the new value of the dictionary without currentuserId
self.root.child("groups").child(groupKey).child("utenti").setValue(uid)
}
}
}
})
you can use
for (key, value) in uid {
}
to loop over a dictionary.
But really, looking in the official documentation of swift would give you the right answer...
let groupsRef = self.root.child("groups")
groupsRef.observeSingleEvent(of: .value, with: { snapshot in
for groupChild in snapshot.children {
let groupSnap = groupChild as! DataSnapshot
for subGroupChild in groupSnap.children {
//Here you need to remove that specific user if condition match
//specificUserUID is that user's id that user to be deleted
if let snapref = subGroupSnap as! DatabaseReference {
snapref.queryOrdered(byChild: "utenti").queryEqual(toValue: specificUserUID).observe(.childAdded, with: { (snapshot) in
snapshot.removeValue(completionBlock: { (error, reference) in
if error != nil {
print("There has been an error:\(error)")
}
})
})
}
}
}
})
Your code will go like above you just need find thaat user by user ID and delete that particular snapshot.

Firebase Swift query and client side fan out

I have been on this issue for over three days, i have research and came across other similar questions on SO which relates to my issue but those fix could not solve mine hence the reason am asking this question.
I have a users, posts and users-posts node in firebase as shown below. I want to run a query on the node such that if two users are friends they can see each others post. But if they are not friends they cannot see each others posts
Users
123840ajldkjfas0d9
username: Joe
friend
78983049802930laks: true
78983049802930laks: true
4563049802930laks
username: Ken
friend
123840ajldkjfas0d9: true
78983049802930laks
username: Pean
friend
123840ajldkjfas0d9: true
posts
876f92fh02hfj02930239
post: This is cool
whoposted: 123840ajldkjfas0d9
39fh938hqw9320923308
post: I love pizza
whoposted: 78983049802930laks
users-posts
123840ajldkjfas0d9
876f92fh02hfj02930239: true
78983049802930laks
39fh938hqw9320923308: true
This is my query currently, it is showing all post for all users whether they are friends or not. Please i need help with this.
DataService.ds.REF_USERS.observe(.value, with: { (userSnapshot) in
if let snapshot = userSnapshot.children.allObjects as?
[FIRDataSnapshot]{
for userSnap in snapshot{
print("snapshot.key: \(userSnap.key)")
let userKey = userSnap.key
if var userDict = userSnap.value as? Dictionary<String,
AnyObject>{
let postUserPicUrl = userDict["profileImgUrl"] as? String
if let firstName = userDict["firstName"] as? String{
("firstName: \(firstName)")
DataService.ds.REF_POST.observeSingleEvent(of: .value, with: {
(postSnapshot) in
if let postSnapshot = postSnapshot.children.allObjects as?
[FIRDataSnapshot]{
for postSnap in postSnapshot{
if var postDict = postSnap.value as? Dictionary<String, AnyObject>{
if let refPostUserKey = postDict["user"] as? String{
if userKey == refPostUserKey{
DataService.ds.REF_BLOCK_USER.observeSingleEvent(of: .value, with: {
(blockUserSnapshot) in
if let blockUserSnapshot = blockUserSnapshot.children.allObjects as?
[FIRDataSnapshot] {
for blockUserSnap in blockUserSnapshot{
if var blockUserDict = blockUserSnap.value as? Dictionary<String,
AnyObject> {
if let user = blockUserDict["user"] as? String{
if firstName != user {
postDict["postUserPicUrl"] = postUserPicUrl as AnyObject?;
let postKey = postSnap.key
let post = Post(postKey: postKey, postData: postDict)
self.posts.append(post)
}
}
}
}
}
self.tableView.reloadData()
})
}
}
}
}
}
self.tableView.reloadData()
})
}
}
}
}
self.tableView.reloadData()
})
}
I mean this with no disrespect, but you are not utilizing these queries well with each nested within another. Also, make sure you update all of your queries. The Post query uses the old formatting while your user query is up to date.
You should create 3 dictionaries to hold the data for each node Users, posts, users-posts as well as a var to hold the current user string and a dictionary to contain the post data:
var users = [String:Any]()
var posts = [String:Any]()
var usersposts = [String:Any]()
var currentUserKey:String!
var visibleposts = [String:Any]()
Then have three separate queries to get the data. Currently it does not appear that you are querying for any specific users so I will do the same:
func getUserData(){
DataService.ds.REF_USERS.observe(.childAdded, with: {snapshot in
let key = snapshot.key
let data = snapshot.value as? [String:Any] ?? [:]
self.users[key] = data
})
}
func getPostsData(){
DataService.ds.REF_POST.observe(.childAdded, with: {snapshot in
let key = snapshot.key
let data = snapshot.value as? [String:Any] ?? [:]
self.posts[key] = data
self.refreshPosts()
})
}
func getUsersPostsData(){
DataService.ds.REF_BLOCK_USERS.observe(.childAdded, with:{snapshot in // I am guessing you have the users posts here?? there doesn't seem to be sample data for blocked users in your OP
let key = snapshot.key
let data = snapshot.value as? [String:Any] ?? [:]
self.usersposts[key] = data
self.refreshPosts()
})
}
Now get the current user before firing off these queries in the view did load and then call each query.
override func viewDidLoad(){
self.currentUserKey = (FIRAuth.auth()?.currentUser?.uid)!
/* you may want to do some error handling here to ensure the user
is actually signed in, for now this will get the key if
they are signed in */
self.getUserData()
self.getPostsData()
self.getUsersPostsData()
// data will be refreshed anytime a child is added
}
func refreshPosts(){
self.validposts = [:]
let validUsers = [String]() // this will hold the valid keys to get posts
validUsers.append(self.currentUserKey)
let currentUserData = users[self.currentUserKey] // filter the current user data to get the friends
// get friends keys
let friendsData = currentUserData["friends"] as? [String:Any] ?? [:]
for key in friendsData.keys {
// add friends posts to the validposts data
validUsers.append(key)
}
// get current users posts:
for (key,value) in self.posts {
let postData = value as? [String:Any] ?? [:]
let whoposted = postData["whoposted"] as? String ?? ""
if validUsers.contains(whoposted){
self.validposts[key] = postData
}
}
// access the self.validposts data in your UI however you have it setup
// The child added queries above will continue to fire off and refresh
// your data when new posts are added.
// I am still not clear what the usersposts data is for so it is omitted here.
}

Storing data from firebase child key value pairs as an array

How do I store key value pairs from firebase into an array? I've tried writing a code to send some data to firebase from a local array and noticed they are stored in such form:
notice how the 'medals' child are stored.
I'm trying to take a snapshot of the array from the 'medals' object and return it as an array in swift.
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("users").child(userID!).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
// Get user medals
self.identities3 = snapshot.value!["medals"] as! [String]
})
I know this is very crude, but would it work?
Thanks! Still pretty new to firebase and learning :)
Try this:
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("users").child(userID!).child("medals").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
// Get user medals
self.identities3 = snapshot.value as! [String]
})
Consider changing your JSON structure to :-
Medals: {
Shield : true,
Tie : true
}
To retrieve:-
Swift 3
let array = [String]()
FIRDatabase.database().reference().child("users")child(FIRAuth.auth()!.currentUser!.uid).child("medals").observeSingleEvent(of: .value, with: {(snap) in
if let snapDict = snap.value as? [String:AnyObject]{
for each in snapDict{
print(each.key)
self.array.append(each.key)
}
}
})
Swift 2
let array = [String]()
FIRDatabase.database().reference().child("users")child(FIRAuth.auth()!.currentUser!.uid).child("medals").observeSingleOfEvent(.Value, withBlock: {(snap) in
if let snapDict = snap.value as? [String:AnyObject]{
for each in snapDict{
print(each.0)
self.array.append(each.0)
}
}
})

Firebase and reading nested data using Swift

I am trying to create a simple Instagram style app using Swift and Firebase and I am running into trouble reading comments for each Image post from Firebase.
A couple questions here:
I have the Posts at the top of the tree and then the keys per image under which have data on the comments added by each user. Is it possible to use the username as the key instead of the key generated by childbyAutoId in this instance ?
How would I read the userComment and UserName and save them into an Array that I can then display in a TableView ?
Any response is much appreciated. Thanks.
var postsCommentsDict : NSMutableDictionary = NSMutableDictionary()
var userNameArray : [String] = [String]()
var userCommentArray : [String] = [String]()
FIRDatabase.database.reference().child("Posts").observeEventType(.Value, withBlock: {(Snapshot) in
if Snapshot.exists(){
let imageD = Snapshot.value
let imageD_ID = imageD.key
//Retrieving the email and image name
let imageName = imageD["userImage"] as! String
let userEmail = imageD["userEmail"] as! String
//Here you are accessing each image ID
//First Question Alternative Completed
//To answer the second Question :-
FIRDatabase.database.reference().child("Posts").child(imageD_ID).child("comments").observeEventType(.Value, withBlock: {(Snapshot) in
if let commentsDictionary = Snapshot.value {
for each in commentsDictionary{
postsCommentsDict.setObject(each["userName"] as! String , forKey : each["userComment"] as! String)
//Saving the userName : UserComment in a dictionary
userNameArray.append(each["userName"] as! String)
userCommentArray.append(each["userComment"] as! String)
//Saving the details in arrays
//Prefer dictionary over Arrays
}
} else {
//no comments to append the arrays
}
})
}
})
Once you are Done Saving the Comments dictionary : How to read it : -
for each in postsCommentsDict as! NSDictionary{
let userNm = each.key
let userComment = each.value
//username and userComment's retrieved
}
Please ignore the Typos, if any!...hope this helps..