Bird
Raised Fist0
iOS Swiftmobile~20 mins

Mock objects and protocols in iOS Swift - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Login Screen
A simple login screen that uses a protocol for authentication. We will create a mock object to simulate the login process for testing.
Target UI
---------------------
|     Login Screen    |
|-------------------|
| Username: ________ |
| Password: ________ |
|                   |
|   [Login Button]   |
|                   |
| Status: __________ |
---------------------
Create a protocol named AuthService with a login function.
Implement a MockAuthService that conforms to AuthService and simulates login success or failure.
Build a LoginViewController with username and password text fields and a login button.
When login button is tapped, call the login function from AuthService.
Display login success or failure message in a status label.
Starter Code
iOS Swift
import UIKit

protocol AuthService {
    func login(username: String, password: String, completion: @escaping (Bool) -> Void)
}

class LoginViewController: UIViewController {
    // TODO: Add UI elements and AuthService property
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        // TODO: Setup UI
    }
    
    @objc func loginButtonTapped() {
        // TODO: Call login on AuthService
    }
}
Task 1
Task 2
Task 3
Task 4
Task 5
Task 6
Solution
iOS Swift
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.

Final Result
Completed Screen
---------------------
|     Login Screen    |
|-------------------|
| Username: user     |
| Password: *****    |
|                   |
|   [Login Button]   |
|                   |
| Status: Login Successful! |
---------------------
User types username and password.
User taps Login button.
Status label shows 'Logging in...' briefly.
After delay, status label updates to 'Login Successful!' if credentials are correct, else 'Login Failed. Try again.'
Stretch Goal
Add a loading spinner that appears while login is in progress and disappears after completion.
💡 Hint
Use UIActivityIndicatorView and show it when login starts, hide it in the completion handler.

Practice

(1/5)
1. What is the main purpose of a mock object in Swift testing?
easy
A. To improve app performance in production
B. To simulate real objects for testing without using actual implementations
C. To create user interface elements dynamically
D. To store user data securely

Solution

  1. Step 1: Understand the role of mock objects

    Mock objects are used to imitate real objects during testing to isolate code behavior.
  2. Step 2: Identify the purpose in testing context

    They help test code without relying on real implementations that may be complex or unavailable.
  3. Final Answer:

    To simulate real objects for testing without using actual implementations -> Option B
  4. Quick Check:

    Mock objects = simulate real objects [OK]
Hint: Mocks imitate real parts only for testing [OK]
Common Mistakes:
  • Confusing mocks with production code
  • Thinking mocks improve app speed
  • Using mocks for UI creation
2. Which of the following is the correct way to declare a protocol in Swift?
easy
A. interface MyProtocol { func doSomething() }
B. class MyProtocol { func doSomething() }
C. protocol MyProtocol { func doSomething() }
D. struct MyProtocol { func doSomething() }

Solution

  1. Step 1: Recall Swift syntax for protocols

    Protocols in Swift are declared using the keyword protocol.
  2. Step 2: Compare options

    protocol MyProtocol { func doSomething() } uses protocol keyword correctly; others use class, struct, or interface which are incorrect for protocols.
  3. Final Answer:

    protocol MyProtocol { func doSomething() } -> Option C
  4. Quick Check:

    Swift protocol declaration = protocol keyword [OK]
Hint: Protocols start with the keyword 'protocol' [OK]
Common Mistakes:
  • Using class or struct instead of protocol
  • Using 'interface' which is not Swift syntax
  • Missing the protocol keyword
3. Given the protocol and mock class below, what will be printed when calling mock.fetchData()?
protocol DataFetcher {
  func fetchData() -> String
}

class MockFetcher: DataFetcher {
  func fetchData() -> String {
    return "Mock Data"
  }
}

let mock = MockFetcher()
print(mock.fetchData())
medium
A. "Real Data"
B. Runtime crash
C. Compilation error
D. "Mock Data"

Solution

  1. Step 1: Analyze the protocol and mock class

    The protocol requires a method fetchData() returning a String. The mock class implements it returning "Mock Data".
  2. Step 2: Check the print statement output

    Calling mock.fetchData() returns "Mock Data", so that is printed.
  3. Final Answer:

    "Mock Data" -> Option D
  4. Quick Check:

    Mock method returns "Mock Data" [OK]
Hint: Mock returns its own defined value, not real data [OK]
Common Mistakes:
  • Assuming it returns 'Real Data'
  • Expecting a compile error due to protocol
  • Thinking it crashes at runtime
4. What is wrong with the following mock class implementation?
protocol Service {
  func execute() -> Bool
}

class MockService: Service {
  func execute() {
    print("Executed")
  }
}
medium
A. The mock class does not implement the protocol method signature correctly
B. The mock class should be a struct, not a class
C. The protocol cannot be implemented by a class
D. The method should return Void, not Bool

Solution

  1. Step 1: Compare protocol method signature with mock method

    The protocol requires execute() to return a Bool, but the mock method returns Void.
  2. Step 2: Identify the mismatch causing error

    This mismatch causes a compile error because the mock does not conform properly.
  3. Final Answer:

    The mock class does not implement the protocol method signature correctly -> Option A
  4. Quick Check:

    Method signature mismatch = error [OK]
Hint: Method signatures must match exactly in mocks [OK]
Common Mistakes:
  • Ignoring return type mismatch
  • Thinking class vs struct matters here
  • Believing protocols can't be implemented by classes
5. You want to test a view controller that depends on a network service. Which approach correctly uses a mock object and protocol to isolate the test?
protocol NetworkService {
  func fetchData(completion: (String) -> Void)
}

class MockNetworkService: NetworkService {
  func fetchData(completion: (String) -> Void) {
    completion("Test Data")
  }
}

// In test:
let mockService = MockNetworkService()
let viewController = MyViewController(service: mockService)
viewController.loadData()
What is the benefit of this setup?
hard
A. It allows testing the view controller without real network calls
B. It speeds up the real network requests
C. It automatically updates the UI without code
D. It stores data permanently on the device

Solution

  1. Step 1: Understand the use of protocol and mock

    The protocol defines the network service interface. The mock provides test data without real network calls.
  2. Step 2: Identify the testing benefit

    This setup isolates the view controller from real network dependencies, making tests reliable and fast.
  3. Final Answer:

    It allows testing the view controller without real network calls -> Option A
  4. Quick Check:

    Mock isolates tests from real network [OK]
Hint: Mocks avoid real network calls in tests [OK]
Common Mistakes:
  • Thinking mocks speed up real network
  • Assuming UI updates automatically
  • Confusing mocks with data storage
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard