Use Case and Repository Patterns
Defining use cases as Swift protocols and implementing repository interfaces.
Use Case and Repository Patterns is a free Swift Academy lesson on CoddyKit. This is lesson 2 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, and your progress syncs across the web and the CoddyKit app. The Swift Academy course includes 4 lessons in total.
What is a Use Case?
A use case (interactor) encapsulates a single business action, orchestrating domain entities and repositories.
struct PlaceOrderUseCase {
let cart: CartRepository
let payment: PaymentService
let orders: OrderRepository
func execute(userId: Int) async throws -> Order {
let items = try await cart.items(for: userId)
let receipt = try await payment.charge(items: items)
return try await orders.create(receipt: receipt)
}
}Single Responsibility
Each use case does exactly one thing. Multiple responsibilities mean multiple use cases.
// One use case per business action:
struct FetchUserUseCase { ... }
struct UpdateUserUseCase { ... }
struct DeleteUserUseCase { ... }Use Case as a Protocol
Define a protocol for the use case so it can be mocked in presentation layer tests.
protocol PlacingOrder {
func execute(userId: Int) async throws -> Order
}
struct PlaceOrderUseCase: PlacingOrder { ... }What is a Repository?
A repository abstracts the data source. Callers interact with a Swift protocol; the implementation may be network, Core Data, or in-memory.
protocol OrderRepository {
func create(receipt: PaymentReceipt) async throws -> Order
func list(userId: Int) async throws -> [Order]
func cancel(orderId: Int) async throws
}Repository Implementation
The concrete repository translates domain calls into network or database operations.
struct RemoteOrderRepository: OrderRepository {
func create(receipt: PaymentReceipt) async throws -> Order {
let dto = receipt.toDTO()
let data = try await api.post("/orders", body: dto)
return try JSONDecoder().decode(OrderDTO.self, from: data).toDomain()
}
func list(userId: Int) async throws -> [Order] { ... }
func cancel(orderId: Int) async throws { ... }
}In-Memory Repository for Tests
Provide an in-memory repository for fast, deterministic tests without I/O.
final class InMemoryOrderRepo: OrderRepository {
var orders: [Order] = []
func create(receipt: PaymentReceipt) async throws -> Order {
let order = Order(id: orders.count + 1, items: receipt.items)
orders.append(order)
return order
}
func list(userId: Int) async throws -> [Order] { orders }
func cancel(orderId: Int) async throws { orders.removeAll { $0.id == orderId } }
}Composing Use Cases
Use cases can call other use cases for shared sub-operations, keeping each focused.
struct CheckoutUseCase {
let validateCart: ValidateCartUseCase
let placeOrder: PlaceOrderUseCase
let notifyUser: NotifyUserUseCase
func execute(userId: Int) async throws -> Order {
try await validateCart.execute(userId: userId)
let order = try await placeOrder.execute(userId: userId)
try await notifyUser.execute(order: order)
return order
}
}Repository vs Service
Repository: CRUD over a single entity type. Service: orchestrates multiple entities or calls external APIs without direct storage.
// Repository: single entity
protocol UserRepository { func getUser(id: Int) async throws -> User }
// Service: cross-entity operation
protocol AuthService { func login(email: String, password: String) async throws -> Session }Caching in Repositories
Repositories can transparently cache results without the use case knowing the implementation detail.
struct CachedUserRepository: UserRepository {
private var cache: [Int: User] = [:]
private let remote: UserRepository
func getUser(id: Int) async throws -> User {
if let cached = cache[id] { return cached }
let user = try await remote.getUser(id: id)
cache[id] = user
return user
}
func saveUser(_ user: User) async throws { try await remote.saveUser(user) }
}Testability
Because use cases depend on protocol repositories, you can inject stubs to test business logic in isolation.
let repo = InMemoryOrderRepo()
let sut = PlaceOrderUseCase(orders: repo, ...)
let order = try await sut.execute(userId: 1)
XCTAssertEqual(repo.orders.count, 1)Error Translation
Repositories should translate data-layer errors (network, DB) into domain errors before returning.
func getUser(id: Int) async throws -> User {
do {
let data = try await api.get("/users/\(id)")
return try decode(data)
} catch let e as URLError where e.code == .notConnectedToInternet {
throw DomainError.offline
}
}Quick Check
What is the key difference between a Repository and a Service in Clean Architecture?
Lesson Recap
Use cases encapsulate single business actions and depend on repository protocols. Repositories abstract data sources behind a protocol. Use in-memory repositories for fast tests. Repositories translate infrastructure errors into domain errors. Services orchestrate multi-entity operations.
Learn Swift with an AI tutor — free
Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.
- Courses
- 122
- Lessons
- 409
Frequently Asked Questions
Is the “Use Case and Repository Patterns” lesson free?
Yes — any 3 lessons of the Swift Academy learning path, including “Use Case and Repository Patterns”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The Swift Academy course includes 4 lessons in total.
What will I learn in “Use Case and Repository Patterns”?
Defining use cases as Swift protocols and implementing repository interfaces. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Swift Academy?
No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 2 of 4.
How long does the “Use Case and Repository Patterns” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Swift Academy lesson?
Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Layers: Domain, Data and Presentation
- Use Case and Repository Patterns
- Dependency Inversion with Protocol Abstractions
- Wiring Layers Together without a DI Framework