Currently having trouble displaying all highscores from my firebase realtime database. At the moment I'm only displaying "live" scores.
I am currently making a score board, where the top 50 scores and their usernames are displayed in a table. This works fine. However I am unsure as to whether I should be using a Real-Time database.
You see, when I do a fresh build on my physical device and create a new user, the user is saved into the Firebase database and when I achieve a new score and push to display highscore users, my new user is rightfully there with their score.
If I log out (but am still within the app) and register with a new user, and then play again (with the new user), and then I click to see the highscores leaderboard again I can now see both of the users I have created, with their scores. GREAT!
I can check the Authentication part of firebase, and sure enough, my users are in there with their email and passwords etc. And I can check their highscores on the Real-Time database. All is well.
However, when I delete the app from my device or even push it into the background, and then do a fresh build, or re-open the app. The authentication details (password, username, email) are still there, but when I open the Real-Time database, there is no data there.
Consequently, when I click to see the highscores leaderboard, there is nothing to show. Is this because I should not be using a realtime database, is it only displaying “live/recent data” and doesn't actually store the data I want permanently. Should I be using SQL or something else?
This is the code to add my highscore to my database......
func addingScoreToFB() {
let currentUser = Auth.auth().currentUser
let highScore = UserDefaults.standard.integer(forKey: highScoreIdentifier)
let recentScore = UserDefaults.standard.integer(forKey: recentScoreIdentifier)
if let userID = currentUser?.uid {
let userDB = Database.database().reference().child("Users").child(userID)
if recentScore > highScore {
let highSDictionary = ["highScore":self.recentScores.last!]
userDB.updateChildValues(highSDictionary as Any as! [AnyHashable : Any], withCompletionBlock: { (err, ref) in // Adding Score
if let err = err {
print(err)
}
})
}
}
}
This is my code to display the scores.....
func retrieveUserData() {
let postsRef = self.ref.child("Users")
let query = postsRef.queryOrdered(byChild: "highScore").queryLimited(toLast: 50)
query.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let name = dict["username"] as! String
let score = dict["highScore"] as! Int
let aScore = ScoreClass(withName: name, andScore: score)
self.scoresArray.insert(aScore, at: 0)
}
self.topScoresTableView.reloadData()
})
}
In summary I would like to be able to see the top 50 scores of every user in the database of all time, but I am currently only seeing the recent ones on a new build.
Related
I am working on my user profile page on my app and I need to retrieve data from Firebase and display the data on my user profile page. I have managed to retrieve the data successfully from the database but the problem is that every time I go to the user profile page, it takes a little time to access the information in the database, so as soon as you go to the user profile page, the page will be empty. How can I avoid this?
Put another way, is there a way to access the data and store before going to the user profile page and then displaying the data that is stored? Here is my code:
// Setup the name label
func setupNameLabel() {
// Access the database and get the current user's name
Database.database().reference().child("Users").child(userID!).child("Name").observeSingleEvent(of: .value) { (snapshot) in
guard let name = snapshot.value as? String else { return }
self.nameLabel.text = name
}
view.addSubview(nameLabel)
}
// Setup the username label
func setupUsernameLabel() {
// Access the database and get the current user's username
Database.database().reference().child("Users").child(userID!).child("Username").observeSingleEvent(of: .value) { (snapshot) in
guard let username = snapshot.value as? String else { return }
self.usernameLabel.text = username
}
view.addSubview(usernameLabel)
}
// Setup the email label
func setupEmailLabel() {
// Access the database and get the current user's email
Database.database().reference().child("Users").child(userID!).child("Email").observeSingleEvent(of: .value) { (snapshot) in
guard let email = snapshot.value as? String else { return }
self.emailLabel.text = email
}
view.addSubview(emailLabel)
}
Try To make a Model and then using Single Method and Get Data with Completion
func getData(forUserID: String, completion: #escaping (Model) -> Swift.Void, error: #escaping (Bool) -> Void) {
}
Check models of Salada cocoapod, please.
https://github.com/1amageek/Salada
Especially the models like Relation, Disposer, Set, Array, File are very beautiful.
I am trying to show my last sent message on my home screen of the chat messenger, from storyboard it looks like :
and the code I used are:
func getAllMsg() {
self.users = []
let fromId = Auth.auth().currentUser!.uid
let ref = Database.database().reference().child("privateMessages").child(fromId)
ref.observeSingleEvent(of: .value) { (snapshot) in
for snap in snapshot.children.allObjects as! [DataSnapshot] {
// retrieving receiver's ID
let chatUserID = snap.key
let ref2 = Database.database().reference().child("users").child(chatUserID)
// to retrieve message ID
ref2.observeSingleEvent(of: .value, with: { (snapshot2) in
let newUser = User(dictionary: snapshot2.value as! [String: AnyObject])
// to get the last message
ref.child(chatUserID).queryLimited(toLast: 1).observeSingleEvent(of: .value, with: { (snapshot3) in
let value = snapshot3.children
while let rest = value.nextObject() as? DataSnapshot {
newUser.lastMessage = (rest.value as! [String: AnyObject])["textMessages"] as? String
self.users.append(newUser)
DispatchQueue.main.async {
self.tableView.reloadData()
}
break
}
})
})
}
}
}
I have done some changes to my database and the above codes works, but after i changed the way i do my database, it doesnt work anymore
previously, my firebase looks like
now my firebase looks like:
i made a chatRoomId by using send and receiver ID. however I am now not able to show my last sent message which was supposed to show on my homescreen. Any ways I can go about this? Or the way I fetched my database is wrong?
Your query is wrong according to your db structure. Also don't perform lastMessage query inside the block of other query because this is totally independent query and not related with any. Below piece of code will work for you.
let ref = kFirDefaultDatabase.reference().child("yourChatRoomId").queryOrdered(byChild: "fromId").queryEqual(toValue: "0YfqnPIOYFYKb8cYZMHnSYti62i2").queryLimited(toLast: 1)
ref.observeSingleEvent(of: DataEventType.value) { (snapshot) in
if snapshot.exists() {
print(snapshot.value)
}
}
This will fetch the last message sent by fromId for the requested chatRoomId. For more detail have a look at Firebase Queries doc.
And if you want to do this in table for all users like in WhatsApp or other chatting application then you will need to make an extra table LastMessages and save last message information here corresponding to each chatRoomId or if possible save this detail somewhere you can fetch with the tableData so that you don't need to query for each chatRoom in a loop.
You can do some better stuff to make it faster. Use CoreData or Sqlite and save/update lastMessage information into local db whenever you send or received any message, where chatRoomId will be a primary key and first get the information from local db and show in the table immediately and mean while you can fetch the data from server and update your local db and refresh the table.
EDIT: For comment to get the last message regardless I send to recipient or recipient send to me.
Remove orderBy query and just use limitToLast. See below code:
let ref = kFirDefaultDatabase.reference().child("yourChatRoomId").queryLimited(toLast: 1)
ref.observeSingleEvent(of: DataEventType.value) { (snapshot) in
if snapshot.exists() {
print(snapshot.value)
}
}
You need to set up rooms differently.
If you you have 3 people in a room, what will the RoomID be ? If someone leaves the room how will you know what the room history is ?
I have two UICollection views on a page that displays data about a Room. It includes photos of the room in one UICollection View and another UICollection View which contains a list of items in that room. There's a link to edit the Room. When a user clicks on the link, they then segue to another view that let's them update it including adding additional photos.
After adding a photo, and hitting submit, in the background the photo is uploaded to Firebase storage and in the Firebase database, the record is updated to include the name of the file that was just uploaded. Meanwhile, the user is segued back to the Room view.
There's a watched on the record of the room in Firebase and when it updates, then the view is refreshed with new data. This is where the problem occurs. It appears, based on a lot of debugging that I've been doing, that the Observe method fires twice and what ends up happening, is the UICollection view that holds the images of the room will show duplicates of the last photo added.
For example, if I add one photo to the room, that photo will appear in the collection 2x. I've attempted to clear the array before the array is updated with the images, and from my analysis, it appears that the array only contains two items, despite showing three in the view. I'm not sure what is happening that would cause this?
Here's a link to the entire file, because I think it might help.
Here's the loadData() method in case this is all that's important:
func loadData() {
self.ref = Database.database().reference()
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
guard let userID = Auth.auth().currentUser?.uid else { return }
let buildingRef = self.ref.child("buildings").child(userID)
buildingRef.keepSynced(true)
buildingRef.child(self.selected_building as String).observe(DataEventType.value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
if ((value) != nil) {
let building_id = value?["id"] as! String
let saved_image = value?["imageName"] as! String
let user_id = userID as! String
let destination = "/images/buildings/\(userID)/\(building_id)/"
let slideShowDictionary = value?["images"] as? NSDictionary
if ((slideShowDictionary) != nil) {
self.slideShowImages = [UIImage]()
self.slideShowCollection.reloadData()
var last_value = ""
slideShowDictionary?.forEach({ (_,value) in
print("are they different? \(last_value != (value as! String))")
if (last_value != value as! String) {
print("count: \(self.slideShowImages.count)")
print("last_value \(last_value)")
print("value \(value)")
last_value = value as! String
CloudStorage.instance.downloadImage(reference: destination, image_key: value as! String, completion: { (image) in
self.slideShowImages.append(image)
self.slideShowCollection.reloadData()
})
}
})
CloudData.instance.getBuildingById(userId: user_id, buildingId: building_id, completion: { (building) in
self.title = building.buildingName as String
self.roomsCollection.reloadData()
})
}
}
})
// User is signed in.
self.getRooms()
}
I am not completely familiar with the Firebase API but if you are having issues with the observation I would suspect the following:
#IBAction func unwindToRoomsVC(segue:UIStoryboardSegue) {
loadData()
}
Triggering loadData a second time looks like it would add a second observation block. As best I can tell the .observe method probably persists the block it is given and triggers it on all changes.
I am developing an iOS app using Swift 3 and Firebase.
In this app I am creating a friend list with this structure:
Friends
User_id
Friend_user_id_1
Friend_user_id_2
I want to get a list of friends for a specific user and this is possible but since I only store ids of friends I am missing Name and Email.
My idea is to do a second query for each user in the friend list to get their personal data. The option to this is to store the Name and Email along the friend_id but that feels cumbersome since the friend might change their Name or Email.
Friends
User_id
Friend_user_id_1
Name
Email
Is it ok making a second query to get user details on each loop or is that a performance killer?
Do I have to save the Name and Email in list too?
Update
This is how I solve it right now
func setupContent() {
let user = FIRAuth.auth()?.currentUser
if user != nil {
DataService.dataService.FRIEND_REF.child((user?.uid)!).observe(.value, with: { snapshot in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
DataService.dataService.USER_REF.child(snap.key).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? Dictionary<String, AnyObject>
let friend = Friend(key: snap.key, dictionary: value!)
self.friends.insert(friend, at: 0)
self.tableView.reloadData()
}) { (error) in
print(error.localizedDescription)
}
}
}
})
}
}
Thankful for all help before I start coding.
I'm creating a social app and a common aspect of it is keeping tabs of the posts that users make. I'm using Firebase as the backend for this and here's my schema
SocialApp
posts
-KGsBG5TPYBtzRivZnbf
users
facebook:10154108240254134
cachedProfile
displayName: "Karthik Kannan"
imageURL: "https://scontent.xx.fbcdn.net/v/t1.0-1/p100x100..."
posts
-KGsBG5TPYBtzRivZnbf: true
provider: "facebook"
When i go ahead and add posts from one device(or simulator) this works as planned but when i delete the app from a device and reinstall it, the posts relationship in the users dictionary disappears. I would like it to persist across devices and keep the relationship intact so if a user decides to use another iDevice to login his posts don't get orphaned.
Here's the code I wrote for adding a post to Firebase and how I currently keep track of the user.
func postToFirebase(imgUrl: String) {
var gift: Dictionary<String, AnyObject> = [
"giftName": giftName.text!,
"giftDescription":giftDescription.text!,
"giftAvailableAt": giftAvailableAt.text!,
"giftPrice": Int(giftPrice.text!)!,
"username": NSUserDefaults.standardUserDefaults().valueForKey("uid")!,
"giftImage":imgUrl,
]
let firebasePost = DataService.ds.REF_POSTS.childByAutoId()
firebasePost.setValue(gift)
firebasePost.observeSingleEventOfType(.Value, withBlock: { snapshot in
if let postID = snapshot.key {
DataService.ds.REF_USER_CURRENT.childByAppendingPath("posts").childByAppendingPath(postID).setValue(true)
}
})
}
This is how I set current users:
var REF_USER_CURRENT:Firebase {
if let uid = NSUserDefaults.standardUserDefaults().valueForKey("uid") as? String {
let user = Firebase(url:"\(BASE_URL)").childByAppendingPath("users").childByAppendingPath(did){
return user!
} else {
return Firebase()
}
}
func createFirebaseUser(uid:String, user:Dictionary<String, AnyObject>) {
REF_USERS.childByAppendingPath(uid).setValue(user)
}
And this is the code in my login controller.
NSUserDefaults.standardUserDefaults().setValue(authData.uid, forKey: "uid")
let user = ["provider":authData.provider!,"displayName":authData.providerData["displayName"]!,"cachedProfile":authData.providerData["cachedUserProfile"]!, "imageURL":authData.providerData["profileImageURL"]!]
DataService.ds.createFirebaseUser(authData.uid, user: user)
self.performSegueWithIdentifier("loggedIn", sender: nil)
This is all the code I've written. I suspect it's something to do with NSUserDefaults. Any help would be greatly appreciated by this beginner.
Check your code. I found a typo here:
var REF_USER_CURRENT:Firebase {
if let uid = NSUserDefaults.standardUserDefaults().valueForKey("uid") as? String {
let user = Firebase(url:"\(BASE_URL)").childByAppendingPath("users").childByAppendingPath(did){
return user!
} else {
return Firebase()
}
}
Instead of "...childByAppendingPath(did)"
You might want to change "did" to "uid"
Other than that, I have not tested out the rest of your code.
Seems like You have watched the Mark Price course, for making this app.
So have I :)