Browse Questions

Answer: Inject dependencies through init using protocols. This lets tests swap real implementations with mocks, making tests fast and deterministic.

Code Example:

// Define behaviour as protocols
protocol Clock {
    var now: Date { get }
}

protocol Storage {
    func save(_ data: Data, key: String) throws
    func load(key: String) throws -> Data
}

// Real implementations
struct SystemClock: Clock { var now: Date { Date() } }
struct UserDefaultsStorage: Storage { ... }

// ViewModel takes protocols
class SessionViewModel {
    private let clock: Clock
    private let storage: Storage

    init(clock: Clock = SystemClock(),
         storage: Storage = UserDefaultsStorage()) {
        self.clock = clock
        self.storage = storage
    }

    func saveSession() throws {
        let session = Session(createdAt: clock.now)
        try storage.save(try JSONEncoder().encode(session), key: "session")
    }
}

// In tests — control time and storage
class MockClock: Clock { var now = Date(timeIntervalSince1970: 0) }
class MockStorage: Storage {
    var store: [String: Data] = [:]
    func save(_ data: Data, key: String) { store[key] = data }
    func load(key: String) throws -> Data { store[key]! }
}