Firebase and reading nested data using Swift - 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..

Related

Load data from firebase to a label?

I have data in firebase that I would like to load into a UILabel with Swift.
My data structure looks like:
like-1bf89addclose
artists
-LP6zVO8iekRMMOWe7nm
artistGenre: pop
artistName: postmalone
id: 920930
And my swift code looks like:
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
refHandle = ref.observe(FIRDataEventType.value, with: {(snapshot) in
let dataDict = snapshot.value as! [String: AnyObject]
print(dataDict)
})
ref.child("artists").observeSingleEventOfType(.value, with: {(snapshot) in
let artist = snapshot.value!["artistName"] as! String
let genre = snapshot.value!["artistGenre"] as! String
self.artistlLabel.text = artist
self.genreLabel.text = genre
})
}
where's my mistake? I've tried searching online, but most examples only explain how to put input into tableviews, which has a different code I tried to understand and restructure but couldn't. I know there has to be something wrong with my ref but I can't figure it out.
I'm following a youtube tutorial and this works:
let userID: String = (FIRAuth.auth()?.currentUser?.uid)!
ref.child("Users").child(userID).observeSingleEventOfType(.value, with: {(snapshot) in
let email = snapshot.value!["Email"] as! String
let password = snapshot.value!["Password"] as! String
self.emailLabel.text = email
self.passwordLabel.text = password
})
**issue with this code is I don't need that authentication part (no users have to log in on my app, they're just inputing info in).
You're missing the -LP6zVO8iekRMMOWe7nm level in your reference. Try this:
ref.child("artists/-LP6zVO8iekRMMOWe7nm").observeSingleEventOfType(.value, with: {(snapshot) in
let artist = snapshot.value!["artistName"] as! String
let genre = snapshot.value!["artistGenre"] as! String
print("\(artist) \(genre)")
self.artistlLabel.text = artist
self.genreLabel.text = genre
})
If you want to load all artists, you can load /artists and then loop over the results:
ref.child("artists/-LP6zVO8iekRMMOWe7nm").observeSingleEventOfType(.value, with: {(snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
let artist = child.value!["artistName"] as! String
let genre = child.value!["artistGenre"] as! String
print("\(artist) \(genre)")
}
})

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 swift - Could not cast value of type 'FIRUserInfoImpl' to 'NSDictionary'

I have a facebook and twitter login Button. I follow the user guide. I would like to store the user information however I receive this error Could not cast value of type 'FIRUserInfoImpl' to 'NSDictionary'
I am not sure why I cannot store the relevant data. Is that because the provider data did not match the object type?
func createFirebaseUser(){
//MARK: Assign what to get from current User data.
let key = ref.child("users").childByAutoId().key
if let user = FIRAuth.auth()?.currentUser{
for profile in user.providerData {
let userID = profile.uid as! String!
let username = profile.displayName as! String!
let email = profile.email as! String!
let profilePicUrl = profile.photoURL as! NSURL!
ref.child("users").updateChildValues(profile as? NSDictionary as! [NSObject : AnyObject])
}
}
You should create a dictionnary to send data with updateChildValues.

Could not cast value of type '__NSDictionaryM' to custom User Class using new Firebase

I'm attempting to query for a specific user using the new Firebase like so:
DataService.sharedInstance.REF_USERS.queryOrderedByChild("username").queryEqualToValue(field.text).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if let userDoesNotExist = snapshot.value as? NSNull {
print("No User found!")
} else {
let theUsr = snapshot.value as! User
print(theUsr)
}
}, withCancelBlock: { (error) in
// Error
})
From there I was looking to store the snapshot into its own object and access its values from there. I was attempting to do so by doing this:
class User {
let key: String!
let ref: FIRDatabaseReference?
var username: String!
var email: String!
// Initialize from data snapshot
init(snapshot: FIRDataSnapshot) {
key = snapshot.key
email = snapshot.value!["email"] as! String
username = snapshot.value!["username"] as! String
ref = snapshot.ref
}
func toAnyObject() -> AnyObject {
return [
"email": email,
"username": username,
]
}
}
The problem I'm running into is a crash with the following:
Could not cast value of type '__NSDictionaryM' (0x10239efc0) to 'Scenes.User' (0x100807bf0).
How do I fix this? I don't remember running into this problem on the old Firebase. I need access to the snapshot.value's key and for some reason can't access it by using snapshot.value.key without getting a crash so I figure I'd try passing all the data into it's own object.
snapshot.value isn't a User, it's a dictionary (*)
Here's a couple of options - there are many.
let aUser = User()
aUser.initWithSnapshot(snapshot)
then, within the User class, map the snapshot.values to the user properties
or
let aUser = User()
aUser.user_name = snapshot.value["user_name"] as! String
aUser.gender = snapshot.value["gender"] as! String
Oh, and don't forget that by using .Value your snapshot may return multiple child nodes so those will need to be iterated over
for child in snapshot.children {
let whatever = child.value["whatever"] as! String
}
In your case it's just one user so it's fine they way you have it.
*It could be
NSDictionary
NSArray
NSNumber (also includes booleans)
NSString