https://github.com/swinject/swinjectstoryboard
Swinject extension for automatic dependency injection via Storyboard
https://github.com/swinject/swinjectstoryboard
dependency-injection ioc-container storyboard swinject
Last synced: 12 months ago
JSON representation
Swinject extension for automatic dependency injection via Storyboard
- Host: GitHub
- URL: https://github.com/swinject/swinjectstoryboard
- Owner: Swinject
- License: mit
- Created: 2016-05-05T07:49:17.000Z (about 10 years ago)
- Default Branch: master
- Last Pushed: 2024-02-01T06:48:07.000Z (over 2 years ago)
- Last Synced: 2025-05-31T06:03:07.089Z (about 1 year ago)
- Topics: dependency-injection, ioc-container, storyboard, swinject
- Language: Swift
- Homepage:
- Size: 399 KB
- Stars: 269
- Watchers: 11
- Forks: 143
- Open Issues: 32
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
SwinjectStoryboard
========
[](https://travis-ci.org/Swinject/SwinjectStoryboard)
[](https://github.com/Carthage/Carthage)
[](http://cocoapods.org/pods/SwinjectStoryboard)
[](http://cocoapods.org/pods/SwinjectStoryboard)
[](http://cocoapods.org/pods/SwinjectStoryboard)
[](https://developer.apple.com/swift)
[](https://github.com/apple/swift-package-manager)
SwinjectStoryboard is an extension of Swinject to automatically inject dependency to view controllers instantiated by a storyboard.
## Requirements
- iOS 8.0+ / Mac OS X 10.10+ / tvOS 9.0+
- Xcode 8+
## Installation
Swinject is available through [Carthage](https://github.com/Carthage/Carthage) or [CocoaPods](https://cocoapods.org).
### Carthage
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](https://github.com/Carthage/Carthage).
### CocoaPods
To install Swinject with CocoaPods, add the following lines to your `Podfile`.
```ruby
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](https://cocoapods.org).
### Swift Package Manager
To integrate using Apple's Swift package manager, add the following as a dependency to your `Package.swift`:
```swift
.package(url: "https://github.com/Swinject/SwinjectStoryboard.git", .upToNextMajor(from: "2.2.0"))
```
and then specify `"SwinjectStoryboard"` as a dependency of the Target in which you wish to use SwinjectStoryboard.
## Usage
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.
### Registration
#### Registration without Name
Here is a simple example to register a dependency of a view controller without a registration name:
```swift
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:
```swift
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:
```swift
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`.

#### Registration with Name
If a storyboard has more than one view controller with the same type, dependencies should be registered with registration names.
```swift
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:
```swift
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:
```swift
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.

### UIWindow and Root View Controller Instantiation
#### Implicit Instantiation from "Main" Storyboard
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.**
```swift
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") }
}
}
```
#### Explicit Instantiation in AppDelegate
If you prefer explicit instantiation of UIWindow and its root view controller, instantiate `SwinjectStoryboard` with a container in `application:didFinishLaunchingWithOptions:` method.
```swift
@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 References
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`.
```swift
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.
## For Maintainers
### Making a new release version
Our release procedure is described as [Makefile](https://github.com/Swinject/SwinjectStoryboard/blob/master/Makefile). Run `make help` command for more info.
## Credits
SwinjectStoryboard is inspired by:
- [Typhoon](https://github.com/appsquickly/typhoon) - [Jasper Blues](https://github.com/jasperblues), [Aleksey Garbarev](https://github.com/alexgarbarev) and [contributors](https://github.com/appsquickly/Typhoon/graphs/contributors).
- [BlindsidedStoryboard](https://github.com/briancroom/BlindsidedStoryboard) - [Brian Croom](https://github.com/briancroom).
## License
MIT license. See the [LICENSE file](LICENSE.txt) for details.