Factory is the Dependency Injection (DI) library which will replace it’s popular predecessor – Resolver.

Factory continues to be very lightweight, with just 1 file and 400+ LOC. That makes me less afraid of integrating it into my projects.

How to setup a container?

Firstly, you will simply extend Container and add your dependencies, such as services.

extension Container {
  static let serviceA = Factory(scope: .singleton) { ServiceA() }
  static let serviceB = Factory(scope: .singleton) { ServiceB() }

It is important to use singleton as the scope if you want 1 instance for the whole lifetime.

The default scope is unique, whereby the factory will create a new instance every time. Unknowingly, this can be a PITFALL. This is usually not the behaviour we want, so do use singleton, or shared or a custom Cached session.

MVVM & SwiftUI

We will have a View holding a ViewModel, typical of a MVVM.

struct BookView: View {
  @StateObject var model = BookViewModel()

The ViewModel will be using a service from the container. There are 2 ways.

class BookViewModel: ObservableObject {
  // 1. Using @Injected from Factory
  @Injected(Container.serviceA) private var serviceA

  // 2. Using a Service Locator Pattern
  @Published private let serviceA = Container.serviceA()

You will want to use (2) if you need to add a property wrapper like @Published to the service. This is a limitation as there can only be 1 property wrapper.

Mock it in preview

Whenever you need to mock the dependencies, especially for SwiftUI preview, you can register a mock service.

struct BookView_Previews: PreviewProvider {
  static var previews: some View {
    let _ = Container.serviceA.register { MockServiceA() }

Inject an optional object

It is common to pass a User object, if the user has been authenticated, else nil.

extension Container {
  static let userProviding = Factory<UserProviding?> { nil }

// Set when authenticated
Container.userProviding.register { UserProvider(user: user) }

// Reset when logged out

The injected Container.userProviding will be an optional type.

And more

There are other features such as constructing parameters, @LazyInjected and @WeakLazyInjected and custom containers.




Back to Home