In this tutorial, I am going show you how to create a user sign up page, as well as a user login page. The user’s account will be saved and authenticated in Firebase. For the sake of keeping the post specific to the topic, I am going to assume you already have a Firebase account attached to your Xcode project.
In Main.storyboard, add 2 view controllers to the already existing one (there should be 3 view controllers after this).
The left view controller will be the login page, the middle view controller will be the sign up page, and the right view controller will be the home page.
On the left view controller, add 2 textfields (one for login, one for password) and 2 buttons (one for signing in, one for signing up).
On the middle view controller, add a textfield for each the user’s name, email, and password at least. I added a confirm password textfield as well, but that is optional. Add 2 buttons on this view controller as well (one to submit registration, and one to go back to the login page).
On the last view controller, add 2 labels (one to display the user’s name, one to display the user’s email), and a button for sign out.
Now we want to connect each view controller. From the first view controller, do ctrl-click-drag the sign up button onto the middle view controller (click “Show” when prompted). From the middle view controller, do ctrl-click-drag the done button onto the last view controller (click “Show” when prompted).
It’s now time to create the files to code in for each view controller. In your Xcode project, click File > New > File > Cocoa Touch Class > Next > and give your view controller the title LoginViewController (leave everything else in the window the same) > Next > save to your project file > Create. Do this for the other two files as well (named RegisterViewController and HomeViewController respectively).
Back in Main.storyboard, connect your view controllers to their respective files in the Identity Inspector.
Connect your buttons and textfields to your respective files, and import Firebase.
Below I’ve attached each file along with detailed comments explaining why I added the code I did.
LoginViewController:
import UIKit import Firebase import FirebaseAuth import SwiftKeychainWrapper class LoginViewController: UIViewController, UITextFieldDelegate { //connecting and naming the textfields from Main.storyboard @IBOutlet weak var email: UITextField! @IBOutlet weak var password: UITextField! //in here, we want to write everything that should be called when //the view loads. override func viewDidLoad() { super.viewDidLoad() //we want the email and password delegates to come from the LoginViewController self.email.delegate = self self.password.delegate = self //calling the function from the extension to hide the keyboard when //the user taps around it. self.hideKeyboardWhenTappedAround() } /* This function authenticates the user when the Sign In button is pressed. The Auth function provided by Firebase reads in the information provided in the email and password textfields, and checks to see if it matches any accounts already registered in Firebase. If there are no accounts under this email, the console will print an error message saying that this account does not exist. If the password is incorrect, the console will state that the password is incorrect or the email is incorrect. Otherwise, the user will be logged in and directed to the HomeViewController (I did not include a perform segue function here because the sign in button is the only button from the sign in page that will lead to the HomeViewController, and we already established the segue in Main.storyboard).*/ @IBAction func signInPressed(_ sender: Any){ //reading in the email and password textfields, and using the sign in function //provided by Firebase to authenticate the user. Auth.auth().signIn(withEmail: email.text!, password: password.text!) { (user, error) i //if error does not exists if(error == nil) { print("Signed in!") } else { //error exists print("Error logging in: \(error?.localizedDescription)") self.dismiss(animated: true, completion: nil) } } } /*This function allows the app to segue to the RegisterViewController when the Sign Up button is pressed on the LoginViewController*/ @IBAction func signUpPressed(_ sender: Any) { //it is not necessary to include this line unless the sign up button //could lead to 2 different VC's. self.performSegue(withIdentifier: "toSignUp", sender: self) } /* This function is written here but actually called on the sign out page. It unwinds the whole app and brings it back to the login page.*/ @IBAction func unwindToVC1(segue:UIStoryboardSegue) { //after unwinding the app, I want to reset the email and password //textfields on the login page email.text = "" password.text = "" } /* This function causes the blinking "|" text bar to jump to the next textfield when the user hits the return button on the keyboard. It makes typing in multiple textfields much more convenient for the user.*/ func textFieldShouldReturn(_ textField: UITextField) -> Bool { //if the current textfield is email and the user hits "return" if textField == email { //this textfield is no longer the responder (blinking "|" bar) textField.resignFirstResponder() //this textfield is now the responder password.becomeFirstResponder() } else if textField == password { textField.resignFirstResponder() } //return true so that when the function is called again we know which //responder to use return true } } /* This extension is used to control when the keyboard appears on the screen, and when it disappears.*/ extension UIViewController { //this function causes the keyboard to disappear when the user //taps on the screen anywhere. It makes it much easier for users //to hide the keyboard when they are done typing in the textfields. func hideKeyboardWhenTappedAround() { let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard)) tap.cancelsTouchesInView = false view.addGestureRecognizer(tap) } @objc func dismissKeyboard() { view.endEditing(true) } }
RegisterViewController
import UIKit import Firebase import FirebaseAuth import FirebaseDatabase class registerViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate { //connecting textfields from Main.storyboard @IBOutlet weak var nameTF: UITextField! @IBOutlet weak var emailTF: UITextField! @IBOutlet weak var passwordTF: UITextField! @IBOutlet weak var confirmTF: UITextField! override func viewDidLoad() { super.viewDidLoad() //calling extension function created in LoginViewController //extensions can be called anywhere self.hideKeyboardWhenTappedAround() //setting the name/email/password/confirm password //delegates to the Register View Controller self.nameTF.delegate = self self.emailTF.delegate = self self.passwordTF.delegate = self self.confirmTF.delegate = self } //this function does the same as the other textFieldShouldReturn function //in the LoginViewController where every time the user hits the enter //button on the keyboard, the blinking "|" bar moves to the next textfield func textFieldShouldReturn(_ textField: UITextField) -> Bool { if textField == nameTF { textField.resignFirstResponder() emailTF.becomeFirstResponder() } else if textField == emailTF { textField.resignFirstResponder() passwordTF.becomeFirstResponder() } else if textField == passwordTF { textField.resignFirstResponder() confirmTF.becomeFirstResponder() } else if textField == confirmTF { textField.resignFirstResponder() } return true } //if there is a problem and we want to display an error message, //we call this function. The UIAlertController displays a pop-up //error message with the title "Oops!" and the userMessage underneath. func displayError(userMessage:String) { var myAlert = UIAlertController(title: "Oops!", message: userMessage, preferredStyle: UIAlertControllerStyle.alert) let okAction = UIAlertAction(title:"OK", style: UIAlertActionStyle.default, handler: nil) myAlert.addAction(okAction) self.present(myAlert, animated: true, completion: nil) } //When the user is done filling out the registration form, and hits the //done button, we first want to make sure there are no empty textfields, //and that the password meets the requirements set by Firebase, as well as //make sure the passwordTF and confirmTF match @IBAction func donePressed(_ sender: Any) { let email = emailTF.text! let password = passwordTF.text! //if any of the textfields are empty if(nameTF.text?.isEmpty)! || (emailTF.text?.isEmpty)! || (passwordTF.text?.isEmpty)! || (confirmTF.text?.isEmpty)! { displayError(userMessage: "All fields are required.") return } //if passwordTF and confirmTF do not match if(passwordTF.text! != confirmTF.text!) { displayError(userMessage: "Passwords do not match.") return } //if the password does not meet security requirements if((passwordTF.text?.characters.count)! < 6) { displayError(userMessage: "Password must be at least 6 characters long.") return } //creating user by calling the createUser function provided by Firebase Auth.auth().createUser(withEmail: email, password: password) { (authResult, error) in // there is an error if(error != nil) { print("Error: \(String(describing: error?.localizedDescription))") self.dismiss(animated: true, completion: nil) } else { print("User Created!") //save the data into the Firebase Database to use and refer to later. In order to do this we will create //a path, and enter in the data using JSON format (key:value pairs) Database.database().reference().child("users").child((Auth.auth().currentUser?.uid)!).setValue([ "Name" : self.nameTF.text!, "Email" : self.emailTF.text!, "Password" : self.passwordTF.text!, "UID" : (Auth.auth().currentUser?.uid)! ]) } //end else statement } //end Auth function }//end of done function //if the user wants to exit the RegisterViewController @IBAction func cancelPressed(_ sender: Any) { self.dismiss(animated: true, completion: nil) } }
HomeViewController:
import UIKit import Firebase import FirebaseDatabase import FirebaseAuth class HomeViewController: UIViewController { @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var emailLabel: UILabel! //create a variable to reference the database var ref: DatabaseReference! override func viewDidLoad() { super.viewDidLoad() //setting ref to the start of the path that will lead to the Firebase Database ref = Database.database().reference() //set userID equal to the current user who is logged in let userID = (Auth.auth().currentUser?.uid)! /*call the path to to the current logged in user, and observe the elements within the child node. The snapshot will hold all of the key:value pairs*/ ref.child("users").child(userID).observe(.value) { (snapshot) in //we only want the values, not the keys. let value = snapshot.value as? NSDictionary //create variables to set the values in, casted as String let email = value?["Email"] as? String ?? "" let name = value?["Name"] as? String ?? "" //assign the newly created variables to the labels displayed on the VC. self.emailLabel.text = email self.nameLabel.text = name } } //Call this function when the user hits the sign out button @IBAction func signOutPressed(_ sender: Any) { //this function was copied from the Firebase website let firebaseAuth = Auth.auth() do { try firebaseAuth.signOut() print("Signed out!") //we want to call the unwind segue function we created in the //LoginViewController to unwind everything until we are back at the login page. performSegue(withIdentifier: "unwindToVC1", sender: self) //Print the error in the console if there is an error } catch let signOutError as NSError { print ("Error signing out: %@", signOutError) } } }
Now run your program. You should be able to allow a user to create an account, and save that information in the Authentication page and Database in the Firebase Console, authenticate the user when signing in, grab user data from Firebase Database to display the current logged in user’s information on the home screen, and safely and securely log out of the application.