diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ea81f5..be05c0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,14 @@ [#11](https://github.com/AliSoftware/Dip/pull/11), [@ilyapuchka](https://github.com/ilyapuchka) * Added methods to register/remove individual definitions. [#11](https://github.com/AliSoftware/Dip/pull/11), [@ilyapuchka](https://github.com/ilyapuchka) +* All `resolve` methods now can throw error if type can not be resolved. + [#15](https://github.com/AliSoftware/Dip/issues/15), [@ilyapuchka](https://github.com/ilyapuchka) #### Breaking Changes * Removed container thread-safety to enable recursion calls to `resolve`. **Access to container from multiple threads should be handled by clients** from now on. +* All `resolve` methods now can throw. ## 3.1.0 diff --git a/Dip/Dip.xcodeproj/project.pbxproj b/Dip/Dip.xcodeproj/project.pbxproj index 1b6eff6..f5d4e03 100644 --- a/Dip/Dip.xcodeproj/project.pbxproj +++ b/Dip/Dip.xcodeproj/project.pbxproj @@ -15,7 +15,7 @@ 094526B61BEA520B0034E72A /* Definition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094526B51BEA520B0034E72A /* Definition.swift */; }; 094526B81BEA536A0034E72A /* RuntimeArgumentsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094526B71BEA536A0034E72A /* RuntimeArgumentsTests.swift */; }; 0989323F1BEBC8CD00ACDA2B /* ComponentScopeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0989323E1BEBC8CD00ACDA2B /* ComponentScopeTests.swift */; }; - 09969C551BEB7C0A00F93C70 /* Dip.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 09969C541BEB7C0A00F93C70 /* Dip.podspec */; }; + 098A740D1C0C5CBC005C1296 /* DefinitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098A740C1C0C5CBC005C1296 /* DefinitionTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -40,7 +40,7 @@ 094526B51BEA520B0034E72A /* Definition.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = Definition.swift; sourceTree = ""; tabWidth = 2; }; 094526B71BEA536A0034E72A /* RuntimeArgumentsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuntimeArgumentsTests.swift; sourceTree = ""; }; 0989323E1BEBC8CD00ACDA2B /* ComponentScopeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComponentScopeTests.swift; sourceTree = ""; }; - 09969C541BEB7C0A00F93C70 /* Dip.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Dip.podspec; path = ../Dip.podspec; sourceTree = ""; }; + 098A740C1C0C5CBC005C1296 /* DefinitionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefinitionTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -100,6 +100,7 @@ 094526A01BEA1CFF0034E72A /* DipTests.swift */, 094526B71BEA536A0034E72A /* RuntimeArgumentsTests.swift */, 0989323E1BEBC8CD00ACDA2B /* ComponentScopeTests.swift */, + 098A740C1C0C5CBC005C1296 /* DefinitionTests.swift */, 094526A21BEA1CFF0034E72A /* Info.plist */, ); path = DipTests; @@ -223,6 +224,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 098A740D1C0C5CBC005C1296 /* DefinitionTests.swift in Sources */, 094526A11BEA1CFF0034E72A /* DipTests.swift in Sources */, 0989323F1BEBC8CD00ACDA2B /* ComponentScopeTests.swift in Sources */, 094526B81BEA536A0034E72A /* RuntimeArgumentsTests.swift in Sources */, diff --git a/Dip/Dip.xcodeproj/xcshareddata/xcschemes/Dip.xcscheme b/Dip/Dip.xcodeproj/xcshareddata/xcschemes/Dip.xcscheme index 428cf1d..332314c 100644 --- a/Dip/Dip.xcodeproj/xcshareddata/xcschemes/Dip.xcscheme +++ b/Dip/Dip.xcodeproj/xcshareddata/xcschemes/Dip.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/Dip/Dip/Dip.swift b/Dip/Dip/Dip.swift index 4c790b8..d9b8818 100644 --- a/Dip/Dip/Dip.swift +++ b/Dip/Dip/Dip.swift @@ -96,7 +96,7 @@ public class DependencyContainer { container.register(tag: "service") { ServiceImp() as Service } container.register(.ObjectGraph) { ServiceImp() as Service } container.register { [unowned container] - ClientImp(service: container.resolve() as Service) as Client + ClientImp(service: try! container.resolve() as Service) as Client } ``` */ @@ -152,18 +152,21 @@ public class DependencyContainer { If no definition was registered with this `tag` for this `protocol`, it will try to resolve the definition associated with `nil` (no tag). + Will throw `DipError.DefinitionNotFound` if no registered definition found + that would match type, runtime arguments and tag. + - parameter tag: The arbitrary tag to look for when resolving this protocol. **Example**: ```swift - let service = container.resolve() as Service - let service = container.resolve(tag: "service") as Service - let service: Service = container.resolve() + let service = try! container.resolve() as Service + let service = try! container.resolve(tag: "service") as Service + let service: Service = try! container.resolve() ``` */ - public func resolve(tag tag: Tag? = nil) -> T { - return resolve(tag: tag) { (factory: ()->T) in factory() } + public func resolve(tag tag: Tag? = nil) throws -> T { + return try resolve(tag: tag) { (factory: ()->T) in factory() } } /** @@ -178,21 +181,20 @@ public class DependencyContainer { (currently it's up to six) like in this example: ```swift - public func resolve(tag tag: Tag? = nil, _ arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, ...) -> T { - return resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, ...) -> T) in factory(arg1, arg2, arg3, ...) } + public func resolve(tag tag: Tag? = nil, _ arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, ...) throws -> T { + return try resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, ...) -> T) in factory(arg1, arg2, arg3, ...) } } ``` Though before you do that you should probably review your design and try to reduce the number of dependencies. */ - public func resolve(tag tag: Tag? = nil, builder: F->T) -> T { + public func resolve(tag tag: Tag? = nil, builder: F->T) throws -> T { let key = DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: tag) let nilTagKey = tag.map { _ in DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: nil) } guard let definition = (self.definitions[key] ?? self.definitions[nilTagKey]) as? DefinitionOf else { - fatalError("No definition registered with " + (tag == nil ? "\(key)" : "\(key) or \(nilTagKey)") + ". " - + "Check the tag, type you try to resolve, number, order and types of runtime arguments passed to `resolve()`.") + throw DipError.DefinitionNotFound(key) } let usingKey: DefinitionKey? = definition.scope == .ObjectGraph ? key : nil @@ -302,6 +304,17 @@ public func ==(lhs: DependencyContainer.Tag, rhs: DependencyContainer.Tag) -> Bo } } +enum DipError: ErrorType, CustomStringConvertible { + case DefinitionNotFound(DefinitionKey) + + var description: String { + switch self { + case let .DefinitionNotFound(key): + return "No definition registered for \(key). Check the tag, type you try to resolve, number, order and types of runtime arguments passed to `resolve()`." + } + } +} + extension Dictionary { subscript(key: Key?) -> Value? { get { diff --git a/Dip/Dip/RuntimeArguments.swift b/Dip/Dip/RuntimeArguments.swift index 6cc34fd..f1fd2d8 100644 --- a/Dip/Dip/RuntimeArguments.swift +++ b/Dip/Dip/RuntimeArguments.swift @@ -54,8 +54,8 @@ extension DependencyContainer { - seealso: `resolve(tag:builder:)` */ - public func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1) -> T { - return resolve(tag: tag) { (factory: (Arg1) -> T) in factory(arg1) } + public func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1) throws -> T { + return try resolve(tag: tag) { (factory: (Arg1) -> T) in factory(arg1) } } // MARK: 2 Runtime Arguments @@ -66,8 +66,8 @@ extension DependencyContainer { } /// - seealso: `resolve(tag:_:)` - public func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2) -> T { - return resolve(tag: tag) { (factory: (Arg1, Arg2) -> T) in factory(arg1, arg2) } + public func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2) throws -> T { + return try resolve(tag: tag) { (factory: (Arg1, Arg2) -> T) in factory(arg1, arg2) } } // MARK: 3 Runtime Arguments @@ -77,8 +77,8 @@ extension DependencyContainer { } /// - seealso: `resolve(tag:withArguments:)` - public func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3) -> T { - return resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3) -> T) in factory(arg1, arg2, arg3) } + public func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3) throws -> T { + return try resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3) -> T) in factory(arg1, arg2, arg3) } } // MARK: 4 Runtime Arguments @@ -88,8 +88,8 @@ extension DependencyContainer { } /// - seealso: `resolve(tag:withArguments:)` - public func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4) -> T { - return resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, Arg4) -> T) in factory(arg1, arg2, arg3, arg4) } + public func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4) throws -> T { + return try resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, Arg4) -> T) in factory(arg1, arg2, arg3, arg4) } } // MARK: 5 Runtime Arguments @@ -99,8 +99,8 @@ extension DependencyContainer { } /// - seealso: `resolve(tag:withArguments:)` - public func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4, _ arg5: Arg5) -> T { - return resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, Arg4, Arg5) -> T) in factory(arg1, arg2, arg3, arg4, arg5) } + public func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4, _ arg5: Arg5) throws -> T { + return try resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, Arg4, Arg5) -> T) in factory(arg1, arg2, arg3, arg4, arg5) } } // MARK: 6 Runtime Arguments @@ -110,8 +110,8 @@ extension DependencyContainer { } /// - seealso: `resolve(tag:withArguments:)` - public func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4, _ arg5: Arg5, _ arg6: Arg6) -> T { - return resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) -> T) in factory(arg1, arg2, arg3, arg4, arg5, arg6) } + public func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4, _ arg5: Arg5, _ arg6: Arg6) throws -> T { + return try resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) -> T) in factory(arg1, arg2, arg3, arg4, arg5, arg6) } } } diff --git a/Dip/DipTests/ComponentScopeTests.swift b/Dip/DipTests/ComponentScopeTests.swift index fe484bb..4453709 100644 --- a/Dip/DipTests/ComponentScopeTests.swift +++ b/Dip/DipTests/ComponentScopeTests.swift @@ -55,8 +55,8 @@ class ComponentScopeTests: XCTestCase { container.register { ServiceImp1() as Service } //when - let service1 = container.resolve() as Service - let service2 = container.resolve() as Service + let service1 = try! container.resolve() as Service + let service2 = try! container.resolve() as Service //then XCTAssertFalse((service1 as! ServiceImp1) === (service2 as! ServiceImp1)) @@ -67,8 +67,8 @@ class ComponentScopeTests: XCTestCase { container.register(.Singleton) { ServiceImp1() as Service } //when - let service1 = container.resolve() as Service - let service2 = container.resolve() as Service + let service1 = try! container.resolve() as Service + let service2 = try! container.resolve() as Service //then XCTAssertTrue((service1 as! ServiceImp1) === (service2 as! ServiceImp1)) @@ -90,14 +90,14 @@ class ComponentScopeTests: XCTestCase { func testThatItReusesInstanceInObjectGraphScopeDuringResolve() { //given - container.register(.ObjectGraph) { [unowned container] in Client(server: container.resolve()) as Client } + container.register(.ObjectGraph) { [unowned container] in Client(server: try! container.resolve()) as Client } container.register(.ObjectGraph) { Server() as Server }.resolveDependencies { container, server in - server.client = container.resolve() as Client + server.client = try! container.resolve() as Client } //when - let client = container.resolve() as Client + let client = try! container.resolve() as Client //then let server = client.server @@ -106,16 +106,16 @@ class ComponentScopeTests: XCTestCase { func testThatItDoesNotReuseInstanceInObjectGraphScopeInNextResolve() { //given - container.register(.ObjectGraph) { [unowned container] in Client(server: container.resolve()) as Client } + container.register(.ObjectGraph) { [unowned container] in Client(server: try! container.resolve()) as Client } container.register(.ObjectGraph) { Server() as Server }.resolveDependencies { container, server in - server.client = container.resolve() as Client + server.client = try! container.resolve() as Client } //when - let client = container.resolve() as Client + let client = try! container.resolve() as Client let server = client.server - let anotherClient = container.resolve() as Client + let anotherClient = try! container.resolve() as Client let anotherServer = anotherClient.server //then @@ -127,12 +127,12 @@ class ComponentScopeTests: XCTestCase { //given var service2: Service? container.register(.ObjectGraph) { ServiceImp1() as Service }.resolveDependencies { (c, _) in - service2 = c.resolve(tag: "service") as Service + service2 = try! c.resolve(tag: "service") as Service } container.register(tag: "service", .ObjectGraph) { ServiceImp2() as Service} //when - let service1 = container.resolve(tag: "tag") as Service + let service1 = try! container.resolve(tag: "tag") as Service //then XCTAssertTrue(service1 is ServiceImp1) diff --git a/Dip/DipTests/DefinitionTests.swift b/Dip/DipTests/DefinitionTests.swift new file mode 100644 index 0000000..308ec7f --- /dev/null +++ b/Dip/DipTests/DefinitionTests.swift @@ -0,0 +1,68 @@ +// +// Dip +// +// Copyright (c) 2015 Olivier Halligon +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import XCTest +@testable import Dip + +class DefinitionTests: XCTestCase { + + typealias F1 = ()->Service + typealias F2 = (String)->Service + + let tag1 = DependencyContainer.Tag.String("tag1") + let tag2 = DependencyContainer.Tag.String("tag2") + + func testThatDefinitionKeyIsEqualBy_Type_Factory_Tag() { + let equalKey1 = DefinitionKey(protocolType: Service.self, factoryType: F1.self, associatedTag: tag1) + let equalKey2 = DefinitionKey(protocolType: Service.self, factoryType: F1.self, associatedTag: tag1) + + XCTAssertEqual(equalKey1, equalKey2) + XCTAssertEqual(equalKey1.hashValue, equalKey2.hashValue) + } + + func testThatDefinitionKeysWithDifferentTypesAreNotEqual() { + let keyWithDifferentType1 = DefinitionKey(protocolType: Service.self, factoryType: F1.self, associatedTag: nil) + let keyWithDifferentType2 = DefinitionKey(protocolType: AnyObject.self, factoryType: F1.self, associatedTag: nil) + + XCTAssertNotEqual(keyWithDifferentType1, keyWithDifferentType2) + XCTAssertNotEqual(keyWithDifferentType1.hashValue, keyWithDifferentType2.hashValue) + } + + func testThatDefinitionKeysWithDifferentFactoriesAreNotEqual() { + let keyWithDifferentFactory1 = DefinitionKey(protocolType: Service.self, factoryType: F1.self, associatedTag: nil) + let keyWithDifferentFactory2 = DefinitionKey(protocolType: Service.self, factoryType: F2.self, associatedTag: nil) + + XCTAssertNotEqual(keyWithDifferentFactory1, keyWithDifferentFactory2) + XCTAssertNotEqual(keyWithDifferentFactory1.hashValue, keyWithDifferentFactory2.hashValue) + } + + func testThatDefinitionKeysWithDifferentTagsAreNotEqual() { + let keyWithDifferentTag1 = DefinitionKey(protocolType: Service.self, factoryType: F1.self, associatedTag: tag1) + let keyWithDifferentTag2 = DefinitionKey(protocolType: Service.self, factoryType: F1.self, associatedTag: tag2) + + XCTAssertNotEqual(keyWithDifferentTag1, keyWithDifferentTag2) + XCTAssertNotEqual(keyWithDifferentTag1.hashValue, keyWithDifferentTag2.hashValue) + } + +} diff --git a/Dip/DipTests/DipTests.swift b/Dip/DipTests/DipTests.swift index af5fe8d..38c8e14 100644 --- a/Dip/DipTests/DipTests.swift +++ b/Dip/DipTests/DipTests.swift @@ -53,7 +53,7 @@ class DipTests: XCTestCase { container.register { ServiceImp1() as Service } //when - let serviceInstance = container.resolve() as Service + let serviceInstance = try! container.resolve() as Service //then XCTAssertTrue(serviceInstance is ServiceImp1) @@ -64,7 +64,7 @@ class DipTests: XCTestCase { container.register(tag: "service") { ServiceImp1() as Service } //when - let serviceInstance = container.resolve(tag: "service") as Service + let serviceInstance = try! container.resolve(tag: "service") as Service //then XCTAssertTrue(serviceInstance is ServiceImp1) @@ -76,8 +76,8 @@ class DipTests: XCTestCase { container.register(tag: "service2") { ServiceImp2() as Service } //when - let service1Instance = container.resolve(tag: "service1") as Service - let service2Instance = container.resolve(tag: "service2") as Service + let service1Instance = try! container.resolve(tag: "service1") as Service + let service2Instance = try! container.resolve(tag: "service2") as Service //then XCTAssertTrue(service1Instance is ServiceImp1) @@ -87,11 +87,11 @@ class DipTests: XCTestCase { func testThatNewRegistrationOverridesPreviousRegistration() { //given container.register { ServiceImp1() as Service } - let service1 = container.resolve() as Service + let service1 = try! container.resolve() as Service //when container.register { ServiceImp2() as Service } - let service2 = container.resolve() as Service + let service2 = try! container.resolve() as Service //then XCTAssertTrue(service1 is ServiceImp1) @@ -106,22 +106,67 @@ class DipTests: XCTestCase { } //when - container.resolve() as Service + try! container.resolve() as Service //then XCTAssertTrue(resolveDependenciesCalled) } - func testThatItReusesInstanceRegisteredAsSingleton() { + func testThatItThrowsErrorIfCanNotFindDefinitionForType() { //given - container.register(.Singleton) { ServiceImp1() as Service } + container.register { ServiceImp1() as ServiceImp1 } //when - let service1 = container.resolve() as Service - let service2 = container.resolve() as Service + do { + try container.resolve() as Service + XCTFail("Unexpectedly resolved protocol") + } + catch DipError.DefinitionNotFound(let key) { + typealias F = ()->Service + let expectedKey = DefinitionKey(protocolType: Service.self, factoryType: F.self, associatedTag: nil) + XCTAssertEqual(key, expectedKey) + } + catch { + XCTFail("Thrown unexpected error") + } + } + + func testThatItThrowsErrorIfCanNotFindDefinitionForTag() { + //given + container.register(tag: "some tag") { ServiceImp1() as Service } - //then - XCTAssertTrue((service1 as! ServiceImp1) === (service2 as! ServiceImp1)) + //when + do { + try container.resolve(tag: "other tag") as Service + XCTFail("Unexpectedly resolved protocol") + } + catch DipError.DefinitionNotFound(let key) { + typealias F = ()->Service + let expectedKey = DefinitionKey(protocolType: Service.self, factoryType: F.self, associatedTag: "other tag") + XCTAssertEqual(key, expectedKey) + } + catch { + XCTFail("Thrown unexpected error") + } + } + + func testThatItThrowsErrorIfCanNotFindDefinitionForFactory() { + //given + container.register { ServiceImp1() as Service } + + //when + do { + try container.resolve(withArguments: "some string") as Service + XCTFail("Unexpectedly resolved protocol") + } + catch DipError.DefinitionNotFound(let key) { + typealias F = (String)->Service + let expectedKey = DefinitionKey(protocolType: Service.self, factoryType: F.self, associatedTag: nil) + XCTAssertEqual(key, expectedKey) + } + catch { + XCTFail("Thrown unexpected error") + } } } diff --git a/Dip/DipTests/RuntimeArgumentsTests.swift b/Dip/DipTests/RuntimeArgumentsTests.swift index 7aeefcd..f5006fe 100644 --- a/Dip/DipTests/RuntimeArgumentsTests.swift +++ b/Dip/DipTests/RuntimeArgumentsTests.swift @@ -56,7 +56,7 @@ class RuntimeArgumentsTests: XCTestCase { }) //when - let service = container.resolve(withArguments: arg1) as Service + let service = try! container.resolve(withArguments: arg1) as Service //then XCTAssertTrue(service is ServiceImp1) @@ -72,7 +72,7 @@ class RuntimeArgumentsTests: XCTestCase { } //when - let service = container.resolve(withArguments: arg1, arg2) as Service + let service = try! container.resolve(withArguments: arg1, arg2) as Service //then XCTAssertTrue(service is ServiceImp1) @@ -88,7 +88,7 @@ class RuntimeArgumentsTests: XCTestCase { } //when - let service = container.resolve(withArguments: arg1, arg2, arg3) as Service + let service = try! container.resolve(withArguments: arg1, arg2, arg3) as Service //then XCTAssertTrue(service is ServiceImp1) @@ -105,7 +105,7 @@ class RuntimeArgumentsTests: XCTestCase { } //when - let service = container.resolve(withArguments: arg1, arg2, arg3, arg4) as Service + let service = try! container.resolve(withArguments: arg1, arg2, arg3, arg4) as Service //then XCTAssertTrue(service is ServiceImp1) @@ -123,7 +123,7 @@ class RuntimeArgumentsTests: XCTestCase { } //when - let service = container.resolve(withArguments: arg1, arg2, arg3, arg4, arg5) as Service + let service = try! container.resolve(withArguments: arg1, arg2, arg3, arg4, arg5) as Service //then XCTAssertTrue(service is ServiceImp1) @@ -142,7 +142,7 @@ class RuntimeArgumentsTests: XCTestCase { } //when - let service = container.resolve(withArguments: arg1, arg2, arg3, arg4, arg5, arg6) as Service + let service = try! container.resolve(withArguments: arg1, arg2, arg3, arg4, arg5, arg6) as Service //then XCTAssertTrue(service is ServiceImp1) @@ -155,8 +155,8 @@ class RuntimeArgumentsTests: XCTestCase { container.register { (a1: Int, a2: Int) in ServiceImp2() as Service } //when - let service1 = container.resolve(withArguments: arg1) as Service - let service2 = container.resolve(withArguments: arg1, arg2) as Service + let service1 = try! container.resolve(withArguments: arg1) as Service + let service2 = try! container.resolve(withArguments: arg1, arg2) as Service //then XCTAssertTrue(service1 is ServiceImp1) @@ -170,8 +170,8 @@ class RuntimeArgumentsTests: XCTestCase { container.register(factory: { (a1: String) in ServiceImp2() as Service }) //when - let service1 = container.resolve(withArguments: arg1) as Service - let service2 = container.resolve(withArguments: arg2) as Service + let service1 = try! container.resolve(withArguments: arg1) as Service + let service2 = try! container.resolve(withArguments: arg2) as Service //then XCTAssertTrue(service1 is ServiceImp1) @@ -185,8 +185,8 @@ class RuntimeArgumentsTests: XCTestCase { container.register { (a1: String, a2: Int) in ServiceImp2() as Service } //when - let service1 = container.resolve(withArguments: arg1, arg2) as Service - let service2 = container.resolve(withArguments: arg2, arg1) as Service + let service1 = try! container.resolve(withArguments: arg1, arg2) as Service + let service2 = try! container.resolve(withArguments: arg2, arg1) as Service //then XCTAssertTrue(service1 is ServiceImp1) @@ -197,11 +197,11 @@ class RuntimeArgumentsTests: XCTestCase { //given let arg1 = 1, arg2 = 2 container.register { (a1: Int, a2: Int) in ServiceImp1() as Service } - let service1 = container.resolve(withArguments: arg1, arg2) as Service + let service1 = try! container.resolve(withArguments: arg1, arg2) as Service //when container.register { (a1: Int, a2: Int) in ServiceImp2() as Service } - let service2 = container.resolve(withArguments: arg1, arg2) as Service + let service2 = try! container.resolve(withArguments: arg1, arg2) as Service //then XCTAssertTrue(service1 is ServiceImp1) @@ -217,11 +217,11 @@ class RuntimeArgumentsTests: XCTestCase { //when let url: NSURL = NSURL(string: "http://example.com")! - let service1 = container.resolve(withArguments: 80, url) as Service - let service2 = container.resolve(withArguments: 80, NSURL(string: "http://example.com")) as Service + let service1 = try! container.resolve(withArguments: 80, url) as Service + let service2 = try! container.resolve(withArguments: 80, NSURL(string: "http://example.com")) as Service - let service3 = container.resolve(withArguments: 80, NSURL(string: "http://example.com")! as NSURL!) as Service - let service4 = container.resolve(withArguments: 80, NSURL(string: "http://example.com")!) as Service + let service3 = try! container.resolve(withArguments: 80, NSURL(string: "http://example.com")! as NSURL!) as Service + let service4 = try! container.resolve(withArguments: 80, NSURL(string: "http://example.com")!) as Service //then XCTAssertEqual(service1.getServiceName(), name1) diff --git a/DipPlayground.playground/Pages/Circular dependencies.xcplaygroundpage/Contents.swift b/DipPlayground.playground/Pages/Circular dependencies.xcplaygroundpage/Contents.swift index f7d9d80..1b70d4b 100644 --- a/DipPlayground.playground/Pages/Circular dependencies.xcplaygroundpage/Contents.swift +++ b/DipPlayground.playground/Pages/Circular dependencies.xcplaygroundpage/Contents.swift @@ -44,12 +44,12 @@ Now you can register those classes in container: */ container.register(.ObjectGraph) { [unowned container] in - Interactor(networkClient: container.resolve()) as NetworkClientDelegate + Interactor(networkClient: try! container.resolve()) as NetworkClientDelegate } container.register(.ObjectGraph) { NetworkClientImp() as NetworkClient } - .resolveDependencies(container) { (container, client) -> () in - client.delegate = container.resolve() as NetworkClientDelegate + .resolveDependencies { (container, client) -> () in + client.delegate = try! container.resolve() as NetworkClientDelegate } /*: @@ -65,14 +65,14 @@ This way `DependencyContainer` breaks infinite recursion that would happen if we Now when you resolve `NetworkClientDelegate` you will get an instance of `Interactor` that will have client with delegate referencing the same `Interactor` instance: */ -let interactor = container.resolve() as NetworkClientDelegate +let interactor = try! container.resolve() as NetworkClientDelegate interactor.networkClient.delegate === interactor // true: they are the same instances /*: **Warning**: Note that one of the properties (`delegate`) is defined as _weak_. That's crucial to avoid retain cycle. But now if you try to resolve `NetworkClient` first it's delegate will be released before `resolve` returns, bcause no one holds a reference to it except the container. */ -let networkClient = container.resolve() as NetworkClient +let networkClient = try! container.resolve() as NetworkClient networkClient.delegate // delegate was alread released =( /*: @@ -91,15 +91,15 @@ If we would have used `.Prototype` for one of the components it will lead to the container.reset() container.register(.Prototype) { [unowned container] in - Interactor(networkClient: container.resolve()) as NetworkClientDelegate + Interactor(networkClient: try! container.resolve()) as NetworkClientDelegate } container.register(.ObjectGraph) { NetworkClientImp() as NetworkClient } - .resolveDependencies(container) { (container, client) -> () in - client.delegate = container.resolve() as NetworkClientDelegate + .resolveDependencies { (container, client) -> () in + client.delegate = try! container.resolve() as NetworkClientDelegate } -let invalidInteractor = container.resolve() as NetworkClientDelegate +let invalidInteractor = try! container.resolve() as NetworkClientDelegate invalidInteractor.networkClient.delegate // that is not valid //: [Next: Shared Instances](@next) diff --git a/DipPlayground.playground/Pages/Resolving components.xcplaygroundpage/Contents.swift b/DipPlayground.playground/Pages/Resolving components.xcplaygroundpage/Contents.swift index 93499ca..e839cae 100644 --- a/DipPlayground.playground/Pages/Resolving components.xcplaygroundpage/Contents.swift +++ b/DipPlayground.playground/Pages/Resolving components.xcplaygroundpage/Contents.swift @@ -12,7 +12,7 @@ let container = DependencyContainer { container in You resolve previously registered definition using `resolve` method: */ -var service = container.resolve() as Service +var service = try! container.resolve() as Service /*: That code says that you want your `container` to give you an instance that was registered as implementation of `Service` protocol. @@ -20,7 +20,7 @@ That code says that you want your `container` to give you an instance that was r It's important to specify the same type that you used for registration. You can use either `as` syntax, or specify type of you variable when you define it: */ -let otherService: Service = container.resolve() +let otherService: Service = try! container.resolve() /*: Both ways will let the `container` detect the type that you want to resolve as. We prefer the `as` syntax because it reads more naturally in Swift. @@ -32,16 +32,16 @@ container.register(tag: "production") { ServiceImp1() as Service } container.register(tag: "test") { ServiceImp2() as Service } // Will give you a ServiceImp1 instance -let productionService = container.resolve(tag: "production") as Service +let productionService = try! container.resolve(tag: "production") as Service // Will give you a ServiceImp2 instance -let testService = container.resolve(tag: "test") as Service +let testService = try! container.resolve(tag: "test") as Service // Will give you a ServiceImp1 because one was registered without a tag on line 4 -let defaultService = container.resolve() as Service +let defaultService = try! container.resolve() as Service /*: You can use runtime arguments to resolve components. Dip supports up to six arguments. For more details see ["Runtime arguments"](Runtime%20arguments). */ container.register { service in ClientImp1(service: service) as Client } -let client = container.resolve(service) as Client +let client = try! container.resolve(withArguments: service) as Client //: [Next: Runtime Arguments](@next) diff --git a/DipPlayground.playground/Pages/Runtime arguments.xcplaygroundpage/Contents.swift b/DipPlayground.playground/Pages/Runtime arguments.xcplaygroundpage/Contents.swift index 017c770..2bb08a0 100644 --- a/DipPlayground.playground/Pages/Runtime arguments.xcplaygroundpage/Contents.swift +++ b/DipPlayground.playground/Pages/Runtime arguments.xcplaygroundpage/Contents.swift @@ -18,10 +18,10 @@ container.register { (port: Int, url: NSURL?) in ServiceImp4(name: "3", baseURL: container.register { (port: Int, url: NSURL!) in ServiceImp4(name: "4", baseURL: url, port: port) as Service } let url: NSURL = NSURL(string: "http://example.com")! -let service1 = container.resolve(withArguments: url, 80) as Service -let service2 = container.resolve(withArguments: 80, url) as Service -let service3 = container.resolve(withArguments: 80, NSURL(string: "http://example.com")) as Service -let service4 = container.resolve(withArguments: 80, NSURL(string: "http://example.com")! as NSURL!) as Service +let service1 = try! container.resolve(withArguments: url, 80) as Service +let service2 = try! container.resolve(withArguments: 80, url) as Service +let service3 = try! container.resolve(withArguments: 80, NSURL(string: "http://example.com")) as Service +let service4 = try! container.resolve(withArguments: 80, NSURL(string: "http://example.com")! as NSURL!) as Service (service1 as! ServiceImp4).name (service2 as! ServiceImp4).name @@ -39,8 +39,8 @@ extension DependencyContainer { return registerFactory(tag: tag, scope: scope, factory: factory) as DefinitionOf T> } - public func resolve(tag tag: Tag? = nil, _ arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4, _ arg5: Arg5, _ arg6: Arg6) -> T { - return resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) -> T) in factory(arg1, arg2, arg3, arg4, arg5, arg6) } + public func resolve(tag tag: Tag? = nil, _ arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4, _ arg5: Arg5, _ arg6: Arg6) throws -> T { + return try resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) -> T) in factory(arg1, arg2, arg3, arg4, arg5, arg6) } } } diff --git a/DipPlayground.playground/Pages/Scopes.xcplaygroundpage/Contents.swift b/DipPlayground.playground/Pages/Scopes.xcplaygroundpage/Contents.swift index 53d6fd8..a0bb378 100644 --- a/DipPlayground.playground/Pages/Scopes.xcplaygroundpage/Contents.swift +++ b/DipPlayground.playground/Pages/Scopes.xcplaygroundpage/Contents.swift @@ -22,24 +22,24 @@ container.register(tag: "prototype", .Prototype) { ServiceImp1() as Service } container.register(tag: "object graph", .ObjectGraph) { ServiceImp2() as Service } container.register(tag: "shared instance", .Singleton) { ServiceImp3() as Service } -let service = container.resolve() as Service -let anotherService = container.resolve() as Service +let service = try! container.resolve() as Service +let anotherService = try! container.resolve() as Service // They are different instances as the scope defaults to .Prototype service as! ServiceImp1 === anotherService as! ServiceImp1 // false -let prototypeService = container.resolve(tag: "prototype") as Service -let anotherPrototypeService = container.resolve(tag: "prototype") as Service +let prototypeService = try! container.resolve(tag: "prototype") as Service +let anotherPrototypeService = try! container.resolve(tag: "prototype") as Service // They are different instances: prototypeService as! ServiceImp1 === anotherPrototypeService as! ServiceImp1 // false -let graphService = container.resolve(tag: "object graph") as Service -let anotherGraphService = container.resolve(tag: "object graph") as Service +let graphService = try! container.resolve(tag: "object graph") as Service +let anotherGraphService = try! container.resolve(tag: "object graph") as Service // still different instances — the ObjectGraph scope only keep instances during one (recursive) resolution call, // so the two calls on the two lines above are different calls and use different instances graphService as! ServiceImp2 === anotherGraphService as! ServiceImp2 // false -let sharedService = container.resolve(tag: "shared instance") as Service -let sameSharedService = container.resolve(tag: "shared instance") as Service +let sharedService = try! container.resolve(tag: "shared instance") as Service +let sameSharedService = try! container.resolve(tag: "shared instance") as Service // same instances, the singleton scope keep and reuse instances during the lifetime of the container sharedService as! ServiceImp3 === sameSharedService as! ServiceImp3 diff --git a/DipPlayground.playground/Pages/Shared Instances.xcplaygroundpage/Contents.swift b/DipPlayground.playground/Pages/Shared Instances.xcplaygroundpage/Contents.swift index 8092bf9..c17d7d5 100644 --- a/DipPlayground.playground/Pages/Shared Instances.xcplaygroundpage/Contents.swift +++ b/DipPlayground.playground/Pages/Shared Instances.xcplaygroundpage/Contents.swift @@ -112,7 +112,7 @@ class DipViewController: UIViewController { convenience init(dependencies: DependencyContainer) { self.init() - self.apiClient = dependencies.resolve() as ApiClientProtocol + self.apiClient = try! dependencies.resolve() as ApiClientProtocol } init() { @@ -136,7 +136,7 @@ protocol ApiClientProvider { extension DependencyContainer: ApiClientProvider { func apiClient() -> ApiClientProtocol { - return self.resolve() as ApiClientProtocol + return try! self.resolve() as ApiClientProtocol } } diff --git a/DipPlayground.playground/Pages/Testing.xcplaygroundpage/Contents.swift b/DipPlayground.playground/Pages/Testing.xcplaygroundpage/Contents.swift index 213ebbe..5b18226 100644 --- a/DipPlayground.playground/Pages/Testing.xcplaygroundpage/Contents.swift +++ b/DipPlayground.playground/Pages/Testing.xcplaygroundpage/Contents.swift @@ -57,7 +57,7 @@ Register fake implementation as `Service`: func testThatDoSomethingIsCalled() { let sut = Client() - sut.service = container.resolve() as Service + sut.service = try! container.resolve() as Service sut.callService() diff --git a/README.md b/README.md index b7eff7d..1432cc3 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Typically, to register your dependencies as early as possible in your app life-c ```swift container.register { ServiceImp() as Service } -let service = container.resolve() as Service +let service = try! container.resolve() as Service ``` ### Scopes @@ -130,7 +130,7 @@ let wsDependencies = DependencyContainer() { dip in dip.register(tag: WebService.StashipWS.tag) { URLSessionNetworkLayer(baseURL: "http://dev.myapi.com/api/")! as NetworkLayer } } -let networkLayer = dip.resolve(tag: WebService.PersonWS.tag) as NetworkLayer +let networkLayer = try! dip.resolve(tag: WebService.PersonWS.tag) as NetworkLayer ``` ### Runtime arguments @@ -144,9 +144,9 @@ let webServices = DependencyContainer() { webServices in webServices.register { (port: Int, url: NSURL?) in WebServiceImp3(url!, port: port) as WebServiceAPI } } -let service1 = webServices.resolve(withArguments: NSURL(string: "http://example.url")!, 80) as WebServiceAPI // service1 is WebServiceImp1 -let service2 = webServices.resolve(withArguments: 80, NSURL(string: "http://example.url")!) as WebServiceAPI // service2 is WebServiceImp2 -let service3 = webServices.resolve(withArguments: 80, NSURL(string: "http://example.url")) as WebServiceAPI // service3 is WebServiceImp3 +let service1 = try! webServices.resolve(withArguments: NSURL(string: "http://example.url")!, 80) as WebServiceAPI // service1 is WebServiceImp1 +let service2 = try! webServices.resolve(withArguments: 80, NSURL(string: "http://example.url")!) as WebServiceAPI // service2 is WebServiceImp2 +let service3 = try! webServices.resolve(withArguments: 80, NSURL(string: "http://example.url")) as WebServiceAPI // service3 is WebServiceImp3 ``` Though Dip provides support for up to six runtime arguments out of the box you can extend this number using following code snippet for seven arguments: @@ -156,8 +156,8 @@ func register(tag: Tag? = nil, scop return registerFactory(tag, scope: .Prototype, factory: factory) as DefinitionOf T)> } -func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4, _ arg5: Arg5, _ arg6: Arg6, _ arg7: Arg7) -> T { - return resolve(tag) { (factory: (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7) -> T) in factory(arg1, arg2, arg3, arg4, arg5, arg6, arg7) } +func resolve(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4, _ arg5: Arg5, _ arg6: Arg6, _ arg7: Arg7) throws -> T { + return try resolve(tag) { (factory: (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7) -> T) in factory(arg1, arg2, arg3, arg4, arg5, arg6, arg7) } } ``` @@ -168,12 +168,12 @@ _Dip_ supports circular dependencies. To resolve them use `ObjectGraph` scope an ```swift container.register(.ObjectGraph) { [unowned container] in - ClientImp(server: container.resolve() as Server) as Client + ClientImp(server: try! container.resolve() as Server) as Client } container.register(.ObjectGraph) { ServerImp() as Server } .resolveDependencies { container, server in - server.client = container.resolve() as Client + server.client = try! container.resolve() as Client } ``` More infromation about circular dependencies you can find in a playground. @@ -206,17 +206,17 @@ Then to use dependencies throughout your app, use `dip.resolve()`, like this: ```swift struct WebService { - let env: EnvironmentType = dip.resolve() + let env: EnvironmentType = try! dip.resolve() func sendRequest(path: String, …) { // ... use stuff like env.baseURL here } } struct SomeViewModel { - let ws: WebServiceType = dip.resolve() + let ws: WebServiceType = try! dip.resolve() var friendsProvider: FriendsProviderType init(userName: String) { - friendsProvider = dip.resolve(tag: userName, userName) + friendsProvider = try! dip.resolve(tag: userName, userName) } func foo() { ws.someMethodDeclaredOnWebServiceType() diff --git a/SampleApp/DipSampleApp/Providers/SWAPIPersonProvider.swift b/SampleApp/DipSampleApp/Providers/SWAPIPersonProvider.swift index 8d08ab4..e254b5c 100644 --- a/SampleApp/DipSampleApp/Providers/SWAPIPersonProvider.swift +++ b/SampleApp/DipSampleApp/Providers/SWAPIPersonProvider.swift @@ -9,7 +9,7 @@ import Foundation struct SWAPIPersonProvider : PersonProviderAPI { - let ws = wsDependencies.resolve(tag: WebService.PersonWS.tag) as NetworkLayer + let ws = try! wsDependencies.resolve(tag: WebService.PersonWS.tag) as NetworkLayer func fetchIDs(completion: [Int] -> Void) { ws.request("people") { response in diff --git a/SampleApp/DipSampleApp/Providers/SWAPIStarshipProvider.swift b/SampleApp/DipSampleApp/Providers/SWAPIStarshipProvider.swift index cebc96f..92358b8 100644 --- a/SampleApp/DipSampleApp/Providers/SWAPIStarshipProvider.swift +++ b/SampleApp/DipSampleApp/Providers/SWAPIStarshipProvider.swift @@ -9,7 +9,7 @@ import Foundation struct SWAPIStarshipProvider : StarshipProviderAPI { - let ws = wsDependencies.resolve(tag: WebService.StarshipWS.tag) as NetworkLayer + let ws = try! wsDependencies.resolve(tag: WebService.StarshipWS.tag) as NetworkLayer func fetchIDs(completion: [Int] -> Void) { ws.request("starships") { response in diff --git a/SampleApp/DipSampleApp/ViewControllers/PersonListViewController.swift b/SampleApp/DipSampleApp/ViewControllers/PersonListViewController.swift index f8062e0..38a05d0 100644 --- a/SampleApp/DipSampleApp/ViewControllers/PersonListViewController.swift +++ b/SampleApp/DipSampleApp/ViewControllers/PersonListViewController.swift @@ -13,11 +13,11 @@ class PersonListViewController: UITableViewController, FetchableTrait { var batchRequestID = 0 func fetchIDs(completion: [Int] -> Void) { - let provider = providerDependencies.resolve() as PersonProviderAPI + let provider = try! providerDependencies.resolve() as PersonProviderAPI return provider.fetchIDs(completion) } func fetchOne(personID: Int, completion: Person? -> Void) { - let provider = providerDependencies.resolve(tag: .Int(personID)) as PersonProviderAPI + let provider = try! providerDependencies.resolve(tag: .Int(personID)) as PersonProviderAPI return provider.fetch(personID, completion: completion) } diff --git a/SampleApp/DipSampleApp/ViewControllers/StarshipListViewController.swift b/SampleApp/DipSampleApp/ViewControllers/StarshipListViewController.swift index 1677aef..e9961b4 100644 --- a/SampleApp/DipSampleApp/ViewControllers/StarshipListViewController.swift +++ b/SampleApp/DipSampleApp/ViewControllers/StarshipListViewController.swift @@ -14,7 +14,7 @@ class StarshipListViewController : UITableViewController, FetchableTrait { var batchRequestID = 0 private func provider(tag:Int?) -> StarshipProviderAPI { - return providerDependencies.resolve(tag: tag.flatMap { .Int($0) }) + return try! providerDependencies.resolve(tag: tag.flatMap { .Int($0) }) } func fetchIDs(completion: [Int] -> Void) {