Bird
Raised Fist0
iOS Swiftmobile~5 mins

Repository pattern in iOS Swift

Choose your learning style10 modes available
Introduction

The Repository pattern helps keep your app organized by separating data access from the rest of your code. It acts like a middleman between your app and data sources.

When you want to keep your code clean and easy to change.
When your app gets data from different places like a server or local storage.
When you want to test your app easily without real data sources.
When you want to change where data comes from without changing the whole app.
Syntax
iOS Swift
protocol Repository {
    associatedtype Item
    func fetchAll() -> [Item]
    func add(_ item: Item)
    func remove(_ item: Item)
}

This is a simple protocol defining what a repository should do.

Use associatedtype to make it work with any data type.

Examples
This example shows a UserRepository that stores users in memory.
iOS Swift
struct User {
    let id: Int
    let name: String
}

class UserRepository: Repository {
    typealias Item = User
    private var users = [User]()

    func fetchAll() -> [User] {
        return users
    }

    func add(_ item: User) {
        users.append(item)
    }

    func remove(_ item: User) {
        users.removeAll { $0.id == item.id }
    }
}
This example shows a repository that could fetch users from a server.
iOS Swift
class RemoteUserRepository: Repository {
    typealias Item = User

    func fetchAll() -> [User] {
        // Imagine fetching from a server here
        return [User(id: 1, name: "Alice")]
    }

    func add(_ item: User) {
        // Send user to server
    }

    func remove(_ item: User) {
        // Remove user from server
    }
}
Sample App

This program creates a user repository, adds two users, then removes one. It prints the list of users before and after removal.

iOS Swift
import Foundation

struct User: Equatable {
    let id: Int
    let name: String
}

protocol Repository {
    associatedtype Item
    func fetchAll() -> [Item]
    func add(_ item: Item)
    func remove(_ item: Item)
}

class UserRepository: Repository {
    typealias Item = User
    private var users = [User]()

    func fetchAll() -> [User] {
        return users
    }

    func add(_ item: User) {
        users.append(item)
    }

    func remove(_ item: User) {
        users.removeAll { $0.id == item.id }
    }
}

// Using the repository
let repo = UserRepository()
repo.add(User(id: 1, name: "Alice"))
repo.add(User(id: 2, name: "Bob"))
print("Users after adding:", repo.fetchAll())
repo.remove(User(id: 1, name: "Alice"))
print("Users after removing Alice:", repo.fetchAll())
OutputSuccess
Important Notes

The Repository pattern helps you swap data sources easily without changing your app logic.

Keep repository methods simple and focused on data operations.

Use protocols to define repository behavior for flexibility and testing.

Summary

The Repository pattern separates data access from app logic.

It makes your code cleaner and easier to maintain.

You can switch data sources without changing the rest of your app.

Practice

(1/5)
1.

What is the main purpose of the Repository pattern in iOS Swift development?

easy
A. To separate data access logic from the rest of the app
B. To handle UI animations smoothly
C. To manage app navigation between screens
D. To store user interface layout information

Solution

  1. Step 1: Understand the role of Repository pattern

    The Repository pattern is designed to isolate data access logic from business logic in an app.
  2. Step 2: Identify the correct purpose

    Separating data access makes the app easier to maintain and test, which matches To separate data access logic from the rest of the app.
  3. Final Answer:

    To separate data access logic from the rest of the app -> Option A
  4. Quick Check:

    Repository pattern = separate data access [OK]
Hint: Repository = data access separation [OK]
Common Mistakes:
  • Confusing Repository with UI or navigation roles
  • Thinking Repository manages animations
  • Assuming Repository stores UI layout
2.

Which of the following is the correct way to declare a repository protocol in Swift?

?
easy
A. struct UserRepository { func fetchUsers() -> [User] }
B. class UserRepository { func fetchUsers() -> [User] }
C. enum UserRepository { func fetchUsers() -> [User] }
D. protocol UserRepository { func fetchUsers() -> [User] }

Solution

  1. Step 1: Identify correct Swift syntax for protocols

    Protocols in Swift are declared with the keyword protocol followed by method signatures without bodies.
  2. Step 2: Match the correct declaration

    protocol UserRepository { func fetchUsers() -> [User] } correctly declares a protocol with a method signature. Other options use class, struct, or enum which are not protocols.
  3. Final Answer:

    protocol UserRepository { func fetchUsers() -> [User] } -> Option D
  4. Quick Check:

    Protocol declaration uses protocol keyword [OK]
Hint: Protocols start with 'protocol' keyword [OK]
Common Mistakes:
  • Using class or struct instead of protocol for interface
  • Adding method bodies inside protocol
  • Using enum incorrectly for repository
3.

Given the following code snippet, what will be printed?

protocol UserRepository {
  func fetchUsers() -> [String]
}

class MockUserRepository: UserRepository {
  func fetchUsers() -> [String] {
    return ["Alice", "Bob"]
  }
}

let repo: UserRepository = MockUserRepository()
print(repo.fetchUsers())
medium
A. []
B. ["Alice", "Bob"]
C. nil
D. Compilation error

Solution

  1. Step 1: Understand protocol conformance and method implementation

    The class MockUserRepository conforms to UserRepository and implements fetchUsers() returning ["Alice", "Bob"].
  2. Step 2: Analyze the print statement

    The variable repo is typed as UserRepository but holds an instance of MockUserRepository. Calling fetchUsers() returns ["Alice", "Bob"], which is printed.
  3. Final Answer:

    ["Alice", "Bob"] -> Option B
  4. Quick Check:

    Protocol method returns mock data [OK]
Hint: Protocol variable calls concrete implementation [OK]
Common Mistakes:
  • Expecting empty array instead of returned data
  • Thinking protocol variable can't call methods
  • Assuming compilation error due to protocol
4.

Identify the error in this repository implementation:

protocol UserRepository {
  func fetchUsers() -> [String]
}

class UserRepo: UserRepository {
  func fetchUsers() {
    return ["Alice", "Bob"]
  }
}
medium
A. Protocol method has wrong name
B. Method does not return a value
C. Missing return type in method signature
D. Class does not conform to protocol

Solution

  1. Step 1: Compare protocol and class method signatures

    The protocol method fetchUsers() returns [String]. The class method lacks the return type in its signature.
  2. Step 2: Identify the mismatch causing error

    Because the class method signature does not match the protocol, it causes a compilation error. The method must declare the return type.
  3. Final Answer:

    Missing return type in method signature -> Option C
  4. Quick Check:

    Method signatures must match protocol [OK]
Hint: Method signature must match protocol exactly [OK]
Common Mistakes:
  • Forgetting to add return type in implementation
  • Assuming method name mismatch
  • Thinking protocol conformance is automatic
5.

You want to switch your data source from a local database to a remote API without changing your app's business logic. How does the Repository pattern help you achieve this?

hard
A. By defining a repository interface and implementing separate classes for each data source
B. By embedding API calls directly in the UI code
C. By storing all data in global variables accessible everywhere
D. By using a single class that mixes local and remote data fetching

Solution

  1. Step 1: Understand the role of repository interface

    The Repository pattern uses an interface (protocol) to abstract data access methods.
  2. Step 2: Implement separate classes for each data source

    You create one class for local database access and another for remote API, both conforming to the same interface. This allows switching without changing business logic.
  3. Final Answer:

    By defining a repository interface and implementing separate classes for each data source -> Option A
  4. Quick Check:

    Interface + multiple implementations = easy data source switch [OK]
Hint: Use interface + multiple classes for data sources [OK]
Common Mistakes:
  • Mixing data fetching code in UI layer
  • Using global variables for data access
  • Combining local and remote logic in one class
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