Swinject

Swinject (https://github.com/Swinject/Swinject) is a lightweight Dependency Injection container written in Swift, which is used in more than 20,000 apps:

Swinject offers pure Swift Type Support, Injection with arguments, constructor and property Injection, object scope, and thread safety. Like Typhoon, in the Swinject repository, you can find a sample app that shows how to use the framework (https://github.com/Swinject/SwinjectSimpleExample).

Swinject's dependency-resolver engine is based on the concept of the container, where the classes and services are registered and then resolved:

var container = Container()

// Registrations for the network using Alamofire.
container.register(Networking.self) { _ in Network() }
container.register(WeatherFetcher.self) { r in
WeatherFetcher(networking: r.resolve(Networking.self)!)
}
//..

In the preceding example, the Networking and WeatherFetcher classes are registered in a container; since WeatherFetcher needs the Networking instance, it can be resolved by the resolver passed as a parameter of the callback.

Then the instances can be resolved to call the resolve function in the container:

let fetcher = container.resolve(WeatherFetcher.self)!
fetcher.fetch { cities = $0 }

If the objects require more parameters to be passed during the creation process, they can be passed in the resolve function that can later pass inside the callback:

container.register(Animal.self) { _, name in
Horse(name: name)
}
container.register(Animal.self) { _, name, running in
Horse(name: name, running: running)
}

To resolve them, run the following code:

let firstAnimal = container.resolve(Animal.self, argument: "Spirit")!

print(firstAnimal.name) // prints "Spirit"
print((firstAnimal as! Horse).running) // prints "false"

let secondAnimal = container.resolve(Animal.self, arguments: "Lucky", true)!

print(secondAnimal.name) // prints "Lucky"
print((secondAnimal as! Horse).running) // prints "true"

Given the Injection process is handled by a callback, how to inject the dependencies is completely in the hands of the developer. It could be a constructor Injection, such as the preceding examples, or a property Injection, as follows:

let container = Container()
container.register(Animal.self) { _ in Cat() }
container.register(Person.self) { r in
    let owner = PetOwner()
    owner.pet = r.resolve(Animal.self)
    return owner
}

Like in Typhoon, it is possible to define different types of scope:

container.register(ProductsFetcher.self) { _ in NetworkProductsFetcher() }
.inObjectScope(.container)

.graph is the scope defined by default one. It means that a new instance is created every time resolve() is called, but if that function is called inside the callback, the previous instance is resolved instead. This permits us to create object graphs with the same life cycle.

.container means that the instance created is owned by that container and it will have the same life cycle of the container. This is equivalent to a singleton in Typhoon.

It is also possible to define a custom scope, as follows:

extension ObjectScope {
static let custom = ObjectScope(storageFactory: PermamentStorage.init)
}

Instances in the .custom scope work as in .container, but the container can be cleaned with the following:

container.resetObjectScope(.custom)

After resetting it, subsequent calls to resolve create a new instance of the objects in the container.