Skip to main content
Version: 0.1.1-a

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 resolve
  • singleton { //definition } - Provides an instance that is retained once it is created
  • get() - 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()) }
}
danger

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)
}
}
Singletons

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()
}
}