https://github.com/andrey-shavelev/juice
Lightweight dependency injection container with simple fluent interface.
https://github.com/andrey-shavelev/juice
dependency-injection di-container fluent-interface inversion-of-control property-wrappers swift swift5
Last synced: about 2 months ago
JSON representation
Lightweight dependency injection container with simple fluent interface.
- Host: GitHub
- URL: https://github.com/andrey-shavelev/juice
- Owner: andrey-shavelev
- License: mit
- Created: 2019-10-10T17:45:41.000Z (over 6 years ago)
- Default Branch: main
- Last Pushed: 2022-01-31T07:39:20.000Z (about 4 years ago)
- Last Synced: 2025-12-25T18:21:56.969Z (3 months ago)
- Topics: dependency-injection, di-container, fluent-interface, inversion-of-control, property-wrappers, swift, swift5
- Language: Swift
- Homepage:
- Size: 336 KB
- Stars: 5
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://opensource.org/licenses/MIT)
[](https://swift.org)

# Juice
Lightweight dependency injection container with simple fluent interface.
## Installing
With a swift package manager:
```swift
dependencies: [
.package(url: "https://github.com/andrey-shavelev/Juice", from: "0.1.0")
//...
]
```
## Quick Start
### Creating container and registering services
```swift
let container = try Container { builder in
// register a type that conforms to Injectable protocol
builder.register(injectable: FreshJuice.self)
.instancePerDependency()
.as(Juice.self)
// register by initializer
builder.register(initializer: Apple.init(color:))
.instancePerDependency()
.asSelf()
// register a custom factory
builder.register { scope -> TeaBlend in
let orange = try scope.resolve(Orange.self)
let blackTea = try scope.resolve(BlackTea.self)
return TeaBlend(fruit: orange, tea: blackTea)
}
.singleInstance()
.asSelf()
}
```
### Resolving a Service
```swift
// required
let orangeJuice = try container.resolve(Juice.self)
// optional
let compot = try container.resolveOptional(Compot.self)
let tea = try container.resolve(Tea?.self)
// supplying arguments
let appleJuice = try container.resolve(
Juice.self,
withArguments: Apple())
// or when you need to specify type directly
let appleJuice = try container.resolve(
Juice.self,
withArguments: Argument(Apple()))
```
Arguments passed to `resolve` method are matched with initializer parameters by their type and take precedence over services registered in container.
### Dependency Injection
#### Initializer
For services that conform to one of `Injectable` protocols or are registered by their `init` method, parameters of the initializer are filled in by `Container`.
```swift
class IcyLemonade: InjectableWithFiveParameters {
let fruitJuice: Juice
let lemon: Lemon
let optionalSweetener: Sweetener?
let water: Water
let ice: Ice
// All parameters will be filled by Container from resolution scope.
required init(_ fruitJuice: Juice,
_ lemon: Lemon,
_ optionalSweetener: Sweetener?,
_ water: Water,
_ ice: Ice) {
self.fruitJuice = fruitJuice
self.lemon = lemon
optionalSweetener = optionalSweetener
self.water = water
self.ice = ice
}
}
let container = try Container { builder in
builder.register(injectable: IcyLemonade.self)
.singleInstance()
.asSelf()
///...
}
```
### Property
Alternatively, dependencies could be injected into properties.
```swift
// Using Inject property wrapper
class Jam {
@Inject var fruit: Fruit
@Inject var spice: Spice?
init() {
}
}
// Or without wrapper
struct TeaBlend {
var fruit: Fruit!
var spice: Spice?
init() {
}
}
// In this case you need to specify properties for injection when registering a service
let container = try Container { builder in
builder.register(injectable: Jam.self)
.singleInstance()
.asSelf()
.injectDependency(into: \.fruit)
.injectDependency(into: \.spice)
}
```
### Lazy dependencies
```swift
class Egg {
unowned var chicken: Chicken
required init(_ chicken: Chicken) {
self.chicken = chicken
}
}
class Chicken {
var egg: Lazy
required init(_ egg: Lazy) {
self.egg = egg
}
}
/*
* Lazy - is a wrapper that delays actual resolution
* until its value is requested.
* For this purpose it keeps a strong reference to a resolution
* context of the owning instance,
* including all arguments (if any) that were passed to it.
*/
```
### Modules
```swift
let container = try Container { builder in
builder.register(module: FruitModule())
}
// Module allows to group registrations
struct FruitModule : Module {
func registerServices(into builder: ContainerBuilder) {
builder.register(injectable: Apple.self)
.instancePerDependency()
.asSelf()
builder.register(injectable: Orange.self)
.instancePerDependency()
.as(Fruit.self)
}
}
```
### Child Containers
```swift
let container = try Container { builer in
// Some types are registered here
}
let childContainer = try container.createChildContainer { builer in
// Additional types are be registered here
// Registrations from the parent container could be overriden
}
```
### Thread Safety
Current version of Juice does not support resolving services from concurrent threads.
## Registration options
```swift
let container = try Container { builder in
// Instance per depenedency
builder.register(injectable: Banana.self)
.singleInstance()
.asSelf()
// Each time Banana is resolved, container will return the same instance. Container will keep strong reference to it.
// Single instance
builder.register(initializer: Apple.init(color:))
.instancePerDependency()
.asSelf()
// Each time Apples is resolved, container will create a new instance. Container will not keep reference to any of it.
// External singletons
let someExternalSingleton = SingletonService.instance
builder.register(instance: someExternalSingleton)
.ownedExternally()
.asSelf()
// For instances registered as ownedExternally() container will keep an unowned reference.
let anotherExternalSingleton = AnotherSingletonService.instance
builder.register(instance: anotherExternalSingleton)
.ownedByContainer()
.asSelf()
// For instances registered as ownedByContainer() container will keep a strong reference.
}
```
## License
This project is licensed under MIT License.