Swift Dependency Injection with Swinject

Search for a command to run...

No comments yet. Be the first to comment.
Many times we need to make API calls to fetch data to display in a list. Here, I show how to do that with SwiftUI. To illustrate the structure of the application, let’s look at the following diagram: The Todo APIService We need to create an API serv...
EnvironmentObject is useful when you want to create a dependency in a higher component of the layout tree and use it on a lower component without having to pass it down the tree through every child component. We will now use EnvironmentObject to moni...

CompositionLocal is useful when you want to create a dependency in a higher node of the layout tree and use it on a lower node without having to pass it down the tree through every child Composable. Here we will use it to direct our application when ...

The TDD (Test Driven Development) approach is important when it comes to testing small portions of business logic code in isolation. In this blog, I'll cover how I would use TDD to build a clean architecture To-do SwiftUI application I'll cover imp...

The TDD (Test Driven Development) approach is important when it comes to testing small portions of business logic code in isolation. In this blog, I'll cover how I would use TDD to build a clean architecture To-do Jetpack Compose Android application ...

The Room persistence library provides an abstraction layer over SQLite. To use Room in your app, add the following dependencies to your app’s build.gradle file: dependencies { .... kapt "org.xerial:sqlite-jdbc:3.34.0" // Room def roo...

As in most loosely coupled projects where we have big chains of dependencies. Swinject makes it simple and easy to do dependency injection. We’re going to use it in combination with the Swifts new property wrapper
Let’s say we have a 3 layer dependency:

Of course, we can use good old manual dependency injection as follows:
class TodoListViewModel: ObservableObject {
var getTodosUseCase = GetTodosUseCase(
repo: TodoRepositoryImpl(
dataSource: TodoDataSourceImpl()
)
)
....
}
class TodoListViewModel: ObservableObject {
@Inject
private var getTodosUseCase : GetTodos
....
}
This would allow us to control the external dependencies by simply decorating the property with the Inject annotation and specifying the interface.
So the first thing is the @Inject annotation. In Swift, we use a property wrapper. Since Swift 5.1, the property wrapper feature enables us to attach logic directly to the property itself. Here we create a property wrapper called “Inject” that will automatically assign something to the prop
@propertyWrapper
struct Inject<I> {
let wrappedValue: I
init() {
//Resolve the interface to an implementation.
self.wrappedValue = Resolver.shared.resolve(I.self)
}
}
Let’s build the resolver
class Resolver {
static let shared = Resolver()
//get the IOC container
private var container = buildContainer()
func resolve<T>(_ type: T.Type) -> T {
container.resolve(T.self)!
}
}
All we now need is an IOC container. Let’s use Swinject to build one to resolve all interfaces to their respective implementations:
import Swinject
func buildContainer() -> Container {
let container = Container()
container.register(GetTodos.self) { _ in
return GetTodosUseCase()
}.inObjectScope(.container)
container.register(TodoDataSource.self) { _ in
return TodoMockDataSourceImpl()
}.inObjectScope(.container)
container.register(TodoRepository.self) { _ in
return TodoRepositoryImpl()
}.inObjectScope(.container)
return container
}