Dependency Injection
Using Environment variables.
Dependency Injection
Using Environment variables.
0
0
Checkbox to mark video as read
Mark as read

Dependency Injection (DI) is a design pattern that enables you to inject dependencies into objects rather than having them instantiate those dependencies directly. This practice improves code modularity, testability, and flexibility, making it easier to build and maintain large applications. In Swift and SwiftUI, DI is commonly used for managing external dependencies, such as network services, data models, or database instances.

For instance, a view that needs access to a data service can have the service passed in as a dependency rather than creating it directly. This approach allows for easy replacement of the service in different contexts, like testing.

Protocol injection

In order to be able to replace the injected object with a different one, we will create a protocol, which will be the actual type injected, being able to use any object that conforms that protocol.

Let's see an example:

protocol DataService {
    func fetchData() -> String
}

class NetworkDataService: DataService {
    func fetchData() -> String {
        return "Data from network"
    }
}

class NetworkDataServiceMock: DataService {
    func fetchData() -> String {
        return "Mocked data"
    }
}

let runTimeService: DataService = NetworkDataService()
runTimeService.fetchData() // "Data from network"

let mockService: DataService = NetworkDataServiceMock()
mockService.fetchData() // "Mocked data"

Let's use this with different types of injections:

Types of Dependency Injection

The most common ways to inject a dependency are:

  • Constructor Injection: Injecting dependencies through the initializer.
  • Property Injection: Injecting dependencies through properties after object creation.

1. Constructor Injection Example

Constructor injection is a straightforward way to inject dependencies. Here’s a basic example where we inject a service dependency through the initializer:

class ViewModel {
    let dataService: DataService

    init(dataService: DataService) {
        self.dataService = dataService
    }
    
    func getData() -> String {
        return dataService.fetchData()
    }
}

let viewModel = ViewModel(dataService: NetworkDataService())
let testViewModel = ViewModel(dataService: NetworkDataServiceMock())

In this example, ViewModel depends on DataService. By using constructor injection, we can pass any implementation of DataService to ViewModel, making it flexible and easy to test.

2. Dependency Injection in SwiftUI

SwiftUI provides the @Environment property wrapper, a type of dependency injection that allows you to share data across views without manually passing it to each child. This approach is beneficial when using a shared state or view model.

Before using our DataService as an @Environment variable, we need to do two small things:

Mark our services as @Observable:

@Observable
class NetworkDataService: DataService {
    ...
@Observable
class NetworkDataServiceMock: DataService {
    ...

And declare our DataService as an @Environment variable, providing a default value:

extension EnvironmentValues {
    @Entry var dataService: DataService = NetworkDataService()
}

Then we can use it directly in our view:

struct ContentView: View {
    @Environment(\.dataService) var dataService

    @State var responseLabel: String = ""

    var body: some View {
        Text(responseLabel)
            .onAppear {
                responseLabel = dataService.fetchData()
            }
    }
}

NetworkDataService will be used as default unless we indicate a different one using the .environment modifier.

We can do this on our ContentView preview:

#Preview {
    ContentView()
        .environment(\.dataService, NetworkDataServiceMock())
}

course

Quiz Time!

0 Comments

Join the community to comment
Sign Up
I have an account
Be the first to comment

Accept Cookies

We use cookies to collect and analyze information on site performance and usage, in order to provide you with better service.

Check our Privacy Policy