iOS

User log in and log out

Introduction

In the user registration guide we learned how to integrate a signup option into an iOS app using the Back4App platform and the ParseSwift SDK. Once a user successfully signs up in your app, logging in and logging out actions are key features within the app’s flow.

The ParseSwift SDK will allow us to integrate these features seamlessly into any iOS app.

Prerequisites

To complete this quickstart, you need:

Goal

To implement a user login and logout feature using the ParseSwift SDK and the Back4App platform.

Step 1 - Setting up the login and logout features

Before starting to implement any login functionality, we have to create the object that will represent the user. For simplicity, we will reuse the same User struct (which conforms to the ParseUser protocol) we introduced in the user registration guide:

1
2
3
4
5
6
7
8
9
10
11
12
13
import Foundation
import ParseSwift

struct User: ParseUser {
  ...
    
  var username: String?
  var email: String?
  var emailVerified: Bool?
  var password: String?
    
  var age: Int?
}

We recommend following the user registration guide and registering at least one user to use it as an example for this guide.

Similar to the signup process, logging in requires a form where the user enters their username and password. Then, we perform a login request using the corresponding methods provided by the ParseSwift SDK. In its turn, Back4App processes the request and returns a response containing the login information. When an error occurs, the response returns information to identify and handle this error.

The logout process is straightforward. The ParseSwift SDK allows us to implement it in a single line of code.

Step 2 - Setting up the app

Once you connected your Xcode project to your Back4App application, the next step is to set up the app’s user interface.

For the login process, we will implement a simple controller containing the corresponding input fields and a login button:

The class in charge of this form is called LogInController and it is a subclass of UIViewController. The key components to integrate into this controller are two UITextField’s and one UIButton. The following snippet shows the implementation of the LogInController class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import UIKit
import ParseSwift

class LogInController: UIViewController {
  private let usernameTextField: UITextField = {
    let textField = UITextField()
    textField.borderStyle = .roundedRect
    textField.placeholder = "Username *"
    textField.autocapitalizationType = .none
    textField.textAlignment = .center
    return textField
  }()
    
  private let passwordTextField: UITextField = {
    let textField = UITextField()
    textField.borderStyle = .roundedRect
    textField.isSecureTextEntry = true
    textField.placeholder = "Password *"
    textField.textAlignment = .center
    return textField
  }()
    
  private let logInButton: UIButton = {
    let button = UIButton(type: .roundedRect)
    button.setTitle("Log in", for: .normal)
    return button
  }()
    
  override func viewDidLoad() {
    super.viewDidLoad()
        
    navigationItem.title = "Back4App Log In"
        
    // Lays out the login form
    let stackView = UIStackView(arrangedSubviews: [usernameTextField, passwordTextField, logInButton])
    stackView.translatesAutoresizingMaskIntoConstraints = false
    stackView.spacing = 8
    stackView.axis = .vertical
    stackView.distribution = .fillEqually
        
    let stackViewHeight = CGFloat(stackView.arrangedSubviews.count) * (44 + stackView.spacing) - stackView.spacing
        
    view.addSubview(stackView)
    stackView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
    stackView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor).isActive = true
    stackView.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor, multiplier: 0.7).isActive = true
    stackView.heightAnchor.constraint(equalToConstant: stackViewHeight).isActive = true
        
    // Adds the method that will be called when the user taps the login button
    logInButton.addTarget(self, action: #selector(handleLogIn), for: .touchUpInside)
        
    // If the user is already logged in, we redirect them to the HomeController
    guard let user = User.current else { return }
    let homeController = HomeController()
    homeController.user = user
        
    navigationController?.pushViewController(homeController, animated: true)
  }
    
  /// Called when the user taps on the logInButton button
  @objc private func handleLogIn() {
    guard let username = usernameTextField.text, !username.isEmpty,
          let password = passwordTextField.text, !password.isEmpty else {
      // Shows an alert with the appropriate title and message.
      return showMessage(title: "Error", message: "Invalid credentials.")
    }
        
    logIn(with: username, password: password)
  }
    
  /// Logs in the user and presents the app's home screen (HomeController)
  /// - Parameters:
  ///   - username: User's username
  ///   - password: User's password
  private func logIn(with username: String, password: String) {
    // TODO: Here we will implement the login process
  }
}

Additionally, the helper function showMessage(title:message:) is implemented in an extension of UIViewController:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension UIViewController {

    /// Presents an alert with a title, a message and a back button.
    /// - Parameters:
    ///   - title: Title for the alert
    ///   - message: Shor message for the alert
    func showMessage(title: String, message: String) {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
        
        alertController.addAction(UIAlertAction(title: "Back", style: .cancel))
        
        present(alertController, animated: true)
    }
}

For the logout process, we insert a button in the home controller, i.e., HomeController. This view controller will only contain the logout button and a label showing the user’s username:

The implementation of this view controller is straightforward:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import UIKit
import ParseSwift

class HomeController: UIViewController {
    
  /// When set, it updates the usernameLabel's text with the user's username.
  var user: User? {
    didSet {
      usernameLabel.text = "Hello \(user?.username ?? "N/A")!"
    }
  }
    
  private let usernameLabel: UILabel = {
    let label = UILabel()
    label.textAlignment = .center
    label.font = .boldSystemFont(ofSize: 18)
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
  }()
    
  private let logOutButton: UIButton = {
    let button = UIButton(type: .roundedRect)
    button.setTitle("Log out", for: .normal)
    button.translatesAutoresizingMaskIntoConstraints = false
    return button
  }()
    
  override func viewDidLoad() {
    super.viewDidLoad()
        
    // Sets up the layout (usernameLabel and logOutButton)
    view.backgroundColor = .systemBackground
    navigationItem.hidesBackButton = true
    navigationItem.title = "Back4App"
    view.addSubview(usernameLabel)
    view.addSubview(logOutButton)
        
    usernameLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8).isActive = true
    usernameLabel.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
        
    logOutButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 8).isActive = true
    logOutButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
        
    // Adds the method that will be called when the user taps the logout button
    logOutButton.addTarget(self, action: #selector(handleLogOut), for: .touchUpInside)
  }
    
  /// Called when the user taps the logout button.
  @objc private func handleLogOut() {
    // TODO: Here we will implement the logout process
  }
}

Step 3 - Login request

We now proceed to implement the logIn(with:password) method in the LogInController class. The ParseUser protocol gives the User object the static method login(username:password). This method prepares and sends the login request to your Back4App application. Depending on the use case, one can use one of the many implementations of the login(...) method. We now complete the logIn(with:password) method in LogInController:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class HomeController: UIViewController {
  ...

  /// Logs in the user and presents the app's home screen (HomeController)
  /// - Parameters:
  ///   - username: User's username
  ///   - password: User's password
  private func logIn(with username: String, password: String) {
    // WARNING: Use only one of the following implementations, the synchronous or asynchronous option

    // Logs in the user synchronously, it throws a ParseError error if something happened. 
    // This should be executed in a background thread!
    do {
      let loggedInUser = try User.login(username: username, password: password)

      // After the login success we send the user to the home screen
      let homeController = HomeController()
      homeController.user = loggedInUser

      navigationController?.pushViewController(homeController, animated: true)
    } catch let error as ParseError {
      showMessage(title: "Error", message: "Failed to log in: \(error.message)")
    } catch {
      showMessage(title: "Error", message: "Failed to log in: \(error.localizedDescription)")
    }
    
    // Logs in the user asynchronously
    User.login(username: username, password: password) { [weak self] result in // Handle the result (of type Result<User, ParseError>)
      switch result {
      case .success(let loggedInUser):
        self?.usernameTextField.text = nil
        self?.passwordTextField.text = nil

        // After the login success we send the user to the home screen
        let homeController = HomeController()
        homeController.user = loggedInUser

        self?.navigationController?.pushViewController(homeController, animated: true)
      case .failure(let error):
        self?.showMessage(title: "Error", message: "Failed to log in: \(error.message)")
      }
    }
  }
}

Step 4 - Logout request

The logout request is as simple as the login request. Once again, the ParseUser protocol provides the User with the static method logout(...). By calling this method the current user (accessed via User.current) logs out from your Back4App application. We will call this method when the user taps the logout button located on the home screen, i.e., in the handleLogOut() method in the HomeController class, we add the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class HomeController: UIViewController {
  ...

  /// Called when the user taps the logout button.
  @objc private func handleLogOut() {
    // WARNING: Use only one of the following implementations, the synchronous or asynchronous option

    // Logs out the user synchronously, it throws a ParseError error if something happened.
    // This should be executed in a background thread!
    do {
      try User.logout()

      // After the logout succeeded we dismiss the home screen
      navigationController?.popViewController(animated: true)
    } catch let error as ParseError {
      showMessage(title: "Error", message: "Failed to log out: \(error.message)")
    } catch {
      showMessage(title: "Error", message: "Failed to log out: \(error.localizedDescription)")
    }
    
    // Logs out the user asynchronously
    User.logout { [weak self] result in // Handle the result (of type Result<Void, ParseError>)
      switch result {
      case .success:
        // After the logout succeeded we dismiss the home screen
        self?.navigationController?.popViewController(animated: true)
      case .failure(let error):
        self?.showMessage(title: "Error", message: "Failed to log out: \(error.message)")
      }
    }
  }
}

Step 5 - Run the app!

In this repository, you will find an Xcode project containing the login and logout processes we described above. Before running the app, make sure you connected the Xcode project to your Back4App application.

Conclusion

The Back4App and the ParseSwift SDK allow us to integrate login and logout features in iOS apps in a quick way. After connecting your Back4App application with your Xcode project, the login (or logout) process only requires calling a single method.