CODESAMPLE

Dependency Injection - Swift

Share on:

Dependency Injection (DI) is a design pattern that promotes loose coupling by providing dependencies to a class instead of the class creating them itself. This improves testability, maintainability, and reusability. Our Swift example demonstrates DI through initializer injection. The NetworkService protocol defines the dependency, and concrete implementations like MockNetworkService and RealNetworkService provide different behaviors. The ViewController doesn’t create these services; instead, it receives an instance via its initializer. This makes it easy to swap the network service with a mock for testing without altering the ViewController’s code. Swift’s protocol-oriented programming lends itself naturally to DI, allowing us to define contracts (protocols) for dependencies and inject conforming types.

// NetworkService.swift
protocol NetworkService {
    func fetchData(completion: @escaping (String) -> Void)
}

// RealNetworkService.swift
class RealNetworkService: NetworkService {
    func fetchData(completion: @escaping (String) -> Void) {
        // Simulate network request
        DispatchQueue.global().async {
            let data = "Data from the real network!"
            completion(data)
        }
    }
}

// MockNetworkService.swift (for testing)
class MockNetworkService: NetworkService {
    var mockData: String
    
    init(mockData: String) {
        self.mockData = mockData
    }

    func fetchData(completion: @escaping (String) -> Void) {
        completion(mockData)
    }
}

// ViewController.swift
class ViewController: UIViewController {
    private let networkService: NetworkService

    init(networkService: NetworkService) {
        self.networkService = networkService
        super.init()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        networkService.fetchData { data in
            print("Received data: \(data)")
        }
    }
}

// Usage (in your app delegate or scene delegate)
// Let's use the real service in production:
let realNetworkService = RealNetworkService()
let viewController = ViewController(networkService: realNetworkService)

// For testing, inject the mock:
let mockNetworkService = MockNetworkService(mockData: "Mock Data")
let testViewController = ViewController(networkService: mockNetworkService)