In my app I have a simple user base that looks like this:
What I'm trying to do is to simply fetch this list once, to check wether a username is valid when a new user signs up with a new username.
The thing is that the only ways I found to retrieve data utilize some sort of observer methods, which are not good for me.
The logic I'm trying to achieve (with the retrieving method that doesn't work) :
// When user tries to sign up with a new username
let username = nicknameField.text?.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
self.usersRef.observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
let dict = snapshot.value as! NSDictionary
for val in dict.allValues {
if username == val as! String {
// Present alert
return
}
}
}
self.usersRef.child(username).setValue(username) { (error, dbRef) in
if error == nil {
// Continue
}
}
How can I simply just fetch the list of users once?
Thanks in advance!
I had to change the observeEventType method to observeSignleEventOfType.
I have also updated my code to make it work (regardless):
self.usersRef.observeSingleEventOfType(.Value) { (snapshot: FIRDataSnapshot) in
let dict = snapshot.value as! NSDictionary
for val in dict.allValues {
if username == val as! String {
// Present alert
return
}
else {
self.usersRef.child(username).setValue(username) { (error, dbRef) in
if error == nil {
// Continue
}
}
}
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've Firebase Database where each user has own email and username. How to check unique username? I tried to make it like this, but my code doesn't work properly therefore different users can have the same username
usernameField.isHidden = false
let username:String = self.usernameField.text!
if (usernameField.text?.isEmpty == false){
ref.child("users").queryOrdered(byChild("username").queryEqual(toValue: username).observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists(){
print("username exist")
}else{
ref.root.child("users").child(userID).updateChildValues(["username": username])
}
})
}
I'm a little bit newbie in Firebase I store email and username for each user like this newUserReference.setValue(["username":String(), "email" : self.emailTextField.text!]). On next view, user can type username in usernameField.text and this value will be added in Firebase Database. But if the next user (user 2) will type the same username like previous user, it must be blocked, because username should be unique
You still need to indicate what property you want to order/filter on with queryOrdered(byChild:):
if (usernameField.text?.isEmpty == false){
ref.child("users").queryOrdered(byChild:"username").queryEqual(toValue: username).observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists(){
If you're trying to store your user's id on login do this when you receive a successful response to the login:
create a Shared Instance to store the ID
class userDataSource {
var id : String? // variable to store your users ID
static let sharedInstance = PageDataSource() // global access to this dataSource
private init() {}
}
Assign the id a value after successful login
func getIDFromLogin() {
if let user = Auth.auth().currentUser {
print(user.uid)
userDataSource.sharedInstance.id = user.uid
}
}
Then you can do this to view each id:
ref.child("users").observeSingleEvent(of: .value, with: { snapshot in
if let snapshots = snapshot.children.allObjects as? [DataSnapshot] {
for snap in snapshots {
print(snap.key) // you can compare userDataSource.sharedInstance.id to this value
}
}
})
Or if you just want that user's data do this:
ref.child("users").child(userDataSource.sharedInstance.id!).observeSingleEvent(of: .value, with: { snapshot in
if let snapshots = snapshot.children.allObjects as? [DataSnapshot] {
for snap in snapshots {
print(snap)
}
}
})
Edit to answer your question more accurately
Here is an answer more inline with your question. First thing I will recommend is for you to add a table to Firebase that only contains the usernames, and the .uid's that they belong to. You will need to first read through that table to make sure that no one else has that username, then update the table accordingly:
// This function will check through all of the usernames and return a true or false value in the completion handler
func checkUsernames(_ completion: #escaping(_ success: Bool) -> Void) {
ref.child("usernames").observeSingleEvent(of: .value, with: { snapshot in
if let snapshots = snapshot.children.allObjects as? [DataSnapshot] {
for snap in snapshots {
if snap.value == username {
completion(false)
}
}
completion(true)
} else {
completion(true) // TODO: check for errors before setting completion
}
})
}
// this function will set the username values in Firebase
func storeUsername() {
let usernameRef = ref.child("usernames")
usernameRef.updateChildValues(["\(userDataSource.sharedInstance.id!)" : username])
}
}
}
Assuming you have already handled your username variable and set it's value, you will call the functions like this:
checkUsernames({ (success) in
if success {
storeUsername()
// you may also want to update your "users" table here as well
} else { print("Duplicate Username") } // handle alert or something here
})
I'm using this function to check to see if a certain handle exists in my database. It works fine for the most part - if the handle exists, it updates the table view to display that user. However if there is no match for the handle entered, my alert view is not showing up.
// Search DB for matching handles
#IBAction func searchHandleButtonPressed(_ sender: Any) {
if let handleToSearch = handleSearchTextField.text?.lowercased() {
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("users").queryOrdered(byChild: "lowercaseHandle").queryEqual(toValue: handleToSearch).observeSingleEvent(of: .value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject] {
for each in snapDict{
let key = each.key
let handle = each.value["handle"] as! String
self.returnedHandles.removeAll()
self.returnedHandles.append(handle)
let pic = each.value["profilePicture"] as! String
self.returnedUsersProfilePic = pic
self.returnedUsersUID = key
if handle.lowercased() != handleToSearch {
self.noHandleFoundAlert()
}
if handle != "" {
DispatchQueue.main.async {
self.getFriendsInfo()
self.tableView.reloadData()
}
}
}
}
}, withCancel: {(Err) in
// print(Err.localizedDescription)
})
}
}
I put the alert in the loop as:
if handle.lowercased() != handleToSearch {
self.noHandleFoundAlert()
}
But obviously this isn't the correct approach as it isn't working. If I enter a random string of characters, or a handle that I know isn't in use, the alert doesn't come up. Where else would I put the alert so it will show up if there's no match?
The snapshot will be converted to an empty dictionary with your current code.
Before converting the snapshot.value to a Dictionary, check if it exists with snapshot.exists(): https://firebase.google.com/docs/reference/ios/firebasedatabase/api/reference/Classes/FIRDataSnapshot#-exists
You first have to check if the query you made, has data. So you have a
.hasChild("handle")
it returns a boolean, so when is true you do the loop, else notified user.
I'm having trouble updating a value in my Firebase database with the key status. I just can't figure out how to access the child(id) in which it is an autoID. Should I have an attribute in my Swift 3 Entity? Or is there a child(autoID) or something similar? I'm new in Firebase and Swift 3. Can any one help? This is my code:
self.ref?.child("requests").queryOrdered(byChild: "Sender").queryEqual(toValue: self.items[indexPath.row].Sender).setValue("2", forKey: "status"))
Swift 3 && Firebase 3
Hope this code will be help you...
// create the reference you want to observe
let myRequestRef = FIRDatabase.database().reference().child("requests").queryOrdered(byChild: "Sender").queryEqual(toValue: self.items[indexPath.row].Sender)
// check the sender value
myRequestRef.observeSingleEvent(of: .value, with: { (snapshot) in
//get the sender key
let senderKey = snapshot.key as String
// create status ref for new status value
let statusRef = FIRDatabase.database().reference().child("requests").child(senderKey)
// create new dict for status value
let newValue = ["Status": 2] as [String: Any]
statusRef.updateChildValues(newValue, withCompletionBlock: { (error, _) in
if error != nil {
print(error?.localizedDescription ?? "Failed to set status value")
}
print("Successfully set status value")
// Update your UI
DispatchQueue.main.async {
// Do anything with your UI
}
})
}) { (error) in
print("Failed to get snapshot", error)
}
Try moving your code inside .observe. This will allow you to get the status and check that it's accessing the right child. By using the child path below, it will override the status value but not the rest of the values inside the child.
self.ref?.child("requests").queryOrdered(byChild: "Sender").queryEqual(toValue: self.items[indexPath.row].Sender).observe(.value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
if value != nil {
print(value)
print(value["status"])
// If status is what you want, and prints out the correct status and
// correct child item
// Update the status
self.ref.child("requests/\(self.items[indexPath.row].Sender)/status").setValue("2")
}
})
I haven't tested the code above, but you should be able to make minor tweaks to get it to work in your code and on your firebase database.
You can update status value as below:
self.ref?.child("requests").queryOrdered(byChild: "Sender").queryEqual(toValue: self.items[indexPath.row].Sender).observe(.value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
if value != nil {
self.ref.child("requests/(self.items[indexPath.row].Sender)").updateChildValues(["status":"2"])
}
})
In my iOS app, a user is able to add friends by searching for there unique username.
The user types the username in a textField and I have a textFieldDidChange notification which is fired every time the text changes.
Within that method I then call the Firebase method below to check if the username exists.
func searchFor(_ username: String) {
guard let uid = FIRAuth.auth()?.currentUser?.uid else {
return
}
let lowercaseUsername = username.lowercased()
let ref = FIRDatabase.database().reference()
ref.child(FirebaseDatabaseBranchNames.usernames.rawValue).child(lowercaseUsername).observeSingleEvent(of: .value, with: { [unowned self](snapshot) in
if snapshot.exists() {
if let usernameUid = snapshot.value as? String {
self.isUserAlreadyAFriend(ref, uid: uid, usernameUid: usernameUid)
}
} else {
// username doesn't exist
}
}, withCancel: nil)
}
How can I cancel this method, before performing it again?
When you attach a listener/observer, Firebase returns a handle for that observer. You can subsequently remove the listener/observer by calling ref.removeObserverWithHandle().
So assuming you want at most one observer, you can keep the reference and observer handle in a member field of you class and then use this code in the searchFor method:
if (self.searchHandle != nil) {
self.searchRef.removeObserverWithHandle(searchHandle)
}
self.searchRef = ref.child(FirebaseDatabaseBranchNames.usernames.rawValue).child(lowercaseUsername)
self.searchHandle = self.searchRef.observeSingleEvent(of: .value, with: { [unowned self](snapshot) in
if snapshot.exists() {
if let usernameUid = snapshot.value as? String {
self.isUserAlreadyAFriend(ref, uid: uid, usernameUid: usernameUid)
}
} else {
// username doesn't exist
}
}, withCancel: nil)
Be aware that you won't be saving data transfer with this though, as the most likely result is that the database client simply drops the data that it gets back from the server.
Better you add the "removeAllObservers()" after the observe single event block. It is working for me.
let ref = Database.database().reference().ref.child(XXXX).child(YYYYY)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
}else{
}
}) { (error) in
print(error.localizedDescription)
}
ref.removeAllObservers()