Load data from firebase to a label? - swift

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)")
}
})

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}")

Order tableviewcell by child timestamp

I tried with this code to sort my posts by timestamp it doesn't work, each time I launch the simulator the order of the cells is different, I suppose this isn't the way to do it, could somebody explain me where I am wrong...
I edited the code, now my problem is that the most recent posts are displayed at the bottom and I would like them to to be displayed at the top
self.user.removeAll()
for child in DataSnapshot.children.allObjects as! [DataSnapshot] {
print("Processing user \(child.key)")
let value = child.value as? NSDictionary
//if country == "UNITED STATES"{
if let uid = value?["userID"] as? String{
if uid != Auth.auth().currentUser!.uid {
//
let userToShow = User()
if let fullName = value?["username"] as? String , let imagePath = value?["photoURL"] as? String{
userToShow.username = fullName
userToShow.imagePath = imagePath
userToShow.userID = uid
self.user.append(userToShow)
}
}
}
}
As soon as you call DataSnapshot.value, you're converting the data in the snapshot into a dictionary. And the order if keys in that dictionary is not guaranteed.
To maintain the order of the elements as they come back from the database, you need to loop over DataSnapshot.children. See these questions for examples of how to do that:
Iterate over snapshot children in Firebase
post on the firebase-talk mailing list
For your code this would look something like:
ref.child("users").queryOrdered(byChild: "timestamp").observeSingleEvent(of: .value, with: { snapshot in
self.user.removeAll()
for child in snapshot.children.allObjects as [DataSnapshot] {
print("Processing user \(child.key)")
let value = child.value as? NSDictionary
if let uid = value["userID"] as? String {
...
}
}
self.tableview.reloadData()
})

retrieving data from auto id database child

I'm kinda new on swift programming, my case is:
I have a database on firebase realtime "see picture", I want to retrieve the firstName and lastName data of a specific ID only.
for example:
When i'm at login screen and logged in using this email: "ali_y_k#hotmail.com", and when going to the next screen i want to display the firstName and lastName of this email in the text fields showing on picture
I have tried several solutions but the problem always was I can't enter the random IDs child to fetch the firstName and lastName
there is what i tried:
First
func retriveInfo () {
let databaseRef = Database.database().reference().child("User_Informations")
databaseRef.observe(.childAdded) { (snapshot) in
let snapshotValue = snapshot.value as! Dictionary<String,String>
let firstName = snapshotValue["firstName"]!
let lastName = snapshotValue["lastName"]!
let email = snapshotValue["email"]!
print(firstName,lastName,email)
}
}
This is printing all data (firstName,lastName,email) from every id
Second
func retriveInfo() {
let databaseRef = Database.database().reference().child("User_Informations")
databaseRef.observeSingleEvent(of: .value, with: { (snapshot) in
for snap in snapshot.children {
let userSnap = snap as! DataSnapshot
let uid = userSnap.key //the uid of each user
let userDict = userSnap.value as! [String:AnyObject] //child data
let firstName = userDict["firstName"] as! String
let lastName = userDict["lastName"] as! String
print("key = \(uid) First Name = \(firstName), Last Name = \(lastName)")
}
})
This will print every key "Id" and all the info
Thank You in advance :)
Since you have childByAutoId you have to use query ordered and query equal.
let reference = Database.database().reference().child("User_Informations").queryOrdered(byChild: "email")
reference.queryEqual(toValue: "ali_y_k#hotmail.com").observeSingleEvent(of: .childAdded) { (snapshot) in
let dictionary = snapshot.value as! [String : Any]
let firstName = dictionary["firstName"]
print(firstName)
}
You need to use the current user id after you login
let currentUserUid = FIRAuth.auth()!.currentUser!.uid
let databaseRef = Database.database().reference().child("User_Informations/\(currentUserUid)")
databaseRef.observeSingleEvent(of: .value, with: { (snapshot) in
}

reading data on firebase

This is my data structure.
This is how I load club data and its address.
func loadClubs() {
ref = Database.database().reference()
ref.child("club").observe(DataEventType.childAdded, with: { (clubSnapshot) in
if let clubDict = clubSnapshot.value as? [String : AnyObject] {
let name = clubDict["name"] as! String
let explanation = clubDict["explanation"] as! String
let courtNum = clubDict["courtNum"] as! Int
let membershipFee = clubDict["membershipFee"] as! Int
let visitorFee = clubDict["visitorFee"] as! Int
let hasCarParking = clubDict["hasCarParking"] as! Bool
let club2 = Club2(name: name, explanation: explanation, courtNum: courtNum, membershipFee: membershipFee, visitorFee: visitorFee, hasCarParking: hasCarParking)
self.club2Array.append(club2) // Add to clubArray
print(self.club2Array)
self.tableView.reloadData()
}
let addressRef = Database.database().reference()
addressRef.child("address").child(clubSnapshot.key).observe(DataEventType.childAdded, with: { (addressSnapshot) in
if let addressDict = addressSnapshot.value as? [String: AnyObject] {
let clubAddress = ClubAddress(postCode: addressDict["postcode"] as! String, cityName: addressDict["city"] as! String, ward: addressDict["ward"] as! String, address1: addressDict["address1"] as! String, address2: addressDict["address2"] as! String)
self.addressArray.append(clubAddress)
print(self.addressArray)
}
})
})
}
basically, after retrieving each snapshot of club, I get club's key (-KsJB9TkoGNIkiZFGg7), then use that key to retrieve address.
However, print(self.addressArray) doesn't not print anything.
I add a debug breakpoint at if let addressDict = addressSnapshot.value as? [String: AnyObject] { , it does not stop the debug process.
Why is it not calling?
What do I miss here?
Ah! Your code is very close.
Keep in mind that .childAdded iterates over all of the child nodes and loads each one.
In your case, you don't want to iterate over all of the address nodes, you just want one, and don't want to leave an observer.
To do that, we load the specific node child data of the address node, by observeSingleEvent(of: .value. Here's a snippet of the important part.
let addressRef = Database.database().reference()
addressRef.child("address").child(clubSnapshot.key)
.observeSingleEvent(of: .value, with: { (addressSnapshot) in
let dict = addressSnapshot.value as! [String: Any]
let address = dict["address1"] as! String
print(address)

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.
}