← All topics/Architecture & design patterns

Practical interview questions

Scenario-style prompts with sample answer outlines. Focus is on how you would design and reason in real codebases.

Question 4

Repositories, use cases, and data boundaries

View models are calling URLSession directly and caching in static vars. How would you introduce a repository or use-case layer without over-engineering?

Follow-ups

  • How do you handle offline vs online in one place?

Answer outline

In plain terms, a repository is a single type (or module) your feature code asks for data. e.g. 'give me this user’s profile', without caring whether the answer comes from the network or a local persistent source.

The view model calls something like userRepository.profile(id:) instead of scattering URLSession, JSONDecoder, and file I/O across every screen.

It's a abstraction behind which you centralize logic such as: when to hit the network, when to reuse a cached value, how long a cache is valid (TTL) etc...

Principles

  • A repository is not 'the database' or 'the API' by itself, it coordinates them for one area of data (users, checkout, etc.).
  • Keep Codable network response structs near the network layer; map to domain types before the view model when JSON and app models diverge.
  • Prefer actor inside the repo if mutable cache is shared across tasks.
  • Start with one repo per aggregate (e.g. FeedRepository), not a GodRepository that knows everything.
View model depends on the abstraction, not URLSession
final class ProfileViewModel {
    private let users: UserRepository

    init(users: UserRepository) { self.users = users }

    func load() async throws {
        let user = try await users.user(id: currentId)
        // format for UI — no URLSession here
    }
}
Protocol + implementation (tests swap a fake)
protocol UserRepository {
    func user(id: String) async throws -> User
}

final class LiveUserRepository: UserRepository {
    func user(id: String) async throws -> User {
        // URLSession → decode response struct → map to User
    }
}

Follow-up angles

  • Offline-first: repository returns local immediately and refreshes in background.
  • Core Data / SwiftData stack usually lives below repositories, not inside SwiftUI views.