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:
- Xcode.
- An application created on Back4App.
- Follow the New Parse App tutorial to learn how to create a Parse application on the Back4App platform.
- Note: Follow the Install ParseSwift SDK Tutorial to create an Xcode Project connected to Back4App.
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.