I have VC where by I am creating a new user and adding to firebase. The user inserts no problem into the database but I am having trouble checking if a user with the same name already exists. I have tried using various query statements within firebase but not getting any joy. Does anyone know how I can modify my code to accomplish this? here it is :-
#IBAction func submitUsername(sender: AnyObject) {
if let user = usernameTxtField.text where user != "" {
let username = DataService.ds.REF_USERS.childByAppendingPath("username")
username.observeSingleEventOfType(.Value, withBlock: { snap in
if let usernameDoesNotExist = snap.value as? NSNull {
let currentUser = DataService.ds.REF_USER_CURRENT.childByAppendingPath("username")
self.usernameTxtField.text = user
currentUser.setValue(user)
}
})
dismissViewControllerAnimated(true, completion: nil)
} else {
showAlert("Username Needed", msg: "please choose a username")
}
}
and the firebase data structure
"users" : {
"000591b6-ba88-4670-9cb7-edd79c273eab" : {
"newuser" : "added to FB",
"provider" : "password"
},
"05551c90-27fe-4969-b45d-279a72583857" : {
"newuser" : "added to FB",
"provider" : "password"
},
"0bbff1b0-681a-4fef-adc0-c4779a4b2100" : {
"provider" : "password",
"testing" : "emailtest",
"username" : "richy446"
},
"12f35e7a-2430-49b3-a287-f7b1daf6a121" : {
"newuser" : "added to FB",
"provider" : "password",
"username" : "richy445"
},
"15a45560-cc81-4c2d-bb59-b8ce6b125d68" : {
"provider" : "password",
"testing" : "emailtest",
"username" : "richy445"
},
"1c135936-6dd0-4189-bcd7-e8036e9736f9" : {
"newuser" : "added to FB",
"provider" : "password"
},
"21591e29-f083-452c-b74f-cc567cecb183" : {
"newuser" : "added to FB",
"provider" : "password"
},
"8b385aa7-0e80-426b-a385-5b3317c894d0" : {
"newuser" : "added to FB",
"provider" : "password",
"username" : "richy445"
}
}
There are a number of approaches to checking for duplicate data. The Firebase structure you have seems a little inconsistent so structuring it like this may be easier
"000591b6-ba88-4670-9cb7-edd79c273eab" : {
"email" : "test#test.com",
"password" : "password"
},
"05551c90-27fe-4969-b45d-279a72583857" : {
"email" : "test2#test.com",
"password" : "password"
},
1) Let Firebase do the work for you.
When creating a user, Firebase will return an error if the username (email) already exists. Using this method will prevent random data being written to your firebase until they account is created in Firebase.
myRootRef.createUser(email, password: pw, withValueCompletionBlock: { error, result in
if error != nil {
self.errMsgField.stringValue = "email/username in use, try again"
} else {
let uid = result["uid"] as! String //the uid of the new user
print("user created as \(uid)")
self.storedDataInUserNode(email, password:pw)
self.authUserWithAuthData( email, password: pw ) //auth the user
// and proceed in starting up the app
}
})
2) Checking for existing data in Firebase.
While the first method stops data from being written in the first place, if you want to check for duplicate data in general, here's a pattern:
let userName = the userName or email the user entered
let usersRef = Firebase(url:"https://test.firebaseio.com/users")
usersRef.queryOrderedByChild("email").queryEqualToValue("\(userName!)")
.observeSingleEventOfType(.Value, withBlock: { snapshot in
if ( snapshot.value is NSNull ) {
print("not found)") //didnt find it, ok to proceed
} else {
print(snapshot.value) //found it, stop!
}
}
There's a method on DataSnapshot named .exists() that you can you can call like this.
Updated for Swift 4.
struct ModelUser {
var id: String
var name: String
init(data: DataSnapshot) {
// do init stuff
}
}
func isUserRegistered(with id: String, completion: #escaping (_ exists: Bool, _ user: ModelUser?) -> ()) {
DatabaseReference.users.child(id).observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists() {
// user is already in our database
completion(true, ModelUser(data: snapshot))
} else {
// not in database
completion(false, nil)
}
}
}
Related
I'm creating an app that allows notes to be shared. I'm having trouble with the Firebase database and feel that the amount of lines of code I am using to get information the user needs is more than I actually need and on top of that doesn’t work. Below is a screenshot of my database and a show of the code I’ve used. There are 3 different account types in my app. Parent, Child, and Other.
The Child can only see their own notes
The Parent can see everyone who’s written notes in for their code
The Other is supposed to see their notes and the parents notes per
code.
I am attempting to show the users whose notes can be seen in a collection view. So far it works for Parents and Children but not for the 'Other' which I am trying to fix.
This is my database. Right now, a Parent makes the account which generates a unique code. Then an Other or Child can join by typing the code in on a ViewController. For a child account it will automatically add the code to the account of the child in the user section and for the Other it is supposed to make a new section (I added a picture of what it looked like before I changed it). All users that have a certain code are added to the code section under that code.
{
"codes" : {
"ParVWb" : {
"totalUsers" : 2,
"users" : {
"VWbSR8qQyWd9deka6tgbxFC9ahs1" : {
"numberofNotes" : 0
},
"qm12qgkWfdbtSMMx58wIrxKuLKh1" : {
"numberofNotes" : 0
}
}
}
},
"users" : {
"JN47TBdI4EYX0NvoSHN21o1xFH92" : {
"email" : "other1#restart.mocs",
"fullName" : "Other 1 Name",
"type" : "Other",
"uid" : "JN47TBdI4EYX0NvoSHN21o1xFH92"
},
"VWbSR8qQyWd9deka6tgbxFC9ahs1" : {
"code" : "ParVWb",
"email" : "parentemail#restart.mocs",
"fullName" : "Parent Name",
"type" : "Parent",
"uid" : "VWbSR8qQyWd9deka6tgbxFC9ahs1"
},
"qm12qgkWfdbtSMMx58wIrxKuLKh1" : {
"code" : "ParVWb",
"email" : "childname#restart.mocs",
"fullName" : "Child Name",
"type" : "Child",
"uid" : "qm12qgkWfdbtSMMx58wIrxKuLKh1"
}
}
}
I used to have the use section look like this but now because of the large amount of reference blocks, it doesn't run the part that adds the "codes" section to the 'Other' user.
This is the function I have in my CollectionViewController that obtains all the needed users depending on the currentUser type.
func fetchConnections() {
ref.child("users").child(userID!).observe(.value, with: { (snapshot) in
let user = User(snapshot: snapshot)
if user.type == "Parent" {
let code = user.code as? String
let codeRef = databaseRef.child("codes").child(code!).child("users")
codeRef.observe(.value, with: { (codeSnapshot) in
if let users = codeSnapshot.value as? [String:AnyObject] {
DispatchQueue.main.async(execute: {
for user in users {
if let dict = user.value as? [String: Any] {
let userInformation = cellInfo(uid: user.0, numberofNotes: (dict["numberofNotes"] as? Int)!, code: self.code)
self.userCollection.append(userInformation)
self.collectionView.reloadData()
}
}
})
}
}) { (error) in
print(error.localizedDescription)
}
} else if user.type == "Child" {
let code = user.code
self.ref.child("codes").child(code!).child("users").child(self.userID!).observe(.value, with: { (snapshot) in
if let dict = snapshot.value as? [String: Any] {
print(user.key, (dict["numberofNotes"] as? Int)!)
let userInformation = cellInfo(uid: (user.key as? String)!, numberofNotes: (dict["numberofNotes"] as? Int)!, code: "code")
self.userCollection.append(userInformation)
self.collectionView.reloadData()
}
})
} else if user.type == "Other" {
self.ref.child("users").child(self.userID!).child("codes").observe(.value, with: { (snap) in
if let codes = snap.value as? [String:AnyObject] {
for code in codes {
if code.key == "numberofCodes" {
print("ignore")
} else { // If it's an actual code
self.ref.child("users").child(code.0).observeSingleEvent(of: .value, with: { (codeSnap) in
let user = User(snapshot: codeSnap)
let codetoFetch = user.code
self.ref.child("codes").child(codetoFetch!).child("users").child(self.userID!).observe(.value, with: {(codeSnapshot) in
if let users = codeSnapshot.value as? [String:AnyObject] {
DispatchQueue.main.async(execute: {
for user in users {
if let dict = user.value as? [String: Any] {
let userInformation = cellInfo(uid: code.0, numberofNotes: (dict["numberofNotes"] as? Int)!, code: self.code)
//self.userCollection.append(userInformation)
//self.collectionView.reloadData()
}
}
})
}
})
})
}
}
}
})
}
})
}
How can I obtain all of the codes that the 'Other' is a part of and all of the UID's & NumberofNotes for the 'Parents' of that code so that I can put them into the collectionView? (This code works for Parent and Child but if the way I'm doing it is long and ineffective please let me know.)
I want to get a user corresponding to an event
I have a list of Events
let eventsRef = FIRDatabase.database().reference.child("Events")
I have a list of Users
let usersRef = FIRDatabase.database().reference.child("Users")
My Event and User model is as below
Events
Event1
eventHost: user1_uid
Event2
eventHost: user2_uid
Users
User1
email: email1
User2
email: email2
The following callback (in the Event model) is never invoked:
if let userKey = eventData["eventHost"] as? String {
userRef = usersRef.child(userKey)
userRef.observeSingleEvent(of: .value, with: { snapshot in
...
})
}
I can confirm that I have not enabled disk persistence and that user uid is available. Is there anything I am doing obviously wrong?
======
EDIT: The simplified event model
import Foundation
import Firebase
class Event {
// event metadata
private var _eventId: String!
private var _eventHost: User!
var eventId: String {
return _eventId
}
var eventHost: User {
return _eventHost
}
init(eventId: String, eventData: Dictionary<String, Any>) {
self._eventId = eventId
if let userKey = eventData["eventHost"] as? String {
let usersRef = FIRDatabase.database().reference().child("Users")
let userRef = usersRef.child(userKey)
print(userRef)
userRef.observeSingleEvent(of: .value, with: { (snapshot) in
print("USERY: \(snapshot.key)")
if let userDict = snapshot.value! as? Dictionary<String, Any> {
let id = snapshot.key
self._eventHost = User(userId: id, userData: userDict)
}
})
}
}
}
The print(userRef) resolves to
https://xxxx.firebaseio.com/Users/AFHpS3npOga4tfj10GS2HGeT9uJ3`
which is a valid object in my Firebase structure. Snippet of Firebase User structure
"AFHpS3npOga4tfj10GS2HGeT9uJ3" : {
"email" : "test#gmail.com",
"firstName" : "Wilma",
"lastName" : "Flintstone",
"profileImageUrl" : "http://images.iimg.in/c/569f4771c45d324bda8b4660-4-501-0-1453279096/google/user-icon-png-pnglogocom.img",
"provider" : "Firebase",
"userId" : "AFHpS3npOga4tfj10GS2HGeT9uJ3"
},
can you take a look at the rules of your firebase database :
It should be something like this
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
in order to let non authenticated people read / write data you should do the following :
{
"rules": {
".read": true,
".write": true
}
}
From the code you post I don't see any problem ...
If this doesn't work please post more code and tell us what you plan to do exactly so we can better help you.
Edit: To go along with the above suggestion, adding a cancel block to the observe function will reveal if there's a rule issue. If the user cannot access the node, the Xcode console will print 'Permission Denied'
userRef.observeSingleEvent(of: .value, with: { (snapshot) in
print("USERY: \(snapshot.key)")
if let userDict = snapshot.value! as? Dictionary<String, Any> {
let id = snapshot.key
self._eventHost = User(userId: id, userData: userDict)
}
}, withCancel: { error in
print(error.localizedDescription)
})
I am doing a project which is concerned about Firebase, I have tried to upload the data to the Firebase as the code in the tutorial from the Firebase website:
// An index to track Ada's memberships
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
// Index Ada's groups in her profile
"groups": {
// the value here doesn't matter, just that the key exists
"techpioneers": true,
"womentechmakers": true
}
},
...
},
"groups": {
"techpioneers": {
"name": "Historical Tech Pioneers",
"members": {
"alovelace": true,
"ghopper": true,
"eclarke": true
}
},
...
}
}
and I follow this structure to write my own data structure, which looks like this:
func pushUser() {
let username = self.username
let userid = FIRAuth.auth()?.currentUser?.uid
let userInfo : [String : Any] = ["username" : username,
"userID" : userid,
"sggestedRoutes" : "",
"myTracedRoutes" : "",
"routes": {
"usualInfo" : true,
"calcuInfo" : true
}
]
let userRef = FIRDatabase.database().reference()
userRef.child("user").childByAutoId().setValue(userInfo)
}
in Swift3, but the debugger says the nested routes{} is not correct, I am quite confused about this, could anyone give me a hint on this?
Kind regards
After received the advice, I make some change on the code, but it is still not working on the Route part:(
func pushUser() {
let username = self.username
let userid = FIRAuth.auth()?.currentUser?.uid
let userInfo : [String : Any] =
["username" : username,
"userID" : userid,
"sggestedRoutes" : "",
"uploadedRoutes" : "",
"myTracedRoutes" : "",
["Routes":
["usualInfo" : true,
"calcuInfo" : true]
]
]
let userRef = FIRDatabase.database().reference()
userRef.child("user").childByAutoId().setValue(userInfo)
}
Try adding the routes as ["routes": ["usualInfo": true, "calcuInfo": true]]
Don't forget the correct closures for both children "user info" and "routes"
I have a view controller for My Profile. Logging in allows the profile page to appear without errors but when signing up, app crashes when pressing the contacts button located at bottom of view controller as seen below.
The process:
User Signs Up:
func signUp(_ email: String, usersname: String, password: String, data: Data!, loginHandler: LoginHandler?) {
FIRAuth.auth()?.createUser(withEmail: email, password: password, completion: { (user, error) in
if error != nil {
// Show error to user
self.handleFirebaseErrors(err: error as! NSError, loginHandler: loginHandler)
} else { // success creating user
if user?.uid != nil { // if there is a valid user id
// Store user to database
self.setUserInfo(user, usersname: usersname, email: email, password: password, data: data!)
// Log In the user
self.login(email: email, password: password, loginHandler: loginHandler)
}
}
})
}
As in the signUp(), setUserInfo() is called, which contains images, and then calls saveUser()
Save User
func saveUser(_ user: FIRUser!, usersname: String, email: String, password: String) {
// Create the user dictionary info
let userInfo = ["email": user.email!, "password": password, "usersname": usersname, "uid": user.uid, "photoUrl": String(describing: user.photoURL!)]
// create user reference
let userRef = DataService.Instance.dbRef.child("riders").child(user.uid)
// Save the user info in the database
userRef.setValue(userInfo)
}
Logs In
func login(email: String, password: String, loginHandler: LoginHandler?) {
FIRAuth.auth()?.signIn(withEmail: email, password: password, completion: { (user, error) in
if error != nil {
self.handleFirebaseErrors(err: error as! NSError, loginHandler: loginHandler)
} else {
loginHandler?(nil, nil)
}
})
}
The problem here is in saveUser():
At the moment, firebase shows the user.uid but I want it to show the username of the user.
let userRef = DataService.Instance.dbRef.child("riders").child(usersname)
With the above code, once the contacts button is pressed on the RidersVC, it crashes the app with error:
fatal error: unexpectedly found nil while unwrapping an Optional value
on line 56 of MyProfileVC:
let imageUrl = String(user.photoUrl)
Any ideas as how I can get the username to be displayed as the child of "riders" instead of the user.uid without it crashing?
MyProfileVC.swift
if FIRAuth.auth()?.currentUser == nil {
let vc = UIStoryboard(name: "Rider", bundle: nil).instantiateViewController(withIdentifier: "Login")
present(vc, animated: true, completion: nil)
} else {
dbRef.child("riders/\(FIRAuth.auth()!.currentUser!.uid)").observe(.value, with: { (snapshot) in
DispatchQueue.main.async(execute: {
let user = User(snapshot: snapshot)
self.username.text = user.usersname
self.email.text = FIRAuth.auth()?.currentUser?.email
let imageUrl = String(user.photoUrl)
Firebase Database Structure: (how I want it to be)
{
"riders" : {
"rider 1" : {
"email" : "rider1#me.com",
"password" : "whatever",
"photoUrl" : "https://firebasestorage.googleapis.com/...",
"usersname" : "rider 1"
}
}
}
User.swift
struct User {
let usersname: String!
let email: String!
let password: String!
let photoUrl: String!
var ref: FIRDatabaseReference?
var key: String
init(snapshot: FIRDataSnapshot) {
key = snapshot.key
ref = snapshot.ref
let snapshotValueUsersname = snapshot.value as? NSDictionary
usersname = snapshotValueUsersname?["usersname"] as? String
let snapshotValueEmail = snapshot.value as? NSDictionary
email = snapshotValueEmail?["email"] as? String
let snapshotValuePass = snapshot.value as? NSDictionary
password = snapshotValuePass?["password"] as? String
let snapshotValuePhoto = snapshot.value as? NSDictionary
photoUrl = snapshotValuePhoto?["photoUrl"] as? String
}
Firebase structure - (the way it is now)
{
"drivers" : {
"RideRequests" : {
"europeanjunkie" : {
"active" : true,
"latitude" : "45.267",
"longitude" : "-66.059",
"userId" : "5c17ByRJljZFcM703Vqn5eSFwYJ3",
"username" : "europeanjunkie"
}
}
},
"riders" : {
"5c17ByRJljZFcM703Vqn5eSFwYJ3" : {
"email" : "europeanjunkie#me.com",
"password" : "whatever",
"photoUrl" : "https://firebasestorage.googleapis.com",
"uid" : "5c17ByRJljZFcM703Vqn5eSFwYJ3",
"usersname" : "europeanjunkie"
}
}
}
Here's some stuff to consider - a little, some or all may get you headed in the right direction. Also, you can probably remove all of the DispatchQueue calls as Firebase does most of the heavy lifting for you, and with proper code structure, they are not needed.
1) A Swifty user class
class UserClass {
var usersname = ""
var email = ""
var password = ""
var photoUrl = ""
var uid = ""
init(withSnapshot: FIRDataSnapshot) {
let dict = withSnapshot.value as! [String:AnyObject]
uid = withSnapshot.key
usersname = dict["usersname"] as! String
email = dict["email"] as! String
password = dict["password"] as! String
photoUrl = dict["photoUrl"] as! String
}
}
note that we are using the var uid of each user to identify them (their 'key')
The structure that matches that class
users
uid_0
email: "bill#email.com"
password: "myPassword"
photoUrl: "http://www.url.com"
usersname: "Bill"
uid_1
email: "leroy#email.com"
password: "aPassword"
photoUrl: "http://www.anotherUrl.com"
usersname: "Leroy"
Notice again the users and their associated info are stored within the /users node in each child node that has that users uid as the key.
And some code that reads in uid_0, prints the uid and name. This code is a one-shot so it reads uid_0, but does NOT leave an observer attached to the node.
let userRef = rootRef.child("users/uid_0")
userRef.observeSingleEvent(of: .value, with: { snapshot in
let aUser = UserClass(withSnapshot: snapshot)
print("uid \(aUser.uid) has name \(aUser.usersname)")
})
Now the Geofire node would like something like this
user_locations
uid_0
//geofire data
uid_1
//geofire data
So now there is a direct correlation between the users node and their location.
In general, it's a good idea to disassociate node names (keys, which are static data) from the data they contain, which is dynamic.
With the structure in the initial question, imagine if 'europeanjunkie' changed his name to 'europeanjunkieDude'. Every place you reference 'europeanjunkie' would then have to be changed - and if it's used as a key, the entire node would have to be read in, deleted, updated, and re-written.
Using child keys created by Firebase, uid's and childByAutoId(), removes that issue.
Hope that helps!
In my opinion, if you want to query the username as the keyword. There are two possible ways to struct your dictionary.
First, use childByAutoId, username and userid will be at the same level, so you can get which value you like.
{
"riders" : {
"-KQaU9lVcUYzIo52LgmN" : {
"email" : "rider1#me.com",
"password" : "whatever",
"photoUrl" : "https://firebasestorage.googleapis.com/...",
"usersname" : "rider 1",
"userid" : "rider 1"
}
}
}
Second, make username as the child of riders. However, there would be tons of Mike.
{
"riders" : {
"username" : {
"email" : "rider1#me.com",
"password" : "whatever",
"photoUrl" : "https://firebasestorage.googleapis.com/...",
"userid" : "rider 1"
}
}
}
I have a problem, I'm trying to access some data on firebase this is the data structure
I managed to access "favorite"
This is what I used to get
ref.observeEventType(.Value, withBlock: { snapshot in
let fav = snapshot.value.objectForKey("favPost")
print("\(fav) Printed")
})
but I'm trying to access "favPost" but I couldn't figure it out?!!
Firebase structure
{
"posts" : {
"-KGIxJybfQEJbcSy2bHH" : {
"author" : "Rioodi",
"postText" : "Test",
"votes" : 1
},
"-KGIxLUmPIa1Q1k0oRLs" : {
"author" : "Rioodi",
"postText" : "Raed",
"votes" : 0
},
"-KGJe-5ciAyu6Kom98E0" : {
"author" : "Neal",
"postText" : "Neal",
"votes" : 0
},
"-KGLFC0RqW_48lHMCsx8" : {
"author" : "Rioodi",
"postText" : "Test",
"votes" : 0
}
},
"users" : {
"afd0f27a-f62f-4cf1-9e81-032edc246687" : {
"email" : "test#test.com",
"favorite" : {
"-KGIxJybfQEJbcSy2bHH" : {
"favPost" : "Test"
}
},
"provider" : "password",
"username" : "Rioodi"
},
"fc56cc22-6275-48e0-8376-0b73b273b8e2" : {
"email" : "tests#test.com",
"provider" : "password",
"username" : "Neal"
}
}
}
Since you know the userId and you also know you are looking for their favorite post, directly access it like this.
let usersRef = self.myRootRef.childByAppendingPath("users")
let thisUserRef = usersRef.childByAppendingPath("this users id")
let thisUserFavoriteRef = thisUserRef.childByAppendingPath("favorite")
thisUserFavoriteRef.observeSingleEventOfType(.ChildAdded, withBlock: { snapshot in
let fav = snapshot.value["favPost"] as! String
print(fav)
})
you could combine the path into a single line as well
let thisUserFavoriteRef = rootRef.childByAppendingPath("users/this users id/favorite")
You can try following code. (you can change according to requirements.)
ref.observeEventType( .Value, withBlock: { snapshot in
//lets consider you have reached up to "favarites"
let favarites = snapshot?.value as! [ String : [ String : AnyObject ] ]
//In "favarites" array you will get whole list
for favarite in favarites
{
let favPost = favarite.1[ "favPost" ] as? String //to access value part
print("\(favPost) Printed"
}
}