Swift, Firebase. Check if a users, childs value exists - swift

In my game you, first have to login, then you have to choose a team name, which gets stored in my firebase database, under the players UID, and when the player has entered his team name, I want to check if it is already taken, or the player is good to go.
let rootRef = FIRDatabase.database().reference()
rootRef.queryOrderedByChild("teamName").queryEqualToValue("Bob fc").observeEventType(.Value, withBlock: { snapshot in
if (snapshot.value is NSNull) {
print("Name is not in use")
} else {
print("Name is in use")
}
})
My data tree:
{
"users" : {
"pbXvXYOKmJQqwSQZ9IlBykG7x1P2" : {
"teamName" : "Bob fc"
}
}
}
My database rules:
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
The problem is that it doesn't print anything, what am I doing wrong here?

You are querying your root ref. You should query the /users node instead
let rootRef = FIRDatabase.database().reference()
let usersRef = rootRef.childByAppendingPath("users")
usersRef.queryOrderedBy....
You can shorten that up but I used the verbose model for clarity.
As a side note, with Firebase 3.x, the default is to only allow authenticated users to read and write. This is accomplished through Rules in the Realtime Database section.
If you want to test your code without authenticating, change your Rules to this
{
"rules": {
".read": true,
".write": true
}
}
PLEASE NOTE: This opens up your data to ANYONE that wants to read it, but if you are just learning or testing an app it makes it a bit more convenient.

Related

Firebase Realtime Database rules for specific node of structure

Before creating a new user I want to check if creating username property already exists in Firebase Database.
Checking function is:
let databaseRef = Database.database().reference()
databaseRef.child("users").queryOrdered(byChild: "username").queryEqual(toValue: loginRegisterTextField.text).observeSingleEvent(of: .value, with: { (snapshot: DataSnapshot) in
if snapshot.exists() {
print("Login exists")
} else {
print("Login does not exist")
}
})
JSON is:
Rules are for node users:
{
"rules": {
"users" : {
".read": "auth != null",
"$uid" : {
".write": "auth != null && auth.uid == $uid",
}
},
Is it possible to write a rules to check existing of username without a new uid?
There is no way to check for a specific value across a JSON branch in security rules. This has been covered quite a few times before, so I recommend checking some of these search results.
But you can make your query on /users more secure, by only allowing that specific query, and not allowing people to read all of /users. To secure the query you could some something like:
{
"rules": {
"users" : {
".read": "auth != null &&
query.orderByChild == 'username' &&
query.equalTo !== null",
...
This is the first time I've used query.equalTo !== null, so there may be some small mistakes in that part, but the flow should be clear.

I want to search in firebase through user name

I am facing an issue in retrieving a data present in node
Dev->Regis->[userId("hjgsfgsdfghd8ydfsw3r")]->UserInfo->{userName:"", email:""}
I am trying the following query
ref.child(kChild).child(kRegistration).queryOrdered(byChild: kUserInfo).queryEqual(toValue: searchText).observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists() {
}
}
I also tries by making rules but it does make result
"Development": {
"Registration": {
"$user_id": {
"UserInfo": {
".indexOn": ["userName"],
".read": "auth.uid == $user_id"
}
}
}
}
database structure
and ref means Database.refrence()
Please help to sort out this problem.
Thanks.
Assuming searchText is the username value that you want to grab the object for. Firebase requires an orderBy call.
let searchText = //username you wish to fetch
let ref = db.ref('Development/Registration/${userId}/userInfo')
ref.orderByChild('userName').equalTo(searchText).once("value", function(snapshot) {
if(snapshot.exists()){
//whatever you want to do here
}
}

Firebase rules failed: permission_denied

In my app I have vc1 that pushes on vc2. Inside vc2 I pull some data from Firebase.
The problem is when I set my rules to the below values, when vc2 gets pushed on I keep getting failed: permission_denied
{
"rules": {
"sneakers": {
".read": "auth != null || auth.uid === null",
".write": "auth.uid != null"
},
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
If I go back to vc1, change the values to the below, then I get no problems and I get access
{
"rules": {
".read": "auth.uid != null",
".write": "auth.uid != null"
}
}
But here's the thing, once I set them back to the first values, go back to vc1 then push on vc2 I can then still get access to the database. If I delete the app and relaunch it the process repeats itself.
I want to use the first values because I want to keep the data at the user's node safe.
Why won't do I keep getting permission_denied with my first values, make a change to the rules, then after I change them back I no longer get permission denied? Where am I going wrong in my rules
vc2:
let root = Database.database().reference()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
root?.observeSingleEvent(of: .value, with: {
(snapshot) in
// sneakers node might not exist
if snapshot.hasChild("sneakers/nike)"){
....
}
)}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
root?.removeAllObservers()
}
The database layout:
root
|
#-sneakers // this node might not exist
| |
| nike
|
#-users
| |
| uid
|
#-jackets...
You're trying to read from the root of your database (root?.observeSingleEvent(...), but you only grant access to individual users and sneakers nodes. Since you're trying to read more than you have access to, Firebase rejects your observer.
It's hard to say why sometimes you don't get this error, but most likely it's a caching issue. Either your new rules haven't been applied yet (which should be rare), or you're reading from the cache of your iOS device (where the data was stored when you still did have access).
Either way, you should make sure you only read data you have access to. So:
root?.child("sneakers/nike")?.observeSingleEvent(of: .value, with: {
(snapshot) in
// sneakers node might not exist
if snapshot.exists() {
....
}
)}

Unique usernames in Firebase

I have been trying to implement Chris’ answer here: Can I make Firebase use a username login process? for the Facebook login but I can’t seem to get my head around it.
So far I’ve tried to set conditions on the textField but as Firebase observer works asynchronously, the conditions to check if the username exists in the database won’t work.
let usernameString = usernameTextField.text
let uid = FIRAuth.auth()?.currentUser?.uid
ref.runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in
if var post = currentData.value as? [String : AnyObject], let uid = FIRAuth.auth()?.currentUser?.uid {
let usernamesDictionary = post["usernames"] as! NSDictionary
for (key, _) in usernamesDictionary {
if key as? String == usernameString {
print("username not available: \(key)")
}
else if usernameString == "" {
print("Uh oh! Looks like you haven't set a username yet.")
}
else if key as? String != usernameString {
print("username available: \(key)")
print("All set to go!")
let setValue: NSDictionary = [usernameString!: uid]
post["usernames"] = setValue
currentData.value = post
}
}
return FIRTransactionResult.successWithValue(currentData)
}
return FIRTransactionResult.successWithValue(currentData)
}
Then I tried creating /usernames/ node in the database and set up rules as:
{
"rules": {
"usernames": {
".read": "auth != null",
".write": "newData.val() === auth.uid && !data.exists()"
}
}
}
Now that won’t let me set any username to the database. I get confused in creating rules but my whole point is that I need a sign up flow with the username data that’s unique for each user in the database.
While trying every answer I found in related posts, what worked for me the easy way i.e. without making Firebase rules play a part in it or creating a separate usernames node in the database was to not put an if/else condition inside the Firebase observer but instead to use the exists() method of FIRDataSnapshot.
Now here’s the trick, while I did try only the exists() method with a simple observer but that did not help me. What I did was first query usernames in order, then match the username with queryEqualToValue to filter the query:
refUsers.queryOrderedByChild("username").queryEqualToValue(usernameString).observeSingleEventOfType(.Value , withBlock: {
snapshot in
if !snapshot.exists() {
if usernameString == "" {
self.signupErrorAlert("Uh oh!", message: "Looks like you haven't set a username yet.")
}
else {
// Update database with a unique username.
}
}
else {
self.signupErrorAlert("Uh oh!", message: "\(usernameString!) is not available. Try another username.")
}
}) { error in
print(error.localizedDescription)
}
}
This is the first time out of most of the answers here that worked for me. But for now, I don’t know if this would scale. Post your experiences and best practices. They’ll be appreciated.

Firebase Swift - Slow response - Rules index [g]

My app is sending and retrieving large amount of data from Firebase every second, performing multiple functions.
I'm trying to understand the Firebase rules. Currently I have the default rules set up.
{
"rules": {
".read": "auth != null",
".write": "auth != null",
".indexOn": ["g"]
}
}
However, in my debugger it is saying...
[FirebaseDatabase] Using an unspecified index. Consider adding ".indexOn": "g" at /gameUserCoordinates to your security rules for better performance
Should I be creating something extra in my FireBase rules for gameUserCoordinates?
It is set up FirebaseRoot -> gameUserCoordinates -> UID -> GeoFireCoordinates
My user has to sign in and be authenticated to use the app.
Thanks, any help would be much appreciated.
UPDATE
var dbRef: FIRDatabaseReference {return FIRDatabase.database().reference()}
var gameUserCoordinatesRef: FIRDatabaseReference {return dbRef.child("gameUserCoordinates")}
func geoFireUploadUserCoordinates(latitude: CLLocationDegrees, longitude: CLLocationDegrees) {
let userKey = FIRAuth.auth()?.currentUser?.uid
let geoFireUser = GeoFire(firebaseRef: gameUserCoordinatesRef)
geoFireUser?.setLocation(CLLocation(latitude: latitude, longitude: longitude), forKey: userKey)