How to add a user with setValue from Firebase Reading and writing data in ios - ios

I am a newbie in Swift and I am learning by building a social media application.
I am struck at trying to implement self.ref.child("users").child(user.uid).setValue(["username": username]) in my code (from https://firebase.google.com/docs/database/ios/read-and-write).
I have been following the instructions of Kasey Schlaudt and at this point of the tutorial https://youtu.be/GrRggN41VF0?t=619 he tried to add a user with setValue as shown in the Firebase documentation I have linked. The errors I get in the line self.ref.child("users").child(user.uid).setValue(["username": username]) are
Use of unresolved identifier 'user' and Use of unresolved identifier 'username'.
My code so far (with some little changes from the original code in the video in my signInPress function) is
import UIKit
import Firebase
import SwiftKeychainWrapper
class ViewController: UIViewController {
#IBOutlet weak var UserImageView: UIButton!
#IBOutlet weak var usernameField: UITextField!
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
override func viewDidLoad()
{
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool)
{
if let _ : Bool = KeychainWrapper.standard.string(forKey: "uid") != nil
{
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
func storeUserData(userID: String)
{
//---------------------------problematic line---------------------------
//from https://firebase.google.com/docs/database/ios/read-and-write
//from https://youtu.be/GrRggN41VF0?t=619
self.ref.child("users").child(user.uid).setValue(["username": username])
([
"username": usernameField.text
])
}
#IBAction func signInPress(_ sender: Any)
{
//this way you make sure there is a property inside emailField.text and you have a variable you can easily use
if let email = emailField.text, let password = passwordField.text
{
Auth.auth().signIn(withEmail: email, password: password)
{ (result, error) in
if error != nil && self.usernameField.text!.isEmpty && self.UserImageView.image != nil
{
Auth.auth().createUser(withEmail: email, password: password)
{ (result, error) in
self.storeUserData(userID: (result?.user.uid)!)
KeychainWrapper.standard.set((result?.user.uid)!, forKey: "KEY_UID")
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
} else
{
KeychainWrapper.standard.set((result?.user.uid)!, forKey: "KEY_UID")
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
}
}
}
I would very much appreciate any indication as to why the error does not occur for Kasey and what I might need to change to do the same process.
Thank you in advance !

You're actually pretty close, more of a typo issue. See that your storeUserData function is expecting a string called userID? That's what's needed in the line to store that data instead of user.uid
func storeUserData(userID: String) {
let username = self.usernameField.text
self.ref.child("users").child(userID).setValue(["username": username])
here ^^^^^^ userID instead of user.uid
}

Related

Swift - Accessing implicitly unwrapped variable gives a nil error

I'm following a tutorial on CoreData and I've been following it exactly, yet when they run the app, everything works and saves correctly, yet I get a nil error. The tutorial is a few years old, so I'm not sure if something has been udpated in the way CoreData works. It's an app to save goals.
Here's the first view controller where you enter the text of the goal and if it is short or long term:
import UIKit
class CreateGoalViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var goalTextView: UITextView!
#IBOutlet weak var shortTermButton: UIButton!
#IBOutlet weak var longTermButton: UIButton!
#IBOutlet weak var nextButton: UIButton!
var userGoalType: GoalType = .shortTerm
override func viewDidLoad() {
super.viewDidLoad()
nextButton.bindToKeyboard()
shortTermButton.setSelectedColor()
longTermButton.setDeselectedColor()
print("\(userGoalType)")
goalTextView.delegate = self
}
#IBAction func nextButtonPressed(_ sender: Any) {
if goalTextView.text != "" && goalTextView.text != "What is your goal?" {
guard let finishVC = storyboard?.instantiateViewController(withIdentifier: "FinishVC") as? FinishGoalViewController else {return}
finishVC.initData(description: goalTextView.text!, type: userGoalType)
print("\(finishVC.goalType.rawValue) after next button pressed")
performSegue(withIdentifier: "goToFinish", sender: self)
}
}
#IBAction func longTermButtonPressed(_ sender: Any) {
userGoalType = .longTerm
longTermButton.setSelectedColor()
shortTermButton.setDeselectedColor()
print("\(userGoalType)")
}
#IBAction func shortTermButtonPressed(_ sender: Any) {
userGoalType = .shortTerm
shortTermButton.setSelectedColor()
longTermButton.setDeselectedColor()
print("\(userGoalType)")
}
#IBAction func backButtonPressed(_ sender: Any) {
dismiss(animated: true)
}
func textViewDidBeginEditing(_ textView: UITextView) {
goalTextView.text = ""
goalTextView.textColor = UIColor(ciColor: .black)
}
}
And here's the following view controller where you set the number of times you want to do that goal where the CoreData functions are:
import UIKit
import CoreData
class FinishGoalViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var createButton: UIButton!
#IBOutlet weak var pointsTextField: UITextField!
var goalDescription: String!
var goalType: GoalType!
func initData(description: String, type: GoalType) {
self.goalDescription = description
self.goalType = type
}
override func viewDidLoad() {
super.viewDidLoad()
createButton.bindToKeyboard()
pointsTextField.delegate = self
}
#IBAction func createGoalPressed(_ sender: Any) {
if pointsTextField.text != ""{
self.save { finished in
if finished {
dismiss(animated: true)
}
}
}
}
#IBAction func backButtonPressed(_ sender: Any) {
dismiss(animated: true)
}
func save(completion: (_ finished: Bool) -> ()) {
guard let managedContext = appDelegate?.persistentContainer.viewContext else {return}
let goal = Goal(context: managedContext)
goal.goalDescription = goalDescription
goal.goalType = goalType.rawValue
goal.goalCompletionValue = Int32(pointsTextField.text!)!
goal.goalProgress = Int32(0)
do{
try managedContext.save()
print("successfully saved data")
completion(true)
}catch{
debugPrint("Could not save: \(error.localizedDescription)")
completion(false)
}
}
}
I'm getting a nil error in the save function with the goalType.rawValue turning up nil. The goal type is set up in an enum file:
import Foundation
enum GoalType: String {
case longTerm = "Long Term"
case shortTerm = "Short Term"
}
I'm not sure why there's an error. Because in the CreateGoalViewController, I print the goalType.rawValue from the following view controller and it comes up with the correct string, either short or long-term. But when FinishGoalViewController loads, it is all of a sudden nil.
You are initiating and configuring your FinishGoalViewController in nextButtonPressed but you never use it. performSegue(withIdentifier: "goToFinish", sender: self) will create and push a new instance of FinishGoalViewController.
The most simple aproach would be to push your allready configured controller from your curent Controller. Remove performSegue(... and use.
self.navigationController?.pushViewController(finishVC, animated: true)
If you still want to use the segue, remove everything from the nextButtonPressed function, leaving just the performSegue(... line. After that add this function to your CreateGoalViewController controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToFinish" {
if let finishVC = segue.destination as? FinishGoalViewController {
// configure finshVC here
}
}
}

Value of type 'AuthDataResult' has no member 'uid' problem

I am a newbie in Swift, learning from building a social media app following a tutorial of Kasey Schlaudt on youtube. When I write this line KeychainWrapper.standard.set((user?.uid)!, forKey: "KEY_UID") at minutes 36:11 if this video: https://youtu.be/gBB5tnAzjjo?t=2171 I get this error —> Value of type 'AuthDataResult' has no member 'uid'. Any suggestions on why this might be happening?
This is my code so far:
import UIKit
import Firebase
import SwiftKeychainWrapper
class ViewController: UIViewController {
#IBOutlet weak var UserImageView: UIButton!
#IBOutlet weak var usernameField: UITextField!
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func signInPress(_ sender: Any) {
if let email = emailField.text, let password = passwordField.text {
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
if error != nil {
//Create account
} else {
KeychainWrapper.standard.set((user?.uid)!, forKey: "KEY_UID")
}
}
}
}
}
Any help would be much appreciated !
The API documentation for createUser says that it provides an AuthDataResult object as the result. As you can see from link, it doesn't have a uid property. You will want to use its user property to get a User object that does have a uid.
Auth.auth().createUser(withEmail: email, password: password) { (result, error) in
if error != nil {
//Create account
} else {
KeychainWrapper.standard.set((result?.user.uid)!, forKey: "KEY_UID")
}
}

data not showing up in firebase database

im attempting to add user data via a create account view controller which contains all UITextFields (password, confirm password, first name, last name, phone number). when the create account button is tapped, the users email shows up in the authentication section on the firebase website but the user information from the first name, last name and phone number text fields are not passed into the database. I'm new to iOS development and have never used firebase so im unsure what the issue is. the app runs without crashing.
below is my Create Account view controller
thanks in advance
import UIKit
import FirebaseAuth
import QuartzCore
import FirebaseDatabase
import Firebase
class CreateAccount: UIViewController {
var refUsers: DatabaseReference!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var confirmPasswordTextField: UITextField!
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var lastNameTextField: UITextField!
#IBOutlet weak var phoneNumberTextField: UITextField!
#IBOutlet weak var alreadyHaveAccountLabel: UILabel!
#IBAction func loginButtonTapped(_ sender: Any) {
performSegue(withIdentifier: "showLoginScreen", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
self.refUsers = Database.database().reference().child("Users");
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
if Auth.auth().currentUser != nil {
print("success")
self.presentMainScreen()
}
}
#IBAction func createAccountTapped(_ sender: Any) {
if let email = emailTextField.text, let password = passwordTextField.text {
Auth.auth().createUser(withEmail: email, password: password, completion:{ user, error in
if let firebaseError = error {
print(firebaseError.localizedDescription)
return
} else {
self.addUser()
print("this is the first name:", self.firstNameTextField.text!)
print("this is the last name:", self.lastNameTextField.text!)
print("this is the phone number" , self.phoneNumberTextField.text!)
print("success")
self.presentMainScreen()
}
})
}
}
func addUser(){
let key = refUsers.childByAutoId().key
let user = ["id":key,
"FirstName":firstNameTextField.text! as String,
"LastName":lastNameTextField.text! as String,
"PhoneNumber":phoneNumberTextField.text! as String
]
refUsers.child(key).setValue(user)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
func presentMainScreen(){
let mainstoryboard = UIStoryboard(name: "Main", bundle: nil)
let mainTabController = mainstoryboard.instantiateViewController(withIdentifier: "MainTabController") as! MainTabController
mainTabController.selectedViewController = mainTabController.viewControllers?[0]
self.present(mainTabController, animated: true, completion: nil)
//let storyboard:UIStoryboard = UIStoryboard(name:"Main", bundle:nil)
//let loggedInVC:LoggedInVC = storyboard.instantiateViewController(withIdentifier: "LoggedInVC") as! LoggedInVC
//self.present(loggedInVC, animated: true, completion: nil)
}
}
Try this:
Instead of set value use update value
let childUpdates = ["/user/\(key)": user]
refUser.updateChildValues(childUpdates)
Hope this helps :)

Firebase Auth creating users

I can't seem to get this to work. The database portion works and I'm getting user info as intended in the database, but it is not creating users in Firebase Auth. For the following code, it printed "can't register."
Can someone please tell me what I'm doing wrong?
import UIKit
import Firebase
import FirebaseAuth
class AddUserTableViewController: UITableViewController, UITextFieldDelegate {
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
#IBAction func saveUserButton(_ sender: Any) {
let ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
FIRAuth.auth()?.createUser(withEmail: emailTextField.text!, password: "pass", completion: { (user, error) in
if error != nil {
print ("Can't Register")
}
else {
print ("I don't know what this means")
}
})
ref?.child("Users").childByAutoId().setValue(["First Name": self.firstNameTextField.text, "Email": self.emailTextField.text])
}
Just include Firebase, you don't need to include FirebaseAuth as well on each page.
Here's my working code for FireBase login, I did this from a Youtube tutorial a few weeks ago.
import UIKit
import Firebase
class LoginController: UIViewController {
#IBOutlet weak var menuButton:UIBarButtonItem!
#IBOutlet weak var signinSelector: UISegmentedControl!
#IBOutlet weak var signinLabel: UILabel!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var signinButton: UIButton!
var isSignIn:Bool = true
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func signinSelectorChanged(_ sender: UISegmentedControl) {
//Flip the boolean true to false
isSignIn = !isSignIn
//Check the boolean and set the buttons and labels
if isSignIn {
signinLabel.text = "Sign In"
signinButton.setTitle("Sign In", for: .normal)
}
else {
signinLabel.text = "Register"
signinButton.setTitle("Register", for: .normal)
}
}
#IBAction func signinButtonTapped(_ sender: UIButton) {
//Do some form validation on email and password
if let email = emailTextField.text, let pass = passwordTextField.text
{
//Check if it's signed or register
if isSignIn {
//Sign in the user with Firebase
Auth.auth().signIn(withEmail: email, password: pass, completion: { (user, error) in
//Check that user isn't nil
if let u = user {
//User is found, goto home screen
self.performSegue(withIdentifier: "goToHome", sender: self)
}
else{
//Error: Check error and show message
}
})
}
else {
//Register the user with Firebase
Auth.auth().createUser(withEmail: email, password: pass, completion: { (user, error) in
//Check that user isn't NIL
if let u = user {
//User is found, goto home screen
self.performSegue(withIdentifier: "goToHome", sender: self)
}
else {
//Check error and show message
}
})
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//Dismiss the keyboard when the view is tapped on
emailTextField.resignFirstResponder()
passwordTextField.resignFirstResponder()
}
}

Swift - Login with FireBase - Can't retrieve user data after login

Firebase Authentication - Can't retrieve user data after login.
I'm trying to retrieve the users email after sign in (logincontroller) and display it in a UILABEL on another controller (maincontroller). When you create a user or use an existing one, it works fine the first time after launching the app, but when you sign out and try to use another email it does not work.
I have two view controllers:
import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
class LoginViewController: UIViewController {
var ref: FIRDatabaseReference!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func signInButton(sender: AnyObject) {
FIRAuth.auth()?.signInWithEmail(emailTextField.text! , password: passwordTextField.text!, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
}
})
self.performSegueWithIdentifier("MainViewSegue", sender: self)
}
#IBAction func createAccountButton(sender: AnyObject) {
FIRAuth.auth()?.createUserWithEmail(emailTextField.text!, password: passwordTextField.text!, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
} else {
print("User Created.")
let userID: String = user!.uid
let userEmail:String = self.emailTextField.text!
self.ref.child("users").child(userID).setValue(["email": userEmail])
}
})
}
}
and:
import Foundation
import UIKit
import Firebase
import FirebaseDatabase
class MainViewController: UIViewController {
var ref: FIRDatabaseReference!
var refHandle: UInt!
#IBOutlet weak var userEmailLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
refHandle = ref.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
let dataDict = snapshot.value as! [String: AnyObject]
print((dataDict))
})
let userID: String = (FIRAuth.auth()?.currentUser?.uid)!
ref.child("users").child(userID).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let userEmail = snapshot.value!["email"] as! String
self.userEmailLabel.text = userEmail
})
}
#IBAction func signOutButton(sender: AnyObject) {
try! FIRAuth.auth()!.signOut()
if let storyboard = self.storyboard {
let viewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController")
self.presentViewController(viewController, animated: false, completion: nil)
}
}
}
This is where I get the error EXC_BAD_INSTRUCTION:
let userID: String = (FIRAuth.auth()?.currentUser?.uid)!
CMD+CLICK on signInWithEmail in FIRAuth.auth()?.signInWithEmail(emailT... and you will be directed to its documentation, above that function in its documentation you will see :-
#param completion Optionally; a block which is invoked when the sign in flow finishes, or is
canceled. Invoked asynchronously on the main thread in the future.
which means your completionBlock is invoked when your user is either signed in or signInWithEmail function has given some error.But in your case self.performSegueWithIdentifier("MainViewSegue", sender: self) will get called even before your completionBlock: is called.
Try this:-
#IBAction func signInButton(sender: AnyObject) {
FIRAuth.auth()?.signInWithEmail(emailTextField.text! , password: passwordTextField.text!, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
}else if error == nil{
self.performSegueWithIdentifier("MainViewSegue", sender: self)
}
})
}
Also replace :-
let userID: String = (FIRAuth.auth()?.currentUser?.uid)!
with
let userID: String = FIRAuth.auth()!.currentUser!.uid
No need to forcefully unwrap an optional value which you know does exist

Resources