You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
9.6 KiB

2 years ago
  1. SwinjectStoryboard
  2. ========
  3. [![Build Status](https://travis-ci.org/Swinject/SwinjectStoryboard.svg?branch=master)](https://travis-ci.org/Swinject/SwinjectStoryboard)
  4. [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
  5. [![CocoaPods Version](https://img.shields.io/cocoapods/v/SwinjectStoryboard.svg?style=flat)](http://cocoapods.org/pods/SwinjectStoryboard)
  6. [![License](https://img.shields.io/cocoapods/l/SwinjectStoryboard.svg?style=flat)](http://cocoapods.org/pods/SwinjectStoryboard)
  7. [![Platform](https://img.shields.io/cocoapods/p/SwinjectStoryboard.svg?style=flat)](http://cocoapods.org/pods/SwinjectStoryboard)
  8. [![Swift Version](https://img.shields.io/badge/Swift-5-F16D39.svg?style=flat)](https://developer.apple.com/swift)
  9. [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager)
  10. SwinjectStoryboard is an extension of Swinject to automatically inject dependency to view controllers instantiated by a storyboard.
  11. ## Requirements
  12. - iOS 8.0+ / Mac OS X 10.10+ / tvOS 9.0+
  13. - Xcode 8+
  14. ## Installation
  15. Swinject is available through [Carthage](https://github.com/Carthage/Carthage) or [CocoaPods](https://cocoapods.org).
  16. ### Carthage
  17. To install Swinject with Carthage, add the following line to your `Cartfile`.
  18. ```
  19. github "Swinject/Swinject"
  20. github "Swinject/SwinjectStoryboard"
  21. ```
  22. 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).
  23. ### CocoaPods
  24. To install Swinject with CocoaPods, add the following lines to your `Podfile`.
  25. ```ruby
  26. source 'https://github.com/CocoaPods/Specs.git'
  27. platform :ios, '8.0' # or platform :osx, '10.10' if your target is OS X.
  28. use_frameworks!
  29. pod 'Swinject'
  30. pod 'SwinjectStoryboard'
  31. ```
  32. Then run `pod install` command. For details of the installation and usage of CocoaPods, visit [its official website](https://cocoapods.org).
  33. ### Swift Package Manager
  34. To integrate using Apple's Swift package manager, add the following as a dependency to your `Package.swift`:
  35. ```swift
  36. .package(url: "https://github.com/Swinject/SwinjectStoryboard.git", .upToNextMajor(from: "2.2.0"))
  37. ```
  38. and then specify `"SwinjectStoryboard"` as a dependency of the Target in which you wish to use SwinjectStoryboard.
  39. ## Usage
  40. 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.
  41. **NOTE**: Do NOT explicitly resolve the view controllers registered by `storyboardInitCompleted` method. The view controllers are intended to be resolved by `SwinjectStoryboard` implicitly.
  42. ### Registration
  43. #### Registration without Name
  44. Here is a simple example to register a dependency of a view controller without a registration name:
  45. ```swift
  46. let container = Container()
  47. container.storyboardInitCompleted(AnimalViewController.self) { r, c in
  48. c.animal = r.resolve(Animal.self)
  49. }
  50. container.register(Animal.self) { _ in Cat(name: "Mimi") }
  51. ```
  52. 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:
  53. ```swift
  54. let sb = SwinjectStoryboard.create(
  55. name: "Animals", bundle: nil, container: container)
  56. let controller = sb.instantiateViewControllerWithIdentifier("Animal")
  57. as! AnimalViewController
  58. print(controller.animal! is Cat) // prints "true"
  59. print(controller.animal!.name) // prints "Mimi"
  60. ```
  61. Where the classes and protocol are:
  62. ```swift
  63. class AnimalViewController: UIViewController {
  64. var animal: Animal?
  65. required init?(coder aDecoder: NSCoder) {
  66. super.init(coder: aDecoder)
  67. }
  68. }
  69. protocol Animal {
  70. var name: String { get set }
  71. }
  72. class Cat: Animal {
  73. var name: String
  74. init(name: String) {
  75. self.name = name
  76. }
  77. }
  78. ```
  79. and the storyboard named `Animals.storyboard` has `AnimalViewController` with storyboard ID `Animal`.
  80. ![AnimalViewController in Animals.storyboard](./Assets/AnimalViewControllerScreenshot1.png)
  81. #### Registration with Name
  82. If a storyboard has more than one view controller with the same type, dependencies should be registered with registration names.
  83. ```swift
  84. let container = Container()
  85. container.storyboardInitCompleted(AnimalViewController.self, name: "cat") {
  86. r, c in c.animal = r.resolve(Animal.self, name: "mimi")
  87. }
  88. container.storyboardInitCompleted(AnimalViewController.self, name: "dog") {
  89. r, c in c.animal = r.resolve(Animal.self, name: "hachi")
  90. }
  91. container.register(Animal.self, name: "mimi") {
  92. _ in Cat(name: "Mimi")
  93. }
  94. container.register(Animal.self, name: "hachi") {
  95. _ in Dog(name: "Hachi")
  96. }
  97. ```
  98. Then view controllers are instantiated with storyboard IDs similarly to the case without registration names:
  99. ```swift
  100. let sb = SwinjectStoryboard.create(
  101. name: "Animals", bundle: nil, container: container)
  102. let catController = sb.instantiateViewControllerWithIdentifier("Cat")
  103. as! AnimalViewController
  104. let dogController = sb.instantiateViewControllerWithIdentifier("Dog")
  105. as! AnimalViewController
  106. print(catController.animal!.name) // prints "Mimi"
  107. print(dogController.animal!.name) // prints "Hachi"
  108. ```
  109. Where `Dog` class is:
  110. ```swift
  111. class Dog: Animal {
  112. var name: String
  113. init(name: String) {
  114. self.name = name
  115. }
  116. }
  117. ```
  118. 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.
  119. ![AnimalViewControllers with user defined runtime attribute in Animals.storyboard](./Assets/AnimalViewControllerScreenshot2.png)
  120. ### UIWindow and Root View Controller Instantiation
  121. #### Implicit Instantiation from "Main" Storyboard
  122. 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.
  123. **Note that `@objc` attribute is mandatory here in swift 4.**
  124. ```swift
  125. extension SwinjectStoryboard {
  126. @objc class func setup() {
  127. defaultContainer.storyboardInitCompleted(AnimalViewController.self) { r, c in
  128. c.animal = r.resolve(Animal.self)
  129. }
  130. defaultContainer.register(Animal.self) { _ in Cat(name: "Mimi") }
  131. }
  132. }
  133. ```
  134. #### Explicit Instantiation in AppDelegate
  135. If you prefer explicit instantiation of UIWindow and its root view controller, instantiate `SwinjectStoryboard` with a container in `application:didFinishLaunchingWithOptions:` method.
  136. ```swift
  137. @UIApplicationMain
  138. class AppDelegate: UIResponder, UIApplicationDelegate {
  139. var window: UIWindow?
  140. var container: Container = {
  141. let container = Container()
  142. container.storyboardInitCompleted(AnimalViewController.self) { r, c in
  143. c.animal = r.resolve(Animal.self)
  144. }
  145. container.register(Animal.self) { _ in Cat(name: "Mimi") }
  146. return container
  147. }()
  148. func application(
  149. _ application: UIApplication,
  150. didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
  151. let window = UIWindow(frame: UIScreen.mainScreen().bounds)
  152. window.makeKeyAndVisible()
  153. self.window = window
  154. let storyboard = SwinjectStoryboard.create(name: "Main", bundle: nil, container: container)
  155. window.rootViewController = storyboard.instantiateInitialViewController()
  156. return true
  157. }
  158. }
  159. ```
  160. 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.
  161. ### Storyboard References
  162. 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`.
  163. ```swift
  164. let container = SwinjectStoryboard.defaultContainer
  165. container.storyboardInitCompleted(AnimalViewController.self) { r, c in
  166. c.animal = r.resolve(Animal.self)
  167. }
  168. container.register(Animal.self) { _ in Cat(name: "Mimi") }
  169. ```
  170. 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.
  171. ## For Maintainers
  172. ### Making a new release version
  173. Our release procedure is described as [Makefile](https://github.com/Swinject/SwinjectStoryboard/blob/master/Makefile). Run `make help` coomand for more info.
  174. ## Credits
  175. SwinjectStoryboard is inspired by:
  176. - [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).
  177. - [BlindsidedStoryboard](https://github.com/briancroom/BlindsidedStoryboard) - [Brian Croom](https://github.com/briancroom).
  178. ## License
  179. MIT license. See the [LICENSE file](LICENSE.txt) for details.