diff --git a/.travis.yml b/.travis.yml index bc46736..d30f94f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,3 +16,6 @@ script: - set -o pipefail && xcodebuild test -project DipUI/DipUI.xcodeproj -scheme DipUI-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV 1080p,OS=latest' ONLY_ACTIVE_ARCH=NO | xcpretty -c - pod lib lint --quick - carthage build --no-skip-current + +notifications: +email: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 65eeb61..01d6aad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # CHANGELOG +## 0.2.0 + +* Multiple UI containers + [#7](https://github.com/AliSoftware/Dip/pull/7), [@ilyapuchka](https://github.com/ilyapuchka) + +* dipTag can be `nil` + [#6](https://github.com/AliSoftware/Dip/pull/6), [@ilyapuchka](https://github.com/ilyapuchka) + ## 0.1.0 * Fixed Dip version in podspec. diff --git a/Dip-UI.podspec b/Dip-UI.podspec index c1f7e63..579ee72 100644 --- a/Dip-UI.podspec +++ b/Dip-UI.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Dip-UI" - s.version = "0.1.0" + s.version = "0.2.0" s.summary = "Dip UI extension" s.description = <<-DESC diff --git a/DipUI/DipUI.xcodeproj/project.pbxproj b/DipUI/DipUI.xcodeproj/project.pbxproj index 2e58d44..4cd4171 100644 --- a/DipUI/DipUI.xcodeproj/project.pbxproj +++ b/DipUI/DipUI.xcodeproj/project.pbxproj @@ -563,7 +563,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.1.0; + CURRENT_PROJECT_VERSION = 0.2.0; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -612,7 +612,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.1.0; + CURRENT_PROJECT_VERSION = 0.2.0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; diff --git a/DipUI/DipUITests/DipUITests.swift b/DipUI/DipUITests/DipUITests.swift index 7233e87..7478677 100644 --- a/DipUI/DipUITests/DipUITests.swift +++ b/DipUI/DipUITests/DipUITests.swift @@ -42,7 +42,8 @@ import XCTest #endif -class DipViewController: ViewController, StoryboardInstantiatable { } +class DipViewController: ViewController, StoryboardInstantiatable {} +class NilTagViewController: ViewController, StoryboardInstantiatable {} class DipUITests: XCTestCase { @@ -73,11 +74,11 @@ class DipUITests: XCTestCase { XCTFail("Should not resolve when container is not set.") } - DependencyContainer.uiContainer = container + DependencyContainer.uiContainers = [container] storyboard.instantiateViewControllerWithIdentifier("ViewController") } - - func testThatItResolvesIfContainerAndTagAreSet() { + + func testThatItResolvesIfContainerAndStringTagAreSet() { var resolved = false let container = DependencyContainer() container.register(tag: "vc") { DipViewController() } @@ -85,11 +86,62 @@ class DipUITests: XCTestCase { resolved = true } - DependencyContainer.uiContainer = container + DependencyContainer.uiContainers = [container] storyboard.instantiateViewControllerWithIdentifier("DipViewController") - XCTAssertTrue(resolved, "Should resolve when container is set.") + XCTAssertTrue(resolved, "Should resolve when container and tag are set.") + } + + func testThatItResolvesIfContainerAndNilTagAreSet() { + var resolved = false + let container = DependencyContainer() + container.register() { NilTagViewController() } + .resolveDependencies { _, _ in + resolved = true + } + + DependencyContainer.uiContainers = [container] + storyboard.instantiateViewControllerWithIdentifier("NilTagViewController") + XCTAssertTrue(resolved, "Should resolve when container and nil tag are set.") + } + + func testThatItDoesNotResolveIfTagDoesNotMatch() { + let container = DependencyContainer() + container.register(tag: "wrong tag") { DipViewController() } + .resolveDependencies { _, _ in + XCTFail("Should not resolve when container is not set.") + } + + DependencyContainer.uiContainers = [container] + storyboard.instantiateViewControllerWithIdentifier("DipViewController") + } + + func testThatItResolvesWithDefinitionWithNoTag() { + var resolved = false + let container = DependencyContainer() + container.register() { DipViewController() } + .resolveDependencies { _, _ in + resolved = true + } + + DependencyContainer.uiContainers = [container] + storyboard.instantiateViewControllerWithIdentifier("DipViewController") + XCTAssertTrue(resolved, "Should fallback to definition with no tag.") } + func testThatItIteratesUIContainers() { + var resolved = false + let container1 = DependencyContainer() + let container2 = DependencyContainer() + container2.register(tag: "vc") { DipViewController() } + .resolveDependencies { container, _ in + XCTAssertTrue(container === container2) + resolved = true + } + + DependencyContainer.uiContainers = [container1, container2] + storyboard.instantiateViewControllerWithIdentifier("DipViewController") + XCTAssertTrue(resolved, "Should resolve using second container") + } } protocol SomeService: class { diff --git a/DipUI/DipUITests/NSStoryboard.storyboard b/DipUI/DipUITests/NSStoryboard.storyboard index 145a3cb..9d46876 100644 --- a/DipUI/DipUITests/NSStoryboard.storyboard +++ b/DipUI/DipUITests/NSStoryboard.storyboard @@ -1,7 +1,7 @@ - + - + @@ -20,6 +20,22 @@ + + + + + + + + + + + + + + + + diff --git a/DipUI/DipUITests/UIStoryboard.storyboard b/DipUI/DipUITests/UIStoryboard.storyboard index f2264d9..26cefce 100644 --- a/DipUI/DipUITests/UIStoryboard.storyboard +++ b/DipUI/DipUITests/UIStoryboard.storyboard @@ -1,8 +1,8 @@ - + - + @@ -48,5 +48,28 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index 4da1c8e..b61654a 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ To injecte dependencies in this view controller when it is instantiated from sto 1. Register the dependencies in the `DependencyContainer`, as well as `MyViewController`: - ```swift +```swift import Dip @UIApplicationMain @@ -71,17 +71,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate { container.service = try container.resolve() as MyViewControllerService } - DependencyContainer.uiContainer = container + DependencyContainer.uiContainers = [container] } } ``` + > Note: All the depdencies are registered as implementations of abstractions (protocols). But `MyViewController` is registered as concrete type. But you can also make your view controller conform to some protocols and register them as implementations of these protocols. + In this case you can use `Nil` attribute type instead of `String`. -2. Set the container as one that will be used to inject dependencies in objects created by storyboards. You do it by setting static `uiContainer` property of `DependencyContainer ` class: `DependencyContainer.uiContainer = container` +2. Set the container as one that will be used to inject dependencies in objects created by storyboards. You do it by setting static `uiContainers` property of `DependencyContainer ` class: `DependencyContainer.uiContainers = [container]` 3. Make your view controller class conform to `StoryboardInstantiatable` protocol: - ```swift +```swift extension MyViewController: StoryboardInstantiatable { } ``` @@ -104,8 +106,8 @@ Now when view controller will be loaded from a storyboard Dip-UI will intercept container.register { MyViewController() as MyScene } extension MyViewController: StoryboardInstantiatable { - func didInstantiateFromStoryboard(container: DependencyContainer, tag: DependencyContainer.Tag) { - try! container.resolveDependenciesOf(self as MyScene, tag: tag) + func didInstantiateFromStoryboard(container: DependencyContainer, tag: DependencyContainer.Tag?) throws { + try container.resolveDependenciesOf(self as MyScene, tag: tag) } } ``` diff --git a/Sources/StoryboardInstantiatable.swift b/Sources/StoryboardInstantiatable.swift index deafcbe..9ea17b2 100644 --- a/Sources/StoryboardInstantiatable.swift +++ b/Sources/StoryboardInstantiatable.swift @@ -118,23 +118,18 @@ public protocol StoryboardInstantiatable { ``` */ - func didInstantiateFromStoryboard(container: DependencyContainer, tag: DependencyContainer.Tag) + func didInstantiateFromStoryboard(container: DependencyContainer, tag: DependencyContainer.Tag?) throws } extension StoryboardInstantiatable { - public func didInstantiateFromStoryboard(container: DependencyContainer, tag: DependencyContainer.Tag) { - do { - try container.resolveDependenciesOf(self, tag: tag) - } - catch { - print(error) - } + public func didInstantiateFromStoryboard(container: DependencyContainer, tag: DependencyContainer.Tag?) throws { + try container.resolveDependenciesOf(self, tag: tag) } } extension DependencyContainer { - ///A container that will be used to resolve dependencies of instances, created by stroyboards. - static public var uiContainer: DependencyContainer? + ///Containers that will be used to resolve dependencies of instances, created by stroyboards. + static public var uiContainers: [DependencyContainer] = [] } let DipTagAssociatedObjectKey = UnsafeMutablePointer.alloc(1) @@ -143,19 +138,19 @@ extension NSObject { ///A string tag that will be used to resolve dependencies of this instance ///if it implements `StoryboardInstantiatable` protocol. - @IBInspectable private(set) public var dipTag: String? { + private(set) public var dipTag: String? { get { return objc_getAssociatedObject(self, DipTagAssociatedObjectKey) as? String } set { objc_setAssociatedObject(self, DipTagAssociatedObjectKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) + guard let instantiatable = self as? StoryboardInstantiatable else { return } - if let - tag = newValue.map(DependencyContainer.Tag.String), - container = DependencyContainer.uiContainer, - instantiatable = self as? StoryboardInstantiatable { - instantiatable.didInstantiateFromStoryboard(container, tag: tag) - } + let tag = dipTag.map(DependencyContainer.Tag.String) + + for container in DependencyContainer.uiContainers { + guard let _ = try? instantiatable.didInstantiateFromStoryboard(container, tag: tag) else { continue } + } } }