Firebase,Swift : Not able to retrieve Data - ios

I'm trying to do a simple task, to get a value from Firebase.
I added the image below with the relevant screens. At least one of the print commands should print a message to the console, but it's not working. I've been trying to fix it for 1,5 days now and I still have no idea why it is not working.
Code:
import UIKit
import Firebase
class MainVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let ref = FIRDatabase.database().reference()
ref.child("zonneschijn").observeSingleEventOfType(.Value, withBlock: { snapshot in
if snapshot.value is NSNull {
print("Does not exist currently")
} else {
print("Exists currently")
}
})
}
}
I've also tried to use viewDidAppear, also with no succes.

If you are not authenticating your user's.
Go to Rules tab in Realtime Database in Firebase console.
Default Security rules of firebase are something like this :-
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
Which means only authenticated users(Those user's who have been signed in either with gmail, Facebook, gitter, email..etc) can read or write data...
Clearly you are not authenticating your user that's why you can not retrieve any data with these default Security Rules .
This is a good practice to only allow authenticated users to have access to the DB, as it is more secure.If you want to read data anyways , Modify your security rules to this:-
Warning :- Not Recommended if you are making app that has some private DB.
// These rules give anyone, even people who are not users of your app,
// read and write access to your database
{
"rules": {
".read": true,
".write": true
}
}
A better alternative :-
Sign in your user(start with email-password : Firebase EMAIL- Password Auth)
Then access your DB
Do read : Security Rule's

Related

Differentiate between admin and normal User for Firebase (Xcode ios app)

I'm currently developing an IOS app and what i want to do is differentiate between admin users and normal users when logging into the app because at the moment a normal user can log in and get access to the admin options. I'm using Firebase as my database but there's no option to differentiate. Any help would be much appreciated.
Here's my code for login
#IBAction func loginTapped(_ sender: Any) {
if let email = emailTextField.text, let password = passwordTextField.text{
Auth.auth().signIn(withEmail: email, password: password) {(user, error) in
if let firebaseError = error{
print (firebaseError.localizedDescription)
self.showAlert("Invalid Email or Password")
return
}
self.presentLoggedInScreen()
}
}
}
I accept if anyone downvotes this post but please comment below why you doing so. Thanks
You need to use other things to implement some role based authentication, you can check examples here to give you some ideas: https://firebase.google.com/docs/auth/admin/custom-claims
You can add rules to your Firebase database:
Got to your database on your Firebase console then click on your Rule Tab on you right side and add one of the following examples adapted to your code.
For example, here everyone has access to your /foo/ path but no one can write to it:
service cloud.firestore{
"rules": {
"foo": {
".read": true,
".write": false
}
}
}
So, if you want a certain autoriztion to some path for determinated users you can do it as follows:
service cloud.firestore{
"rules": {
"users": {
"$uid": {
"foo": {
".read": true,
".write": false
}
}
}
}
}
Or you can do it with a parameter to restrict foo access to a specific uid:
"foo":{
".read": "auth.uid != null &&
query.orderByChild == 'owner' && query.equalTo == auth.uid"
}

Firebase database not working

I am working on an app that requires Firebase auth and database. When registering, the app must check to see if the code entered is genuine then proceed to create a user. This is my code:
ref = FIRDatabase.database().reference()
self.ref.child("PatientCodes").observeSingleEvent(of: .value, with: { (snaphshot) in
print("In")
let value = snaphshot.value as! NSArray
if value.contains(self.patientIDTextField.text!) {
print("Found ID")
FIRAuth.auth()?.createUser(withEmail: self.emailTextField.text!, password: self.passwordTextField.text!, completion: { (user, error) in
if user != nil {
self.performSegue(withIdentifier: "registered", sender: self)
}
})
}
The issue I am having is, none of the code is being executed past self.ref.child etc. The print("In") statement is never hit.
Any help is greatly appreciated. Thanks in advance.
Before starting anything with Firebase Database,
first check that the rules under database in your console is properly set i.e whether read is true or write is true.
Look at your database's rules, the default rule prevents anonymous read and write, so you must log in first to get access to the database.
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}

Unable to write value to firebase Swift 3

I have enabled email (custom) authentication in Firebase , I have set following rules
{
"rules": {
".read": "auth == null", //even non-authorised users CAN read
".write": "auth == null" //even non-authorised users CAN write
}
}
Trying to test Firebase using following code
let rootRef = FIRDatabase.database().reference()
let playlists = rootRef.child("playlists")
playlists.setValue("test")
Not sure whats wrong but it gives setValue and RemoveValue Permission denied error.
Unable to understand whats wrong , I am new to Firebase
Here's the solution
{
"rules": {
".read": true,
".write": true
}
}
These rules allow everyone to read and write to your entire Firebase node. Ok for testing but please don't leave it like that as it's totally insecure.
Also note that your code will erase the playlists node each time and overwrite it with
your_firebase_ref
playlists: "test"
Just to get you going in the right direction, here's updated code that will create a new node each time it's run.
let rootRef = FIRDatabase.database().reference()
let playlists = rootRef.child("playlists")
let aNewPlayList = playLists.childByAutoId()
aNewPlayList.setValue("test")
which will result in
your_firebase_ref
playlists
-Yius889jsijs: "test" //first time it's run
-YUOmsooaosd9: "test" //second time it's run
the -Y.... key is created by childByAutoId and is generally the way to create children keys within a node.

How do I validate if a username exists before sign up in Firebase and Swift 3?

I've incorporated a sign-up flow of five view controllers as opposed to one central VC for Firebase sign up.
Normally there would be the problem of data being lost before sign up, but I'm pushing all the entered values via segue programmatically to the final confirmation page and sending the data to Firebase for auth eventually.
My problem lies therein - I want to check if the username exists before signing up. This is imperative for the UX of my app and I can't just do this all one VC.
Here's the code I have for the UsernameViewController:
let rootRef = FIRDatabase.database().reference()
rootRef.queryOrdered(byChild: "users").queryEqual(toValue: self.chooseUsernameTextField.text!)
.observe(FIRDataEventType.value, with: { (snapshot: FIRDataSnapshot!) in
if snapshot.hasChildren() == false {
print("not found")
} else {
print("usr found")
}
});
Here's the code I have for the Firebase Database Security Rules
{
"rules": {
".read": false,
".write": false,
"users": {
".validate": "data.child('username').val() === true",
".read": true
}
}
}
and finally, a screenshot of my Data tree (My Data tree won't let me nest any new users or create a user node without a value for some reason):
Picture of Firebase Data Tree: App Name/Users
I have a nagging suspicion that my data rules and tree are configured properly to match the code, but I'm stuck in a XY problem of not knowing what to do for Firebase security to get my code of username checking complete.
Please help me! :(
If there's a user created within the Auth section of Firebase as well, then you can actually use the fetchProviders method, and if no providers are returned, you have no user in you Auth section.
FIRAuth.auth()?.fetchProviders(forEmail: email, completion: { (providers, error) in
if providers == nil {
// user doesn't exist
} else {
// user does exist
}
})
I have not tested the code but the theory would be to order by username that equals to the username you are looking for. If this snapshot exists you already have a user in the database with that name if it doesn't you do not.
Will run and test code when i have a chance to and update my post.
let rootRef = FIRDatabase.database().reference()
rootRef.child("users").queryOrdered(byChild:"username").queryEqual(toValue: self.chooseUsernameTextField.text!)
.observe(.value, with: { (snapshot) in
if snapshot.exists() == false {
print("not found")
} else {
print("usr found")
}
});
When using Firestore to save users, I did this
Auth.auth().addStateDidChangeListener({ (auth, user) in
if let id = user?.uid{
let userDoc = db.collection("users").document(id)
userDoc.getDocument { (document, error) in
if document == nil{// if user doesn't exist, create one
self.db.collection("users").document(id).setData(userProfile.toDictionary()){ error in
}
}
}
}
})

Firebase Object Ownership with Event Observation

I'm using Firebase in my iOS app. I'd like each of my objects to have a creatorId property whose value is the authenticated user ID (authData.uid with a Firebase authentication object). I'm using a custom token generator for authentication, but the problem can be reproduced with anonymous log in too.
I'd like a user to only be able to read (and write, but let's focus on reading right now, as that's where I'm having my issues) objects that they created. In other words, the querying user's authenticated user ID will match the creatorId of the objects they are fetching.
I'm having trouble with permissions when I craft queries and rules to make this happen.
Here is the Firebase documentation for Rules and Security.
Here is what my Firebase dashboard looks like for a Task object:
+ firebase-base
+ tasks
+ {task_id}
+ creatorId:
+ title:
where task_id is a unique identifier generated by Firebase upon insertion.
My rules look like this (again, let's ignore writing rules for now):
{
"rules": {
"tasks": {
"$task_id": {
".read": "auth.uid === data.child('creatorId').val()"
}
}
}
}
Reading a specific task works fine, but I'd expect to be able to make a query that says, "fetch all the tasks that I created" using observeEventType and related functions. This doesn't work for me. I get "Permission Denied" errors.
Here is how I'm observing, in Swift:
let reference = Firebase(url: "https://{My-Firebase-Base-Reference}/tasks")
reference.observeEventType(.ChildChanged,
withBlock: { (snapshot: FDataSnapshot!) -> Void in
// Success
}) { (error: NSError!) in
// Error: I get Permissions Denied here.
}
Per #Ymmanuel's suggestions, I also tried being more specific in my query, like so:
let reference = Firebase(url: "https://{My-Firebase-Base-Reference}/tasks")
reference.queryOrderedByChild("creatorId").queryEqualTo({USER_UID}).observeEventType(.ChildChanged,
withBlock: { (snapshot: FDataSnapshot!) -> Void in
// Success
}) { (error: NSError!) in
// Error: I get Permissions Denied here.
}
Neither of these blocks work, I always get "Permission Denied" errors. What am I doing wrong?
What you are missing is that you are assuming that security rules are queries and that is not true.
Check the
Rules are Not Filters section in the link.
Security rules only validate if you can read or write a specific path of your firebase database.
If you want to only receive changes of a specific user you should use firebase queries.
For example if you want to get all the tasks of a specific user, you should do:
let reference = Firebase(url: "https://{My-Firebase-Base-Reference}/tasks")
reference.queryOrderedByChild("creatorId").queryEqualTo(YOUR_CURRENT_USER_UID).observeEventType(.ChildChanged,
withBlock: { (snapshot: FDataSnapshot!) -> Void in
// Success
}) { (error: NSError!) in
// Error: Get Permissions Denied Here.
}
This way you could get all the events only related to your user, and protect the information by applying the security rules.
also if you want to allow only the creator to write their own tasks you should also consider the case where you create the task and write something like this:
"tasks": {
//You need to include the $task_id otherwise the rule will seek the creatorId child inside task and not inside your auto-generated task
"$task_id": {
".read": "auth.uid === data.child('creatorId').val()",
//this is to validate that if you are creating a task it must have your own uid in the creatorId field and that you can only edit tasks that are yours...
".write":"(newData.child('creatorId').val() === auth.uid && !data.exists()) || (data.child('creatorId').val() === auth.uid && data.exists())",
//you should add the index on to tell firebase this is a query index so your queries can be efficient even with larger amounts of children
".indexOn":"creatorId",
}
}
(check the syntax but that's the general idea)
A couple of comments:
If local persistence is on, and you have no internet connection and there is no local value, neither of the blocks will be called.
The observe code in the question is bugging me, it's not wrong but may be a bit clearer if it was like this:
reference.observeEventType(.ChildChanged, withBlock: { snapshot in
print(snapshot.value)
}, withCancelBlock: { error in
print(error.description)
})
Edit:
The question is clearer now
I'd like a user to only be able to read objects that they created.
I'll demonstrate via the following:
A Firebase structure:
posts
post_0
created_by: uid_0
msg: some message
post_1
created_by: uid_1
msg: another message
post_2
created_by: uid_2
msg: yippee
and the rules that will allow a user to only read from the post node they created
"rules": {
".read": false,
".write": false,
"posts": {
".read": false,
"$post_id": {
".read": "root.child('posts').child($post_id).child('created_by').val() == auth.uid",
".write": "auth != null"
}
}
}
then the code to test. We assume the current users uid is uid_0:
let reference = self.myRootRef.childByAppendingPath("posts/post_0")
reference.observeEventType(.ChildAdded, withBlock: { snapshot in
print(snapshot.value)
}, withCancelBlock: { error in
print(error.description)
})
The above code will allow a read from node post_0 which is the post uid_0 created as indicated by the created_by child.
If the path is changed to posts/posts_1 (or anything else) for example, the read is denied.
This answer my not directly answer the question as if you are expecting the rules to 'block' or 'filter' the resultant data, that isn't what rules are for (per the other answer).
So you may want to go down a different path by constructing a query to pull out just the posts you want to work with based on the created_by = auth.uid like this
let reference = self.myRootRef.childByAppendingPath("posts")
reference.queryOrderedByChild("created_by").queryEqualToValue("uid_1")
.observeEventType(.Value, withBlock: { snapshot in
print(snapshot.value)
}, withCancelBlock: { error in
print(error.description)
})

Resources