--------------------- | User List | --------------------- | * Alice | | * Bob | | * Charlie | | | | [Refresh] | ---------------------
Repository pattern in iOS Swift - Mini App: Build & Ship
import SwiftUI // TODO: Define User model here // TODO: Define UserRepository protocol here // TODO: Implement MockUserRepository here struct UserListView: View { // TODO: Add @State properties for users and loading state // TODO: Add repository property var body: some View { NavigationView { VStack { // TODO: Show loading indicator or user list Button("Refresh") { // TODO: Load users from repository } .padding() } .navigationTitle("User List") } .onAppear { // TODO: Load users when view appears } } } struct UserListView_Previews: PreviewProvider { static var previews: some View { UserListView() } }
import SwiftUI struct User: Identifiable { let id: Int let name: String } protocol UserRepository { func fetchUsers() async -> [User] } class MockUserRepository: UserRepository { func fetchUsers() async -> [User] { try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 second delay return [ User(id: 1, name: "Alice"), User(id: 2, name: "Bob"), User(id: 3, name: "Charlie") ] } } struct UserListView: View { @State private var users: [User] = [] @State private var isLoading = false private let repository: UserRepository = MockUserRepository() var body: some View { NavigationView { VStack { if isLoading { ProgressView("Loading...") .padding() } else { List(users) { user in Text(user.name) } } Button("Refresh") { Task { await loadUsers() } } .padding() } .navigationTitle("User List") } .task { await loadUsers() } } private func loadUsers() async { isLoading = true users = await repository.fetchUsers() isLoading = false } } struct UserListView_Previews: PreviewProvider { static var previews: some View { UserListView() } }
We start by defining a simple User struct with an id and name. This lets SwiftUI identify each user uniquely in the list.
The UserRepository protocol declares a method fetchUsers() that returns users asynchronously. This hides the data source details from the UI.
MockUserRepository implements this protocol and simulates a network delay using Task.sleep. It returns a fixed list of users.
In UserListView, we use @State properties to hold the users and loading state. The repository is a constant instance of MockUserRepository.
The UI shows a ProgressView while loading, and a List of user names when done. The Refresh button triggers reloading.
We use Swift concurrency with async and await to fetch users without blocking the UI. The .task modifier loads users when the view appears.
This pattern cleanly separates data fetching logic from UI code, making it easier to maintain and test.
--------------------- | User List | --------------------- | * Alice | | * Bob | | * Charlie | | | | [Refresh] | ---------------------
Practice
What is the main purpose of the Repository pattern in iOS Swift development?
Solution
Step 1: Understand the role of Repository pattern
The Repository pattern is designed to isolate data access logic from business logic in an app.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.Final Answer:
To separate data access logic from the rest of the app -> Option AQuick Check:
Repository pattern = separate data access [OK]
- Confusing Repository with UI or navigation roles
- Thinking Repository manages animations
- Assuming Repository stores UI layout
Which of the following is the correct way to declare a repository protocol in Swift?
?Solution
Step 1: Identify correct Swift syntax for protocols
Protocols in Swift are declared with the keywordprotocolfollowed by method signatures without bodies.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.Final Answer:
protocol UserRepository { func fetchUsers() -> [User] } -> Option DQuick Check:
Protocol declaration usesprotocolkeyword [OK]
- Using class or struct instead of protocol for interface
- Adding method bodies inside protocol
- Using enum incorrectly for repository
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())Solution
Step 1: Understand protocol conformance and method implementation
The classMockUserRepositoryconforms toUserRepositoryand implementsfetchUsers()returning ["Alice", "Bob"].Step 2: Analyze the print statement
The variablerepois typed asUserRepositorybut holds an instance ofMockUserRepository. CallingfetchUsers()returns ["Alice", "Bob"], which is printed.Final Answer:
["Alice", "Bob"] -> Option BQuick Check:
Protocol method returns mock data [OK]
- Expecting empty array instead of returned data
- Thinking protocol variable can't call methods
- Assuming compilation error due to protocol
Identify the error in this repository implementation:
protocol UserRepository {
func fetchUsers() -> [String]
}
class UserRepo: UserRepository {
func fetchUsers() {
return ["Alice", "Bob"]
}
}Solution
Step 1: Compare protocol and class method signatures
The protocol methodfetchUsers()returns[String]. The class method lacks the return type in its signature.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.Final Answer:
Missing return type in method signature -> Option CQuick Check:
Method signatures must match protocol [OK]
- Forgetting to add return type in implementation
- Assuming method name mismatch
- Thinking protocol conformance is automatic
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?
Solution
Step 1: Understand the role of repository interface
The Repository pattern uses an interface (protocol) to abstract data access methods.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.Final Answer:
By defining a repository interface and implementing separate classes for each data source -> Option AQuick Check:
Interface + multiple implementations = easy data source switch [OK]
- Mixing data fetching code in UI layer
- Using global variables for data access
- Combining local and remote logic in one class
Ready to prove it?
Pass the test and this pattern moves unknown
