Abstraction
Essence
Abstraction means extracting what matters and hiding implementation details behind a clear contract. We say "image loader", not "URLSession plus disk cache plus decoding". Each level of abstraction lets you reason about a task without holding the lower levels in your head. It is the main tool for fighting complexity.
In Swift an abstraction is a protocol
Swift has no abstract classes, so the contract is described by a protocol and implementations are any types (most often structs).
import Foundation
protocol ImageLoader {
func load(_ url: URL) async throws -> Data
}
struct NetworkImageLoader: ImageLoader {
func load(_ url: URL) async throws -> Data {
try await URLSession.shared.data(from: url).0
}
}
struct CachedImageLoader: ImageLoader {
let base: any ImageLoader
func load(_ url: URL) async throws -> Data {
// check cache, otherwise delegate to `base`
try await base.load(url)
}
}
// Client code knows only the abstraction:
func makeLoader() -> some ImageLoader {
CachedImageLoader(base: NetworkImageLoader())
}
The client works with ImageLoader and has no idea there is a network and a cache behind it. The implementation can be swapped for a stub in tests, and some ImageLoader hides the concrete type even in the signature.
Abstraction vs encapsulation
- Abstraction answers "what can the object do" (the contract).
- Encapsulation answers "how does it do it and who has access" (protecting state).
- They work together: a protocol defines the abstraction, access levels provide the encapsulation.
What the interviewer looks for
- Being able to name a bad abstraction: leaking details (a
loadFromDiskmethod in a genericImageLoader), interfaces that are too wide (ISP violation), abstraction for its own sake (a protocol with a single implementation and no tests). - Understanding that
URLSession,FileManagerandUIVieware also abstractions over sockets, the file system and Core Animation.