SwinjectStoryboard is an extension of Swinject to automatically inject dependency to view controllers instantiated by a storyboard.
- iOS 8.0+ / Mac OS X 10.10+ / tvOS 9.0+
- Xcode 8+
Swinject is available through Carthage or CocoaPods.
To install Swinject with Carthage, add the following line to your Cartfile
.
github "Swinject/Swinject"
github "Swinject/SwinjectStoryboard"
Then run carthage update --no-use-binaries
command or just carthage update
. For details of the installation and usage of Carthage, visit its project page.
To install Swinject with CocoaPods, add the following lines to your Podfile
.
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0' # or platform :osx, '10.10' if your target is OS X.
use_frameworks!
pod 'Swinject'
pod 'SwinjectStoryboard'
Then run pod install
command. For details of the installation and usage of CocoaPods, visit its official website.
Swinject supports automatic dependency injection to view controllers instantiated by SwinjectStoryboard
. This class inherits UIStoryboard
(or NSStoryboard
in case of OS X). To register dependencies of a view controller, use storyboardInitCompleted
method. In the same way as a registration of a service type, a view controller can be registered with or without a name.
NOTE: Do NOT explicitly resolve the view controllers registered by storyboardInitCompleted
method. The view controllers are intended to be resolved by SwinjectStoryboard
implicitly.
Here is a simple example to register a dependency of a view controller without a registration name:
let container = Container()
container.storyboardInitCompleted(AnimalViewController.self) { r, c in
c.animal = r.resolve(Animal.self)
}
container.register(Animal.self) { _ in Cat(name: "Mimi") }
Next, we create an instance of SwinjectStoryboard
with the container specified. If the container is not specified, SwinjectStoryboard.defaultContainer
is used instead. instantiateViewControllerWithIdentifier
method creates an instance of the view controller with its dependencies injected:
let sb = SwinjectStoryboard.create(
name: "Animals", bundle: nil, container: container)
let controller = sb.instantiateViewControllerWithIdentifier("Animal")
as! AnimalViewController
print(controller.animal! is Cat) // prints "true"
print(controller.animal!.name) // prints "Mimi"
Where the classes and protocol are:
class AnimalViewController: UIViewController {
var animal: Animal?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
protocol Animal {
var name: String { get set }
}
class Cat: Animal {
var name: String
init(name: String) {
self.name = name
}
}
and the storyboard named Animals.storyboard
has AnimalViewController
with storyboard ID Animal
.
If a storyboard has more than one view controller with the same type, dependencies should be registered with registration names.
let container = Container()
container.storyboardInitCompleted(AnimalViewController.self, name: "cat") {
r, c in c.animal = r.resolve(Animal.self, name: "mimi")
}
container.storyboardInitCompleted(AnimalViewController.self, name: "dog") {
r, c in c.animal = r.resolve(Animal.self, name: "hachi")
}
container.register(Animal.self, name: "mimi") {
_ in Cat(name: "Mimi")
}
container.register(Animal.self, name: "hachi") {
_ in Dog(name: "Hachi")
}
Then view controllers are instantiated with storyboard IDs similarly to the case without registration names:
let sb = SwinjectStoryboard.create(
name: "Animals", bundle: nil, container: container)
let catController = sb.instantiateViewControllerWithIdentifier("Cat")
as! AnimalViewController
let dogController = sb.instantiateViewControllerWithIdentifier("Dog")
as! AnimalViewController
print(catController.animal!.name) // prints "Mimi"
print(dogController.animal!.name) // prints "Hachi"
Where Dog
class is:
class Dog: Animal {
var name: String
init(name: String) {
self.name = name
}
}
and the storyboard named Animals.storyboard
has AnimalViewController
s with storyboard IDs Cat
and Dog
. In addition to the storyboard IDs, user defined runtime attributes are specified as cat
and dog
for the key swinjectRegistrationName
, respectively.
If you implicitly instantiate UIWindow
and its root view controller from "Main" storyboard, implement setup
class method as an extension of SwinjectStoryboard
to register dependencies to defaultContainer
. When the root view controller (initial view controller) is instantiated by runtime, dependencies registered to defaultContainer
are injected.
Note that @objc
attribute is mandatory here in swift 4.
extension SwinjectStoryboard {
@objc class func setup() {
defaultContainer.storyboardInitCompleted(AnimalViewController.self) { r, c in
c.animal = r.resolve(Animal.self)
}
defaultContainer.register(Animal.self) { _ in Cat(name: "Mimi") }
}
}
If you prefer explicit instantiation of UIWindow and its root view controller, instantiate SwinjectStoryboard
with a container in application:didFinishLaunchingWithOptions:
method.
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var container: Container = {
let container = Container()
container.storyboardInitCompleted(AnimalViewController.self) { r, c in
c.animal = r.resolve(Animal.self)
}
container.register(Animal.self) { _ in Cat(name: "Mimi") }
return container
}()
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
window.makeKeyAndVisible()
self.window = window
let storyboard = SwinjectStoryboard.create(name: "Main", bundle: nil, container: container)
window.rootViewController = storyboard.instantiateInitialViewController()
return true
}
}
Notice that you should delete the Main storyboard file base name
item (or UIMainStoryboardFile
item if you are displaying raw keys/values) in Info.plist
of your app.
Storyboard Reference introduced with Xcode 7 is supported by SwinjectStoryboard
. To enable dependency injection when an instance is created from a referenced storyboard, register dependencies to defaultContainer
static property of SwinjectStoryboard
.
let container = SwinjectStoryboard.defaultContainer
container.storyboardInitCompleted(AnimalViewController.self) { r, c in
c.animal = r.resolve(Animal.self)
}
container.register(Animal.self) { _ in Cat(name: "Mimi") }
If you implicitly instantiate UIWindow
and its root view controller, the registrations setup for "Main" storyboard can be shared with the referenced storyboard since defaultContainer
is configured in setup
method.
if you want to pass some arguments, you can do it in such way:
- Register VC
let container = SwinjectStoryboard.defaultContainer
container.storyboardInitCompletedArgs(AnimalViewController.self) { (r, c, arg1: Int, arg2: SomeValue) in
c.animal = r.resolve(Animal.self)
c.countAnimals = arg1
c.someValue = arg2
}
container.register(Animal.self) { _ in Cat(name: "Mimi") }
- Resolve VC
let sb = SwinjectStoryboard.create(
name: "Animals", bundle: nil, container: container)
let firstArg: Int = 5
let secondArg: SomeValue = SomeValue()
let catController = sb.instantiateViewController(withIdentifier: "SomeIdentifier", arg1: firstArg, arg2: secondArg) as! AnimalViewController
SwinjectStoryboard is inspired by:
MIT license. See the LICENSE file for details.