Modules
Syringe introduces an intuitive module DSL that makes dependency management a breeze.
The following functions are part of the module DSL:
factory { }
- Provides a factory that returns a new instance on every resolvesingleton { //definition }
- Provides an instance that is retained once it is createdget()
- Resolves a previously registered dependency. May be used with any number of parameters.
Creating a Module​
Let's create a new module
let yourModule = module {
// Dependencies go here
}
Let's add a singleton of type Service
to our module:
class Service {
...
}
let yourModule = module {
singleton { Service() }
}
Note that the same syntax applies to factories:
let yourModule = module {
singleton { Service() }
factory { Int.random(0..<5) }
}
Resolving Dependencies​
The dependencies can now be resolved:
class View {
let service: Service = get()
}
Dependencies may also resolve other dependencies inside of the module:
class User {
...
}
class Service {
let user: User
init(user: User) {
self.user = user
}
}
let yourModule = module {
singleton { Service(user: get()) }
factory { User() }
}
Resolved dependencies will be nil when not previously registered:
class Repository {
let storage: Storage
init(storage: Storage) {
self.storage = storage
}
}
let yourModule = module {
singleton { Repository(storage: get()) }
}
Dependencies are Optional types.
If they are unfound the logger will print possible issues and the resolved dependency will be nil.
Passing Parameters​
It is also possible to pass parameters during resolve:
class Counter {
let value: Int
init(x: Int, y: Int) {
self.value = x + y
}
}
let yourModule = module {
factory { Counter(x: $0, y: $1) }
}
class View {
init() {
var counter: Counter = get(1, 2)
}
}
Passing parameters to singletons works only for the first time. Once singletons are created parameters are ignored on subsequent resolves.
Late-resolving​
Sometimes it is inevitable to have a circular dependency. Syringe allows you to break the circle and late-resolve certain dependencies:
public class Parent {
public let child: Child
public init(child: Child) {
self.child = child
}
}
public class Child {
public var parent: Parent!
init() {
}
}
let testModule = module {
singleton { Parent(child: get()!) }
.onInit {
// Only parameter is the target dependency as Any?
guard let parent: Parent = $0 as? Parent else { fatalError() }
let child: Child = get()!
child.parent = parent
}
singleton { Child() }
}
injectSyringe {
modules {
testModule
}
}
Making use of Polymorphism​
Sometimes you want your dependency to be the base type of something, while the returned object is a specialisation of that. Syringe allows that via regular Swift typecasts.
protocol Repository {
func load()
}
class RepoImpl: Repository {
func load() {
...
}
}
class RepoMock: Repository {
func load() {
...
}
}
let yourModule = module {
// Use mock for testing
#if TESTING
singleton { RepoMock() as Repository }
// Use live for anything else
#else
singleton { RepoImpl() as Repository }
#endif
}
class View {
let repo: Repository = get()
init() {
repo.load()
}
}