import UIKit
protocol AuthService {
func login(username: String, password: String, completion: @escaping (Bool) -> Void)
}
class MockAuthService: AuthService {
func login(username: String, password: String, completion: @escaping (Bool) -> Void) {
// Simulate network delay
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
if username == "user" && password == "pass" {
completion(true)
} else {
completion(false)
}
}
}
}
class LoginViewController: UIViewController {
private let usernameField = UITextField()
private let passwordField = UITextField()
private let loginButton = UIButton(type: .system)
private let statusLabel = UILabel()
var authService: AuthService = MockAuthService()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupUI()
}
private func setupUI() {
usernameField.placeholder = "Username"
usernameField.borderStyle = .roundedRect
usernameField.autocapitalizationType = .none
usernameField.translatesAutoresizingMaskIntoConstraints = false
passwordField.placeholder = "Password"
passwordField.borderStyle = .roundedRect
passwordField.isSecureTextEntry = true
passwordField.translatesAutoresizingMaskIntoConstraints = false
loginButton.setTitle("Login", for: .normal)
loginButton.addTarget(self, action: #selector(loginButtonTapped), for: .touchUpInside)
loginButton.translatesAutoresizingMaskIntoConstraints = false
statusLabel.text = ""
statusLabel.textAlignment = .center
statusLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(usernameField)
view.addSubview(passwordField)
view.addSubview(loginButton)
view.addSubview(statusLabel)
NSLayoutConstraint.activate([
usernameField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40),
usernameField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
usernameField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
passwordField.topAnchor.constraint(equalTo: usernameField.bottomAnchor, constant: 20),
passwordField.leadingAnchor.constraint(equalTo: usernameField.leadingAnchor),
passwordField.trailingAnchor.constraint(equalTo: usernameField.trailingAnchor),
loginButton.topAnchor.constraint(equalTo: passwordField.bottomAnchor, constant: 30),
loginButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
statusLabel.topAnchor.constraint(equalTo: loginButton.bottomAnchor, constant: 30),
statusLabel.leadingAnchor.constraint(equalTo: usernameField.leadingAnchor),
statusLabel.trailingAnchor.constraint(equalTo: usernameField.trailingAnchor)
])
}
@objc func loginButtonTapped() {
let username = usernameField.text ?? ""
let password = passwordField.text ?? ""
statusLabel.text = "Logging in..."
authService.login(username: username, password: password) { [weak self] success in
DispatchQueue.main.async {
self?.statusLabel.text = success ? "Login Successful!" : "Login Failed. Try again."
}
}
}
}We defined a protocol AuthService with a login method that takes username, password, and a completion handler.
The MockAuthService class simulates a login by checking if username is "user" and password is "pass" after a short delay.
The LoginViewController sets up UI elements: two text fields, a button, and a label.
When the login button is tapped, it calls authService.login and updates the status label based on success or failure.
This setup allows easy swapping of AuthService implementations for real or mock authentication, which is great for testing.