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"
}
}
}
Related
I am trying to simply fetch a users favourite maps onto a tableview.
something that i thought would be very basic but turned out to be extremely difficult.
The code here is the best that i have managed so far, Attempting to somehow reference a (users id) with a (yourmaps id) to fetch specific information.
For example. Since the user has made 1 map his favourite(with id (-LpY4XEER-b21hwMi9sp)). I want to look through all maps within root["yourmap"] and only fetch his map onto a tableview.
Firebase
"users" {
"6g55cHXH4begwooHQvO4EKNV3xm1" : {
"photoURL" : "https://firebasestorage.googleap...",
"username" : "lbarri",
"yourmaps" : {
"-LpY4XEER-b21hwMi9sp" : true
}
}
}
"yourmaps": {
"-LpY4XEER-b21hwMi9sp" : {
"author" : {
"photoURL" : "https://firebasestorage.googleapis.com/v...",
"uid" : "6g55cHXH4begwooHQvO4EKNV3xm1",
"username" : "lbarri"
},
"mapmoderators" : {
"6g55cHXH4begwooHQvO4EKNV3xm1" : true
},
"mapphotoURL" : "https://firebasestorage.googleapis...",
"mapusername" : "Hello World"
},
"-LpYo_pQ8zIOGHHlNU1Q" : {
"author" : {
"photoURL" : "https://firebasestorage.googleapis.com/v...3",
"uid" : "RLFK9xnvhccTu2hbNHq0v05J2A13",
"username" : "lbarri"
},
"mapmoderators" : {
"RLFK9xnvhccTu2hbNHq0v05J2A13" : true
},
"mapphotoURL" : "https://firebasestorage.googleapis.com/...",
"mapusername" : "Dream"
}
}
Swift
func getCurrentUserMaps() {
guard let userProfile = UserService.currentUserProfile else { return }
let currentUserId = userProfile.uid
let userRef = Database.database().reference().child("users").child(currentUserId)
userRef.observeSingleEvent(of: .value, with: { (snapshot) in
let root = snapshot.value as? NSDictionary
if let mapsByUser = root!["yourmaps"] as? [String: Bool] {
for (documentId, status) in mapsByUser {
if status {
// Document is true, check for the maps
self.fetchyourmaps(key: documentId, owner: currentUserId)
}
}
}
}) { (error) in
print(error.localizedDescription)
}
}
func fetchyourmaps(key:String, owner:String) {
let yourMapRef = Database.database().reference().child("yourmaps")
yourMapRef.observeSingleEvent(of: .value, with: {snapshot in
let user = snapshot.value as? NSDictionary
if let mapsByUser = user!["mapmoderators"] as? [String: Bool] {
for (userId, status) in mapsByUser {
if userId == owner && status == true {
print("Owner \(owner) manages this \(user)")
var tempYourMap = [YourMapProfile]()
for key in (snapshot.value as? NSDictionary)! {
let childSnapshot = key as? DataSnapshot
let dict = childSnapshot!.value as? [String:AnyObject]
let author = dict!["author"] as? [String:AnyObject]
let uid = author!["uid"] as? String
let username = author!["username"] as? String
let photoURL = author!["photoURL"] as? String
let url = URL(string:photoURL!)
let mapusername = dict!["mapusername"] as? String
let mapphotoURL = dict!["mapphotoURL"] as? String
let mapurl = URL(string:mapphotoURL!)
let userProfile = UserProfile(uid: uid!, username: username!, photoURL: url!, mapPoints: mapPoints!)
let yourmapprofile = YourMapProfile(mapid: childSnapshot!.key as! String, mapauthor: userProfile, mapusername: mapusername!, mapphotoURL: mapurl!)
tempYourMap.append(yourmapprofile)
}
self.yourmaps = tempYourMap
self.tableView.reloadData()
}
}
}
})
}
print("Owner \(owner) manages this \(user)") does print the correct maps onto the console
After that line it is when i cant figure out how to package the information to my tableview.
I have searched everywhere for information on how to retrieve data from Firebase when referencing one root folder to another but i cant find anything helpful. So any link/guide/ tutorial etc would be appreciated and i'll gladly take it from there. Is this at least how you are supposed to do it?
There are a few ways to do this but here's two: Option 1 is to leverage a deep query to get the maps that are this users favorites. The second is to iterate over the users maps and pull each one at a time.
Option 1:
Start with a maps node like this
allMaps
map_0
favorite_of
uid_0: true
uid_3: true
map_user_name: "Larry"
map_1
favorite_of
uid_2: true
map_user_name: "Moe"
map_2
favorite_of
uid_0: true
map_user_name: "Curly"
Then, a deep query to get all the favorite maps of uid_0
func queryToGetMyFavoriteMaps() {
let uid = "uid_0"
let ref = self.ref.child("allMaps")
let path = "favorite_of/" + uid
let query = ref.queryOrdered(byChild: path).queryEqual(toValue: true)
query.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
print(child) //prints the map_0 & map_2 nodes since that's the favorite onces
}
})
}
Option 2
Change up the allMaps node since we won't be doing a query
allMaps
map_0
map_user_name: "Larry"
map_1
map_user_name: "Moe"
map_2
map_user_name: "Curly"
and then the users node will be something like this
users
uid_0
name: "Frank"
favorite_maps:
map_0: true
map_2: true
uid_1
name: "Leroy"
favorite_maps:
map_1: true
and then the code that reads uid_0's favorite_maps node, and gets the keys from that snapshot, and then iterates over them, reading the map nodes one at a time.
func iterateToGetFavoriteMaps() {
let uid = "uid_0"
let userRef = self.ref.child("users").child(uid)
userRef.observeSingleEvent(of: .value, with: { snapshot in
if let mapRefs = snapshot.childSnapshot(forPath: "favorite_maps").value as? [String: Any] {
let mapKeys = mapRefs.keys //note mapKeys is a Dict so we can directly access the keys
for key in mapKeys {
let mapRef = self.ref.child("allMaps").child(key)
mapRef.observeSingleEvent(of: .value, with: { mapSnapshot in
let name = mapSnapshot.childSnapshot(forPath: "mapUserName").value as? String ?? "No Name"
print(name)
})
}
}
})
}
enter image description here
I have a userProfile file:
class UserProfile {
var uid:String
var email: String
var username:String
var photoURL:URL
init(uid:String, email:String, username:String, photoURL:URL) {
self.uid = uid
self.email = email
self.username = username
self.photoURL = photoURL
}
}
and a Post file
class Post {
var id:String
var author:UserProfile
var text:String
var timestamp:Date
init(id:String, author:UserProfile, text:String,timestamp:Double) {
self.id = id
self.author = author
self.text = text
self.timestamp = Date(timeIntervalSince1970: timestamp / 1000)//divided by 1000 because firebase stores dates as milliseconds
}
}
This is the way it shows in firebaseenter image description here
So what I'm trying to do is reuse the references(username, date, and urlimage) that the two files before uses.
Here the code thats used for the post file:
func oberseverRoomatePostFeed(){
let postRef = Database.database().reference().child("posts")
postRef.observe(.value, with: { snapshot in
var currentUserRoomatePose = [Post]()//temporary array
//array****************************
for child in snapshot.children {
if let roommateSnapshot = child as? DataSnapshot,
let dict = roommateSnapshot.value as? [String:Any],
let author = dict["author"] as? [String:Any],
let uid = author["uid"] as? String,
let photoURL = author["photoURL"] as? String,
let email = author["email"] as? String,
let username = author["username"] as? String,
let url = URL(string:photoURL),
let text = dict["text"] as? String,
let timeStamp = dict["timestamp"] as? Double{
let userP = UserProfile(uid: uid, email: email, username: username, photoURL: url)
let post = Post(id: roommateSnapshot.key, author: userP, text: text, timestamp: timeStamp)
currentUserRoomatePose.append(post)
}
}
self.posts = currentUserRoomatePose
self.tableView.reloadData()
})
}
And here is what I have so far
class User: NSObject {
var name: String?
var currentUser: UserProfile
var currentPost: Post
init(dictionary: [String: Any], currentUser:UserProfile, currentPost:Post) {
self.name = dictionary["name"] as? String ?? ""
self.currentUser = currentUser
self.currentPost = currentPost
}
}
and
func fetchUser() {
let postRef = Database.database().reference().child("users")
postRef.observe(.value, with: { snapshot in
var currentUsers = [User]() // temp array
for child in snapshot.children {
if let userSnapshot = child as? DataSnapshot,
let dict = userSnapshot.value as? [String: Any],
let author = dict["author"] as? [String:Any],
let uid = author["uid"] as? String,
let photoURL = author["photoURL"] as? String,
let email = author["email"] as? String,
let username = author["username"] as? String,
let url = URL(string:photoURL),
let text = dict["text"] as? String,
let timeStamp = dict["timestamp"] as? Double {
let userP = UserProfile(uid: uid, email: email, username: username, photoURL: url)
let user = User(dictionary: [String : Any], currentUser: userP, currentPost: Post)
}
}
})
}
enter image description here
enter image description here
func checkLogin() {
if Auth.auth().currentUser?.uid == nil {
perform(#selector(backButton), with:nil, afterDelay:0)
} else {
let uid = Auth.auth().currentUser?.uid
Database.database().reference().child("Users/profile/").child(uid!).observeSingleEvent(of: .value, with: {(snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
self.navigationItem.title = dictionary["username"] as? String
}
}, withCancel: nil)
}
}
"Users" : {
"kFjK5Kcrk7dLCnd3fQOnhBcPQHz1" : {
"Email" : "cmhughes95#gmail.com",
"Full Name" : "Cameron Hughes",
"Google UID" : "112185374105612274429",
"provider" : "Google"
},
"profile" : {
"0Ef8GJch5PPZ8yE9jLSXAS7fVoK2" : {
"email" : "teclarke#aggies.ncat.edu",
"password" : "Tecl6013",
"photoURL" : "https://firebasestorage.googleapis.com/v0/b/aggie-wallet.appspot.com/o/user%2F0Ef8GJch5PPZ8yE9jLSXAS7fVoK2?alt=media&token=62827fc7-38ec-47ae-9972-c078ef1d486e",
"username" : "tec95"
},
"EsqtPIFUWQbXh0ItLWK0W3qxOdI2" : {
"email" : "teclarke#aggies.ncat.edu",
"password" : "Tecl6013",
"photoURL" : "https://firebasestorage.googleapis.com/v0/b/aggie-wallet.appspot.com/o/user%2FEsqtPIFUWQbXh0ItLWK0W3qxOdI2?alt=media&token=40c82e6e-cc4d-4320-a0ab-434cc297567a",
"username" : "tyrek95"
},
Right off, there appears to be an issue with the users node. You've got what looks like a uid at the same level as a node called 'profile' which then contains other user id's. That's not going to work. The uid's should all be at the same level. The nodes also contain different child nodes so it's unclear what the purpose is. This would be a better structure:
users
uid_0
email: "test#test.com"
photoURL: "https://www.xxxxxx"
username: "tyrek95"
uid_1
email: "yipee#yipee.com"
photoURL: "https://www.yyyyy"
username: "someusername"
Based on comments, it appears you're trying to get a single user name to put in a titlebar - there's a whole lot of extraneous code in the question if that's the task. The checkLogin and fetchUser functions aren't called and while denormalization in the posts node is fine, it's unnecessary duplicate data - you don't need to have the email, photoURL duplicated as you know the uid and can get that from the users node
A better structure is
posts
post_0
author: "uid_0"
text: "Hello, World"
timestamp: "some time"
post_1
author: "uid_1"
text: "What's happening?"
timestamp: "some time"
To keep it simple, let's get one post and the associated user and print out what that user said in their post.
let usersRef = self.ref.child("users")
let postsRef = self.ref.child("posts")
let postNum = "post_0"
let postToGetRef = postsRef.child(postNum)
postToGetRef.observeSingleEvent(of: .value, with: { postSnap in
let postDict = postSnap.value as! [String: Any]
let uid = postDict["author"] as! String
let postText = postDict["text"] as! String
let userToGetRef = usersRef.child(uid)
userToGetRef.observeSingleEvent(of: .value, with: { userSnap in
let userDict = userSnap.value as! [String: Any]
let userName = userDict["username"] as! String
print("\(userName) said \(postText)") //here you put the name in the title bar
})
})
and the output is
tyrek95 said Hello, World
I did this for a single post but it could be easily expanded by using .value on the posts node, which will read in all of the posts, and then iterate over them in a for..loop to get the post information and the user for each post.
Note there's no error checking here for brevity.
I am attempting to simply read into the database that is structured as stated below. I am attempting to read the user's "userType" and use it in the following if statements below. Any help is appreciated!
Swift Code:
// Create firebase reference and link to database
var dataRef : DatabaseReference?
dataRef = Database.database().reference()
let userID = Auth.auth().currentUser!.uid // Get the User's ID
// Gather user's type (Customer or Company)
/*Use this space to gather the user's type into some variable named currUserType*/
if (currUserType == "Customer"){
self.performSegue(withIdentifier: "LoginToCustomer", sender: self)
print("User: " + userID + " has been signed in!")
}
else if (currUserType == "Company"){
self.performSegue(withIdentifier: "LoginToHost", sender: self)
}
else{
self.showMessage(alertTitle: "Error",
alertMessage: "Please report the following error with a description of what lead to to the error.",
actionTitle: "Dismiss")
}
Database Structure:
"Users" : {
"ZFH0lFe1fIb5bwSO2Q95ektD33L2" : {
"email" : "cust#test.com",
"userType" : "Customer"
}
First take the ref like i have took below:
let dbRef = Database.database().reference().child("Users")
Then create model like i have created below:
class Users {
var email: String?
var userType: String?
init(email: String, userType: String) {
self.email = email
self.userType = userType
}
}
Then create completion Handler like i have created below:
func getUsersData(handler: #escaping (_ usersArray: [Users]) -> ()) {
var usersArray = [Users]()
dbRef.observe(.value) { (datasnapshot) in
guard let usersnapshot = datasnapshot.children.allObjects as? [DataSnapshot] else { return }
for user in usersnapshot {
let email = user.childSnapshot(forPath: "email").value as! String
let userType = user.childSnapshot(forPath: "userType").value as! String
let userObj = Users(email: email, userType: userType)
usersArray.append(userObj)
}
handler(usersArray)
}
}
simply call this function which returns the whole array of users.
Refrence https://firebase.google.com/docs/database/ios/read-and-write#reading_and_writing_data
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 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)
}
}
}