SwimpleInjection
Simple dependency injection with Swift.
SwimpleInjection is part of the Swimple packages series. Swimple stands for Simple Swift. These packages make coding with Swift simpler and more convenient.
📦
Swift Package Manager From within Xcode 11 or up you can add SwimpleInjection as a Swift Package:
- Selecting your project
- Go to
Swift packages
- Add a package (+)
- Enter
https://github.com/lloydkeijzer/SwimpleInjection.git
as the package repository url - Select the version you want to use and click next
You're now able to import SwimpleInjection in your source code
🎓
Basics Dependency Injection with SwimpleInjection starts with registering the services(concrete class types) or workers(protocol types) you want to resolve later on. You’ve three options for registering a service:
🤖
Automatic example Since version 1.1.0 it's possible to automatically register your services instead of resolving them with a Handler or Assembler. Just provide the type of Service/Worker and its initialize method. There are a few constraints though:
- 9 arguments can currently be automatically resolved per initializer method
- All the initializer method its arguments have to be registered in the same container as the one resolving the Service
Container.main.autoRegister(Worker.self, initializer: Service.init)
Handler example
Using a handler for less complex services.
Container.main.register(Worker.self) { (container) -> Service in
return Service(
a: container.resolve(WorkerA.self),
b: container.resolve(WorkerB.self)
)
}
Assembler example
Using a dedicated Assembler for more complex services.
final class ServiceAssembler: Assembler {
func resolve(using container: Resolver) -> AnyObject {
return Service(
a: container.resolve(WorkerA.self),
b: container.resolve(WorkerB.self)
)
}
}
Container.main.register(assembler: ServiceAssembler(), for: Worker.self)
Resolve example
We can resolve our registered services by calling the resolve method on the Container
.
let service = Container.main.resolve(Worker.self)
Containers
A Container
object is able to register services and resolve them later on. Most of the time you’ll use the default Container.main
instance. But if your use case requires to have multiple containers you can create them by calling let myCustomContainer = Container()
.
Merging Containers Example
When working in multiple frameworks or targets you could end up having multiple containers, that need to be merged in your main target.
let containerA = Container()
containerA.autoRegister(ClassA.self, initializer: ClassA.init)
let containerB = Container()
containerB.autoRegister(ClassB.self, initializer: ClassB.init)
let containerC = Container()
containerC.register(ClassC.self) { (container) -> ClassC in
return ClassC(
a: container.resolve(ClassA.self),
b: container.resolve(ClassB.self)
)
}
// Merging containers a, b and c into the main container
Container.main.merge(with: containerA)
Container.main.merge(with: containerB)
Container.main.merge(with: containerC)
let c = Container.main.resolve(ClassC.self)
c.hello() // 👋🏻 Hello from Class C
Shared Services
With SwimpleInjection you can get rid of the shared singleton boilerplate by declaring your Service to be resolved as a .singleObject
. This makes sure the service will only be resolved once and the Container
saves a reference to the object. The next time the same service asks to be resolved, it will serve the reference to the saved object.
Single Object Example
Container.main.autoRegister(
SharedWorker.self,
as: .singleObject, // default is .uniqueObject
initializer: SharedWorker.init
)
let shared1 = Container.main.resolve(SharedWorker.self)
shared1.count += 1
let shared2 = Container.main.resolve(SharedWorker.self)
shared2.count += 1
print(shared1.count) // 2
print(shared2.count) // 2