Swinject extension for factory injection.
Imagine that your component has a runtime parameter (userID
) which cannot be determined statically.
class UserDetailViewController {
init(userID: Int, userService: UserServiceType, otherService: OtherServiceType)
}
Swinject also supports arguments but Swift compiler cannot check the arguments statically. It can cause a human mistake.
// it takes 1 parameter
container.register(UserDetailViewController.self) { r, userID in
return UserDetailViewController(userID: userID, userService: r.resolve(UserServiceType.self)!)
}
// when pass 3 parameters it will return nil
container.resolve(UserServiceType.self, arguments: 1, 2, 3)
So in this case we shoulud use a factory instead:
// register UserDetailViewController factory
container.register(((userID: Int) -> UserDetailViewController).self) { r in
return { userID in
return UserDetailViewController(userID: userID, userService: r.resolve(UserServiceType.self)!)
}
}
// register a component that has a dependency to UserDetailViewController
container.register(UserListViewController.self) { r in
return UserListViewController(detailFactory: r.resolve(((userID: Int) -> UserDetailViewController).self)!)
}
But it looks really complicated. SwinjectFactory makes it really clean.
The code above becomes short and readable with SwinjectFactory.
// register UserDetailViewController factory
container.register(factory: UserDetailViewController.self)
// register a component that has a dependency to UserDetailViewController
container.register(UserListViewController.self) { r in
UserListViewController(detailFactory: r.resolve(factory: UserDetailViewController.self)!)
}
It can be even shorter with SwinjectAutoregistration.
container.register(factory: UserDetailViewController.self)
container.autoregister(UserListViewControllerType.self, initializer: UserListViewController.init)
Conform your component to FactoryComponent
. This protocol requires a single static variable: factory
. This is a curried function which takes a Swinject.Resolver
as a first function parameter. The nested function takes a runtime parameter which is required for constructing a component class. It finally returns a service type.
class UserDetailViewController: FactoryComponent {
init(userID: Int, userService: UserServiceType) {
}
// (Resolver) -> (Runtime Paramters) -> Service
static var factory: (Resolver) -> (_ userID: Int) -> UserDetailViewController {
return self.autocreate(UserDetailViewController.init) // it will automatically fill parameters
}
}
Here is an example of using UserDetailViewController
as a dependency:
class UserListViewController {
let detailViewControllerFactory: UserDetailViewController.Factory
init(detailViewControllerFactory: UserDetailViewController.Factory) {
self.detailViewControllerFactory = detailViewControllerFactory
}
func presentDetailViewController(userID: Int) {
let viewController = self.detailViewControllerFactory(userID)
self.navigationController?.push(viewController, animated: true)
}
}
SwinjectFactory currently support CocoaPods only.
pod 'SwinjectFactory'
Any discussions and pull requests are welcomed đź’–
To create a Xcode project:
$ make project
This will automatically create a Xcode project file and configure code generating environment.
SwinjectFactory is under MIT license. See the LICENSE file for more info.