Dependency Inversion with Protocol Abstractions
Depending on abstractions not concretions using Swift protocols as boundaries.
Dependency Inversion with Protocol Abstractions is a free Swift Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Dependency Inversion Principle
High-level modules should not depend on low-level modules. Both should depend on abstractions (protocols).
// WITHOUT DIP:
class OrderService { let db = SQLiteDatabase() } // depends on concrete
// WITH DIP:
class OrderService { let db: Database } // depends on abstractionDefining the Abstraction
Extract a protocol that captures the contract your high-level module needs.
protocol Database {
func save<T: Encodable>(_ value: T, key: String) throws
func load<T: Decodable>(key: String) throws -> T
}Concrete Implementations
Provide multiple concrete implementations behind the protocol: production, test, and future alternatives.
class SQLiteDatabase: Database { ... }
class InMemoryDatabase: Database { ... }
class KeychainDatabase: Database { ... }Constructor Injection
Pass the abstraction through the initializer — the most explicit and testable injection style.
class ProfileService {
private let db: Database
init(db: Database) { self.db = db }
}Why Not Singletons for Everything?
Global singletons hide dependencies, making code hard to test and reason about.
// Hard to test:
class Service { func run() { MySingleton.shared.doWork() } }
// Easy to test:
class Service { let dep: MyProtocol; func run() { dep.doWork() } }Protocol Abstractions in Swift
Swift protocols can carry associated types, default implementations, and composition — ideal for rich abstractions.
protocol Cacheable {
associatedtype Key: Hashable
associatedtype Value
func get(_ key: Key) -> Value?
func set(_ key: Key, value: Value)
}Abstraction Granularity
Interface Segregation: prefer narrow protocols over fat ones so implementations only provide what callers need.
// Fat (avoid):
protocol DataStore {
func save(...); func load(...); func delete(...); func query(...); func migrate()
}
// Narrow (prefer):
protocol Readable { func load<T>(...) throws -> T }
protocol Writable { func save<T>(...) throws }DIP with Combine
Return AnyPublisher from protocol methods so the underlying publisher type remains hidden.
protocol UserStream {
func userUpdates() -> AnyPublisher<User, Never>
}DIP with async/await
Protocol methods marked async throws are fully compatible with Swift structured concurrency.
protocol Analytics {
func track(event: String) async
}
struct FirebaseAnalytics: Analytics {
func track(event: String) async { /* Firebase call */ }
}DIP at the App Level
Wire up concrete implementations at the app's entry point (Composition Root), keeping all binding in one place.
@main
struct MyApp: App {
let db: Database = SQLiteDatabase()
let repo: UserRepository = RemoteUserRepository(db: db)
let useCase = FetchUserUseCase(repo: repo)
var body: some Scene {
WindowGroup { ContentView(useCase: useCase) }
}
}Testing with DIP
In tests, substitute real implementations with mocks/stubs at the injection site.
let mockDB = InMemoryDatabase()
let service = ProfileService(db: mockDB)
// Exercise service
XCTAssertEqual(try mockDB.load(key: "profile"), expectedProfile)Quick Check
What does the Dependency Inversion Principle state about high-level modules?
Lesson Recap
DIP: high-level modules depend on protocol abstractions; concrete types implement them. Extract narrow protocols, inject via constructors, and wire everything at the Composition Root. This enables swapping implementations for testing, feature flagging, and future requirements.
Frequently asked questions
Is the “Dependency Inversion with Protocol Abstractions” lesson free?
Yes — the full text of “Dependency Inversion with Protocol Abstractions” is free to read here on the web, and the Swift Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Swift Academy course, upgrade to CoddyKit PRO.
What will I learn in “Dependency Inversion with Protocol Abstractions”?
Depending on abstractions not concretions using Swift protocols as boundaries. 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; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Dependency Inversion with Protocol Abstractions” 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