Swinject extension for automatic dependency injection via Storyboard
SwinjectStoryboard is an extension of Swinject to automatically inject dependency to view controllers instantiated by a storyboard.
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-binariescommand 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 installcommand. 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
NSStoryboardin case of OS X). To register dependencies of a view controller, use
storyboardInitCompletedmethod. 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
storyboardInitCompletedmethod. The view controllers are intended to be resolved by
SwinjectStoryboardimplicitly.
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
SwinjectStoryboardwith the container specified. If the container is not specified,
SwinjectStoryboard.defaultContaineris used instead.
instantiateViewControllerWithIdentifiermethod 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.storyboardhas
AnimalViewControllerwith 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
Dogclass is:
class Dog: Animal { var name: Stringinit(name: String) { self.name = name }
}
and the storyboard named
Animals.storyboardhas
AnimalViewControllers with storyboard IDs
Catand
Dog. In addition to the storyboard IDs, user defined runtime attributes are specified as
catand
dogfor the key
swinjectRegistrationName, respectively.
If you implicitly instantiate
UIWindowand its root view controller from "Main" storyboard, implement
setupclass method as an extension of
SwinjectStoryboardto register dependencies to
defaultContainer. When the root view controller (initial view controller) is instantiated by runtime, dependencies registered to
defaultContainerare injected.
Note that
@objcattribute 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
SwinjectStoryboardwith 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 nameitem (or
UIMainStoryboardFileitem if you are displaying raw keys/values) in
Info.plistof 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
defaultContainerstatic 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
UIWindowand its root view controller, the registrations setup for "Main" storyboard can be shared with the referenced storyboard since
defaultContaineris configured in
setupmethod.
SwinjectStoryboard is inspired by:
MIT license. See the LICENSE file for details.