How do you update a users profile settings using firebase and swift? - swift

I am trying to update a users email and full name. This is my code:
func saveTapped() {
var performSegue = false
if updateEmail.text == "" && updateFullName.text == "" {
self.cleanUrCodeRohan("Please fill in one or more of the missing text fields that you would like to update.")
}
if updateEmail.text != "" {
let user = FIRAuth.auth()?.currentUser
user?.updateEmail(updateEmail.text!) { error in
self.ref.child("users").child(self.currentUser).child("email").setValue(self.updateEmail.text!)
}
let emailUpdateRef = FIRDatabase.database().reference().child(currentUser).child("email")
print(emailUpdateRef)
emailUpdateRef.setValue(self.updateEmail.text)
performSegue = true
}
if updateFullName.text != "" {
let user = FIRAuth.auth()?.currentUser
if let user = user {
let changeRequest = user.profileChangeRequest()
changeRequest.displayName = self.updateFullName.text!
}
performSegue = true
}
if performSegue == true {
self.navigationController!.popViewControllerAnimated(true)
}
}
I am able to update the email under authorization but not under the database. Any help would be appreciated.

If JSON tree is something like this:-
appName{
users :{
userID :{
email : "..",
username : ".."
}
}
}
Use this Code to update your node's child value's:-
func saveTapped(){
if ((updateEmail.text != "" || updateFullName.text != "") && (updateEmail.text != nil || updateFullName.text != nil)){
let userRef = FIRDatabase.database().reference().child("users").child(FIRAuth.auth()!.currentUser!.uid)
if let new_Email = updateEmail.text as? String{
FIRAuth.auth()!.currentUser!.updateEmail(updateEmail.text!) { error in
if error == nil{
userRef.updateChildValues(["email" : new_Email ], withCompletionBlock: {(errEM, referenceEM) in
if errEM == nil{
print(referenceEM)
}else{
print(errEM?.localizedDescription)
}
})
}
}else{
self.cleanUrCodeRohan("Email couldn't be updated in auth")
}
}
if let new_Name = updateFullName.text as? String{
userRef.updateChildValues(["username" : new_Name ], withCompletionBlock: {(errNM, referenceNM) in
if errNM == nil{
print(referenceNM)
}else{
print(errNM?.localizedDescription)
}
})
}
}else{
self.cleanUrCodeRohan("Please fill in one or more of the missing text fields that you would like to update.")
}
}

Related

Filter Firebase Data SwiftUI

This code adds all the data in a single array. In HomeViev I use to Foreach and I added to data to list. But I have to split the data in two. status collection has two type "active" and "closed" but I don't know how can I filter
import SwiftUI
import Combine
import Firebase
let dbCollection = Firestore.firestore().collection("Signals")
class FirebaseSession : ObservableObject {
#Published var session: User? { didSet { self.didChange.send(self) }}
#Published var data = [Signal]()
var didChange = PassthroughSubject<FirebaseSession, Never>()
var handle: AuthStateDidChangeListenerHandle?
func listen () {
handle = Auth.auth().addStateDidChangeListener { (auth, user) in
if let user = user {
print("Got user: \(user)")
self.session = User(uid: user.uid, email: user.email)
self.readData()
} else {
self.session = nil
}
}
}
func readData() {
dbCollection.addSnapshotListener { (documentSnapshot, err) in
if err != nil {
print((err?.localizedDescription)!)
return
}else {
print("read data success")
}
documentSnapshot!.documentChanges.forEach { i in
// Read real time created data from server
if i.type == .added {
let id = i.document.documentID
let symbol = i.document.get("symbol") as? String ?? ""
let status = i.document.get("status") as? String ?? ""
self.data.append(Signal(id: id, symbol: symbol, status: status))
}
// Read real time modify data from server
if i.type == .modified {
self.data = self.data.map { (eachData) -> Signal in
var data = eachData
if data.id == i.document.documentID {
data.symbol = i.document.get("symbol") as! String
data.status = i.document.get("status") as? String ?? ""
return data
}else {
return eachData
}
}
}
// When data is removed...
if i.type == .removed {
let id = i.document.documentID
for i in 0..<self.data.count{
if self.data[i].id == id{
self.data.remove(at: i)
return
}
}
}
}
}
}
}
The question states
But I have to split the data in two
I assume that means two arrays; one for active and one for closed.
var activeData = [...
var closedData = [...
There are a couple of ways to do that
1)
Query Firestore for all status fields equal to active and load those documents into the active array and then another query for status fields equal closed and load those in the the closed array
2)
I would suggest a simpler approach
if i.type == .added {
let id = i.document.documentID
let symbol = i.document.get("symbol") as? String ?? ""
let status = i.document.get("status") as? String ?? ""
if status == "active" {
self.activeData.append(Signal(id: id, symbol: symbol, status: status))
} else {
self.closedData.append(Signal(id: id, symbol: symbol, status: status))
}
}
and do the same thing within .modified and .removed; identify the status so the code will know which array to remove it from.
EDIT:
Based on a comment
I don't know how to query this codes.
I am providing code to query for signals that are active. This code will return only active signals and as signals become active, inactive etc, this will modify a signalArray to stay in sync with the data.
let dbCollection = Firestore.firestore().collection("Signals")
let query = dbCollection.whereField("status", isEqualTo: "active").addSnapshotListener( { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
let signalToAdd = Signal(withDoc: diff.document)
self.signalArray.append(signalToAdd)
}
if (diff.type == .modified) {
let docId = diff.document.documentID
if let indexOfSignalToModify = self.signalArray.firstIndex(where: { $0.signal_id == docId} ) {
let signalToModify = self.signalArray[indexOfSignalToModify]
signalToModify.updateProperties(withDoc: diff.document)
}
}
if (diff.type == .removed) {
let docId = diff.document.documentID
if let indexOfSignalToRemove = self.signalArray.firstIndex(where: { $0.signal_id == docId} ) {
self.signalArray.remove(at: indexOfSignalToRemove)
}
}
}
})
Note that my Signal Class has an initializer that accepts a QueryDocumentSnapshot to initialize it as well as a .updateProperties function to update its internal properties.

Firestore database does not store data

I've created an app in which, when the user signs up, should create two values in the Firestore server in the user's document, that is level and subjects (meaning it's in /users/userid).
I've tried manually creating the 'users' collection, but nothing is being created when the user signs up.
The following is my code (SignUpViewController):
import Firebase
var reference: DocumentReference!
func firebaseAuth() {
let userDisplayName = textfieldDisplayName.text!
let userEmail = textfieldEmail.text!
let userPassword = textfieldPassword.text!
if userEmail == "" || userPassword == "" {
labelMessage.isHidden = false
labelMessage.textColor = UIColor.red
labelMessage.text = "Error: A compulsory field is left blank."
} else {
Auth.auth().createUser(withEmail: userEmail, password: userPassword) { (user, error) in
if user != nil && error == nil {
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = userDisplayName
changeRequest?.commitChanges(completion: { (error) in
if error == nil {
let userID = Auth.auth().currentUser?.uid
let dataToSave: [String: Any] = ["level":0, "subjects":[""]]
self.reference = Firestore.firestore().collection("users").document(userID ?? "")
self.reference.setData(dataToSave, completion: { (error) in
if error == nil {
self.performSegue(withIdentifier: "presentInitial", sender: self)
} else {
self.labelMessage.isHidden = false
self.labelMessage.textColor = UIColor.red
self.labelMessage.text = "Error: \(error?.localizedDescription ?? "")"
}
})
self.performSegue(withIdentifier: "presentInitial", sender: self)
} else {
self.labelMessage.isHidden = false
self.labelMessage.textColor = UIColor.red
self.labelMessage.text = "Error: \(error?.localizedDescription ?? "")"
}
})
} else {
self.labelMessage.isHidden = false
self.labelMessage.textColor = UIColor.red
self.labelMessage.text = "Error: \(error?.localizedDescription ?? "")"
}
}
}
}
The following code is from another View Controller which SignUpViewController redirects to (HomeViewController):
import Firebase
var reference: DocumentReference!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let userID = Auth.auth().currentUser?.uid
reference.getDocument { (docSnapshot, error) in // Fatal error occured here
let data = docSnapshot?.data()
let userLevel = data?["level"] as? String ?? ""
if userLevel == "" {
self.performSegue(withIdentifier: "performSetup", sender: self)
}
}
}
I expected that when redirected to the homepage (segued through presentInitial), the homepage will then read the value of 'level'. However, the app crashed with a fatal error: "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value" where 'level' was meant to be read from the server.
I think the problem is not with Firestore. According to the error message, the code wraps an optional value but failed because it is nil, so the problem is probably about these three lines:
let userDisplayName = textfieldDisplayName.text!
let userEmail = textfieldEmail.text!
let userPassword = textfieldPassword.text!
Sometimes, when a UITextField has no text, its text is nil instead of "", which may cause the problem. You can replace the three lines with the following:
let userDisplayName = textfieldDisplayName.text ?? ""
let userEmail = textfieldEmail.text ?? ""
let userPassword = textfieldPassword.text ?? ""
In this way, these three variables will always be "" when there are no text, and your logic of checking blank fields will still work.
Edit: For future reference, the real problem is not in the question but in the comments of this answer.
Did you check your database rules ?
you need to setup the rules when to work and when not to so no one can access the database but your apps
for a test now use this code to verify it woks then you should change it
{
"rules": {
".read": true,
".write": true
}
}

I want to check the validation for both mobile number and email id in single textfield. How should I validate this?

I want to check the validation for both "Email id " and "mobile number" in single textfield. How should I validate the conditions for the same textfield?
Maybe this will help:
extension String {
var isPhoneNumber: Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.phoneNumber.rawValue)
let matches = detector.matches(in: self, options: [], range: NSMakeRange(0, self.count))
if let res = matches.first {
return res.resultType == .phoneNumber && res.range.location == 0 && res.range.length == self.count
} else {
return false
}
} catch {
return false
}
}
}
extension String {
var isInt: Bool {
return Int(self) != nil
}
}
func isValidEmail(testStr:String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailTest = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailTest.evaluate(with: testStr)
}
if textField.text.characterCount > 6 {
if textField.text.isInt == true {
print("this might be users phone number"
} else if isValidEmail(testStr:textField.text) {
print("this might be users mail")
} else {
print("user entered wrong value or nothing")
}

Confusion about setting a optional variable

I want to add the option for a user to add their phone number. If they add any phone number I want to add an alert informing them if they have not added a valid 10 digit phone number. However if they do not add anything in the phone number field I want the phoneInput variable to be set to "0". How would I go about doing this.
var phoneInput = ""
func signUp(){
if profileImage.image == nil {
showAvatarError()
} else if phoneNumber.text == "" {
self.phoneInput = "0"
} else if (phoneNumber.text?.characters.count)! != 10 {
showphoneNumberError()
}else if email.text == "" {
showEmailError()
}else if isValid(email.text!) != true{
showEmailError()
} else{
submitPressed()
print("Set info")
}
}
I'm not sure why you get the result that you do but here is a cleaner version
var phoneInput = ""
func signUp(){
// This check doesn't have anything to do with the number, so separe it
if profileImage.image == nil {
showAvatarError()
return
}
guard let temp = planEndValue.text else {
return
}
let userInput = temp.trimmingCharacters(in: .whitespaces)
if userInput.count == 0 {
self.phoneInput = "0"
} else if userInput.count != 10 {
showphoneNumberError()
}
}

Creating users in new Firebase and Swift

I'm trying to make a register view, where the users can register through email, password, username, phone number and city.
Here's the code for creating the users through email and password:
#IBAction func Register(sender: AnyObject) {
guard let email = EmailTextField.text?.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) where !email.isEmpty else {
print("Email is empty")
return
}
guard let password = PasswordTextField.text where !password.isEmpty
else {
print("Password is empty")
return
}
guard let repeatPassword = RePasswordTextField.text where !repeatPassword.isEmpty
else {
print("Repeat Password is empty")
return
}
guard password == repeatPassword else {
print("Password does not match")
return
}
FIRAuth.auth()?.createUserWithEmail(EmailTextField.text!, password: PasswordTextField.text!, completion: { user, error in
if let error = error {
print(error)
} else {
if let user = user {
print(user)
}
}
})
}
the question is how can I add the other values like username to firebase dashboard?
in the previous version of firebase I was creating a dictionary then use the setValue function!
But in the new firebase how can I do that?
For storing data in user's respective uid
let ref = FIRDatabase.database().reference() //Global Variable
...
...
#IBAction func Register(sender: AnyObject) {
guard let email = EmailTextField.text?.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) where !email.isEmpty else {
print("Email is empty")
return
}
guard let password = PasswordTextField.text where !password.isEmpty
else {
print("Password is empty")
return
}
guard let repeatPassword = RePasswordTextField.text where !repeatPassword.isEmpty
else {
print("Repeat Password is empty")
return
}
guard password == repeatPassword else {
print("Password does not match")
return
}
FIRAuth.auth()?.createUserWithEmail(EmailTextField.text!, password: PasswordTextField.text!, completion: { user, error in
if let error = error {
print(error)
} else {
ref.child("UserProfile").child(user!.uid).setValue([
"username" : usernameTextField.text!, //Or whatever way you are extracting these information
"phoneNumber" : phoneNumberTextField.text!, //Or whatever way you are extracting these information
"city" : cityTextField.text! //Or whatever way you are extracting these information
])
}
})
}
Your JSON tree will look something like this :-
{UserProfile : {
user_ID_1 :{
userName : "Tim",
phoneNumber : +1(23)....,
city : "Los Angeles"
}
user_ID_2 :{
userName : "Hnery",
phoneNumber : +1(073)....,
city : "New York"
}
}
}