diff --git a/.travis.yml b/.travis.yml index 5192d0c..d3f9963 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,25 @@ matrix: - tar xzf $SWIFT_VERSION-ubuntu14.04.tar.gz - export PATH="${PWD}/${SWIFT_VERSION}-ubuntu14.04/usr/bin:${PATH}" - cd Dip + - script: + - swift build --clean && swift build + os: linux + dist: trusty + sudo: required + language: generic + before_install: + - wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import - + - cd .. + - export SWIFT_VERSION=swift-DEVELOPMENT-SNAPSHOT-2016-09-07-a + - wget https://swift.org/builds/development/ubuntu1404/$SWIFT_VERSION/$SWIFT_VERSION-ubuntu14.04.tar.gz + - tar xzf $SWIFT_VERSION-ubuntu14.04.tar.gz + - export PATH="${PWD}/${SWIFT_VERSION}-ubuntu14.04/usr/bin:${PATH}" + - export SWIFT_RELEASE_VERSION=2.2.1 + - export SWIFT_RELEASE_NAME="${SWIFT_RELEASE_VERSION}-RELEASE" + - wget https://swift.org/builds/swift-$SWIFT_RELEASE_VERSION-release/ubuntu1404/swift-$SWIFT_RELEASE_NAME/swift-$SWIFT_RELEASE_NAME-ubuntu14.04.tar.gz + - tar xzf swift-$SWIFT_RELEASE_NAME-ubuntu14.04.tar.gz + - export SWIFT_EXEC="${PWD}/swift-${SWIFT_RELEASE_NAME}-ubuntu14.04/usr/bin/swiftc" + - cd Dip notifications: email: false diff --git a/Dip/Dip.xcodeproj/project.pbxproj b/Dip/Dip.xcodeproj/project.pbxproj index a8eb6fe..f221e06 100644 --- a/Dip/Dip.xcodeproj/project.pbxproj +++ b/Dip/Dip.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 0919F4D61C16417B00DC3B10 /* RuntimeArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919F4CC1C16417000DC3B10 /* RuntimeArguments.swift */; }; 095F829C1D043B41008CD706 /* TypeForwarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095F829B1D043B41008CD706 /* TypeForwarding.swift */; }; 0982AF0C1C5183A000B62463 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982AF0B1C5183A000B62463 /* Utils.swift */; }; + 09871B411DAA6BF300B40B91 /* Compatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09871B401DAA6BF300B40B91 /* Compatibility.swift */; }; 09873F561C1E0237000C02F6 /* AutoInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09873F551C1E0237000C02F6 /* AutoInjection.swift */; }; 09B036001C5D2B83001EA5B7 /* AutoWiring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B035FF1C5D2B83001EA5B7 /* AutoWiring.swift */; }; 09BD350E1D84E30D00B33E53 /* AutoInjectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BD35041D84E30D00B33E53 /* AutoInjectionTests.swift */; }; @@ -26,6 +27,17 @@ 09BD35151D84E30D00B33E53 /* ThreadSafetyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BD350B1D84E30D00B33E53 /* ThreadSafetyTests.swift */; }; 09BD35161D84E30D00B33E53 /* TypeForwardingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BD350C1D84E30D00B33E53 /* TypeForwardingTests.swift */; }; 09BD35171D84E30D00B33E53 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BD350D1D84E30D00B33E53 /* Utils.swift */; }; + 09FC48061DAA9AC700566AA8 /* Resolve_swift2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC48041DAA9AC700566AA8 /* Resolve_swift2.swift */; }; + 09FC48071DAA9AC700566AA8 /* Resolve.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC48051DAA9AC700566AA8 /* Resolve.swift */; }; + 09FC480F1DAA9CAF00566AA8 /* Register.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC480C1DAA9CAF00566AA8 /* Register.swift */; }; + 09FC48101DAA9CAF00566AA8 /* Register_swift2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC480D1DAA9CAF00566AA8 /* Register_swift2.swift */; }; + 09FC48141DAA9E0200566AA8 /* AutoInjection_swift2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC48121DAA9E0200566AA8 /* AutoInjection_swift2.swift */; }; + 09FC48181DAAA53100566AA8 /* DipError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC48161DAAA53100566AA8 /* DipError.swift */; }; + 09FC48191DAAA53100566AA8 /* DipError_swift2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC48171DAAA53100566AA8 /* DipError_swift2.swift */; }; + 09FC481B1DAAA82800566AA8 /* RuntimeArguments_swift2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC481A1DAAA82800566AA8 /* RuntimeArguments_swift2.swift */; }; + 09FC481E1DAAA8F900566AA8 /* ComponentScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC481C1DAAA8F900566AA8 /* ComponentScope.swift */; }; + 09FC481F1DAAA8F900566AA8 /* ComponentScope_swift2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC481D1DAAA8F900566AA8 /* ComponentScope_swift2.swift */; }; + 09FC48211DAAAC4700566AA8 /* TypeForwarding_swift2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC48201DAAAC4700566AA8 /* TypeForwarding_swift2.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -49,6 +61,7 @@ 0919F4D11C16417000DC3B10 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 095F829B1D043B41008CD706 /* TypeForwarding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TypeForwarding.swift; path = ../../Sources/TypeForwarding.swift; sourceTree = ""; }; 0982AF0B1C5183A000B62463 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Utils.swift; path = ../../Sources/Utils.swift; sourceTree = ""; }; + 09871B401DAA6BF300B40B91 /* Compatibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Compatibility.swift; path = ../../Sources/Compatibility.swift; sourceTree = ""; }; 09873F551C1E0237000C02F6 /* AutoInjection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutoInjection.swift; path = ../../Sources/AutoInjection.swift; sourceTree = ""; }; 09B035FF1C5D2B83001EA5B7 /* AutoWiring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutoWiring.swift; path = ../../Sources/AutoWiring.swift; sourceTree = ""; }; 09BD35041D84E30D00B33E53 /* AutoInjectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutoInjectionTests.swift; path = ../../Tests/DipTests/AutoInjectionTests.swift; sourceTree = ""; }; @@ -61,6 +74,17 @@ 09BD350B1D84E30D00B33E53 /* ThreadSafetyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ThreadSafetyTests.swift; path = ../../Tests/DipTests/ThreadSafetyTests.swift; sourceTree = ""; }; 09BD350C1D84E30D00B33E53 /* TypeForwardingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TypeForwardingTests.swift; path = ../../Tests/DipTests/TypeForwardingTests.swift; sourceTree = ""; }; 09BD350D1D84E30D00B33E53 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Utils.swift; path = ../../Tests/DipTests/Utils.swift; sourceTree = ""; }; + 09FC48041DAA9AC700566AA8 /* Resolve_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Resolve_swift2.swift; path = ../../Sources/Resolve_swift2.swift; sourceTree = ""; }; + 09FC48051DAA9AC700566AA8 /* Resolve.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Resolve.swift; path = ../../Sources/Resolve.swift; sourceTree = ""; }; + 09FC480C1DAA9CAF00566AA8 /* Register.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Register.swift; path = ../../Sources/Register.swift; sourceTree = ""; }; + 09FC480D1DAA9CAF00566AA8 /* Register_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Register_swift2.swift; path = ../../Sources/Register_swift2.swift; sourceTree = ""; }; + 09FC48121DAA9E0200566AA8 /* AutoInjection_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutoInjection_swift2.swift; path = ../../Sources/AutoInjection_swift2.swift; sourceTree = ""; }; + 09FC48161DAAA53100566AA8 /* DipError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DipError.swift; path = ../../Sources/DipError.swift; sourceTree = ""; }; + 09FC48171DAAA53100566AA8 /* DipError_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DipError_swift2.swift; path = ../../Sources/DipError_swift2.swift; sourceTree = ""; }; + 09FC481A1DAAA82800566AA8 /* RuntimeArguments_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RuntimeArguments_swift2.swift; path = ../../Sources/RuntimeArguments_swift2.swift; sourceTree = ""; }; + 09FC481C1DAAA8F900566AA8 /* ComponentScope.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ComponentScope.swift; path = ../../Sources/ComponentScope.swift; sourceTree = ""; }; + 09FC481D1DAAA8F900566AA8 /* ComponentScope_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ComponentScope_swift2.swift; path = ../../Sources/ComponentScope_swift2.swift; sourceTree = ""; }; + 09FC48201DAAAC4700566AA8 /* TypeForwarding_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TypeForwarding_swift2.swift; path = ../../Sources/TypeForwarding_swift2.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -87,12 +111,24 @@ children = ( 0919F4C91C16417000DC3B10 /* Dip.h */, 0919F4CA1C16417000DC3B10 /* Dip.swift */, + 09FC48161DAAA53100566AA8 /* DipError.swift */, + 09FC48171DAAA53100566AA8 /* DipError_swift2.swift */, + 09FC480C1DAA9CAF00566AA8 /* Register.swift */, + 09FC480D1DAA9CAF00566AA8 /* Register_swift2.swift */, + 09FC48051DAA9AC700566AA8 /* Resolve.swift */, + 09FC48041DAA9AC700566AA8 /* Resolve_swift2.swift */, 0919F4C81C16417000DC3B10 /* Definition.swift */, + 09FC481C1DAAA8F900566AA8 /* ComponentScope.swift */, + 09FC481D1DAAA8F900566AA8 /* ComponentScope_swift2.swift */, 0919F4CC1C16417000DC3B10 /* RuntimeArguments.swift */, + 09FC481A1DAAA82800566AA8 /* RuntimeArguments_swift2.swift */, 09873F551C1E0237000C02F6 /* AutoInjection.swift */, + 09FC48121DAA9E0200566AA8 /* AutoInjection_swift2.swift */, 09B035FF1C5D2B83001EA5B7 /* AutoWiring.swift */, 095F829B1D043B41008CD706 /* TypeForwarding.swift */, + 09FC48201DAAAC4700566AA8 /* TypeForwarding_swift2.swift */, 0982AF0B1C5183A000B62463 /* Utils.swift */, + 09871B401DAA6BF300B40B91 /* Compatibility.swift */, 0919F4CB1C16417000DC3B10 /* Info.plist */, ); path = Dip; @@ -246,13 +282,25 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 09871B411DAA6BF300B40B91 /* Compatibility.swift in Sources */, 0982AF0C1C5183A000B62463 /* Utils.swift in Sources */, + 09FC481F1DAAA8F900566AA8 /* ComponentScope_swift2.swift in Sources */, 0919F4D51C16417B00DC3B10 /* Definition.swift in Sources */, + 09FC48061DAA9AC700566AA8 /* Resolve_swift2.swift in Sources */, + 09FC48101DAA9CAF00566AA8 /* Register_swift2.swift in Sources */, + 09FC48191DAAA53100566AA8 /* DipError_swift2.swift in Sources */, + 09FC481E1DAAA8F900566AA8 /* ComponentScope.swift in Sources */, + 09FC48211DAAAC4700566AA8 /* TypeForwarding_swift2.swift in Sources */, 09B036001C5D2B83001EA5B7 /* AutoWiring.swift in Sources */, 0919F4D41C16417B00DC3B10 /* Dip.swift in Sources */, + 09FC481B1DAAA82800566AA8 /* RuntimeArguments_swift2.swift in Sources */, + 09FC480F1DAA9CAF00566AA8 /* Register.swift in Sources */, + 09FC48071DAA9AC700566AA8 /* Resolve.swift in Sources */, 095F829C1D043B41008CD706 /* TypeForwarding.swift in Sources */, 09873F561C1E0237000C02F6 /* AutoInjection.swift in Sources */, + 09FC48181DAAA53100566AA8 /* DipError.swift in Sources */, 0919F4D61C16417B00DC3B10 /* RuntimeArguments.swift in Sources */, + 09FC48141DAA9E0200566AA8 /* AutoInjection_swift2.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Sources/AutoInjection.swift b/Sources/AutoInjection.swift index 5a09f87..c13c0a5 100644 --- a/Sources/AutoInjection.swift +++ b/Sources/AutoInjection.swift @@ -27,29 +27,36 @@ extension DependencyContainer { /** Resolves properties of passed object wrapped with `Injected` or `InjectedWeak` */ - func autoInjectProperties(_ instance: Any) throws { + func autoInjectProperties(in instance: Any) throws { let mirror = Mirror(reflecting: instance) //mirror only contains class own properties //so we need to walk through super class mirrors //to resolve super class auto-injected properties - var superClassMirror = mirror.superclassMirror + var superClassMirror = mirror._superclassMirror while superClassMirror != nil { try superClassMirror?.children.forEach(resolveChild) - superClassMirror = superClassMirror?.superclassMirror + superClassMirror = superClassMirror?._superclassMirror } try mirror.children.forEach(resolveChild) } private func resolveChild(child: Mirror.Child) throws { - //HOTFIX for https://bugs.swift.org/browse/SR-2282 - guard !String(describing: type(of: child.value)).hasPrefix("ImplicitlyUnwrappedOptional") else { return } + #if swift(>=3.0) + //HOTFIX for https://bugs.swift.org/browse/SR-2282 + guard !String(describing: type(of: child.value)).has(prefix: "ImplicitlyUnwrappedOptional") else { return } + #endif guard let injectedPropertyBox = child.value as? AutoInjectedPropertyBox else { return } - let contextKey = DefinitionKey(type: type(of: injectedPropertyBox).wrappedType, typeOfArguments: Void.self, tag: context.tag) - try inContext(contextKey, injectedInType: context?.resolvingType, injectedInProperty: child.label, logErrors: false) { - try injectedPropertyBox.resolve(self) + #if swift(>=3.0) + let wrappedType = type(of: injectedPropertyBox).wrappedType + #else + let wrappedType = injectedPropertyBox.dynamicType.wrappedType + #endif + let contextKey = DefinitionKey(type: wrappedType, typeOfArguments: Void.self, tag: context.tag) + try inContext(key:contextKey, injectedInType: context?.resolvingType, injectedInProperty: child.label, logErrors: false) { + try injectedPropertyBox.resolve(self) } } @@ -81,6 +88,7 @@ public protocol AutoInjectedPropertyBox: class { ///The type of wrapped property. static var wrappedType: Any.Type { get } + #if swift(>=3.0) /** This method will be called by `DependencyContainer` during processing resolved instance properties. In this method you should resolve an instance for wrapped property and store a reference to it. @@ -90,6 +98,17 @@ public protocol AutoInjectedPropertyBox: class { - note: This method is not intended to be called manually, `DependencyContainer` will call it by itself. */ func resolve(_ container: DependencyContainer) throws + #else + /** + This method will be called by `DependencyContainer` during processing resolved instance properties. + In this method you should resolve an instance for wrapped property and store a reference to it. + + - parameter container: A container to be used to resolve an instance + + - note: This method is not intended to be called manually, `DependencyContainer` will call it by itself. + */ + func resolve(container: DependencyContainer) throws + #endif } /** @@ -117,41 +136,42 @@ public final class Injected: _InjectedPropertyBox, AutoInjectedPropertyBox } ///Wrapped value. - public private(set) var value: T? { + public var value: T? { didSet { if let value = value { didInject(value) } } } + #if swift(>=3.0) /** Creates a new wrapper for auto-injected property. - parameters: - - required: Defines if the property is required or not. - If container fails to inject required property it will als fail to resolve + - required: Defines if the property is required or not. + If container fails to inject required property it will als fail to resolve the instance that defines that property. Default is `true`. - tag: An optional tag to use to lookup definitions when injecting this property. Default is `nil`. - - didInject: block that will be called when concrete instance is injected in this property. + - didInject: Block that will be called when concrete instance is injected in this property. Similar to `didSet` property observer. Default value does nothing. - */ + */ public convenience init(required: Bool = true, didInject: @escaping (T) -> () = { _ in }) { self.init(value: nil, required: required, tag: nil, overrideTag: false, didInject: didInject) } - + public convenience init(required: Bool = true, tag: DependencyTagConvertible?, didInject: @escaping (T) -> () = { _ in }) { self.init(value: nil, required: required, tag: tag, overrideTag: true, didInject: didInject) } - private init(value: T?, required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> ()) { + init(value: T?, required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> ()) { self.value = value super.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject) } - + public func resolve(_ container: DependencyContainer) throws { - let resolved: T? = try super.resolve(container) + let resolved: T? = try super.resolve(with: container) value = resolved } - + /// Returns a new wrapper with provided value. public func setValue(_ value: T?) -> Injected { guard (required && value != nil) || !required else { @@ -160,7 +180,14 @@ public final class Injected: _InjectedPropertyBox, AutoInjectedPropertyBox return Injected(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject) } - + + #else + init(value: T?, required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: (T) -> ()) { + self.value = value + super.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject) + } + #endif + } /** @@ -204,7 +231,7 @@ public final class InjectedWeak: _InjectedPropertyBox, AutoInjectedPropert return T.self } - private var valueBox: WeakBox? = nil { + var valueBox: WeakBox? = nil { didSet { if let value = value { didInject(value) } } @@ -215,6 +242,11 @@ public final class InjectedWeak: _InjectedPropertyBox, AutoInjectedPropert return valueBox?.value } + #if swift(>=3.0) + init(value: T?, required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> ()) { + self.valueBox = value.map(WeakBox.init) + super.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject) + } /** Creates a new wrapper for weak auto-injected property. @@ -223,74 +255,84 @@ public final class InjectedWeak: _InjectedPropertyBox, AutoInjectedPropert If container fails to inject required property it will als fail to resolve the instance that defines that property. Default is `true`. - tag: An optional tag to use to lookup definitions when injecting this property. Default is `nil`. - - didInject: block that will be called when concrete instance is injected in this property. + - didInject: Block that will be called when concrete instance is injected in this property. Similar to `didSet` property observer. Default value does nothing. */ public convenience init(required: Bool = true, didInject: @escaping (T) -> () = { _ in }) { self.init(value: nil, required: required, tag: nil, overrideTag: false, didInject: didInject) } - + public convenience init(required: Bool = true, tag: DependencyTagConvertible?, didInject: @escaping (T) -> () = { _ in }) { self.init(value: nil, required: required, tag: tag, overrideTag: true, didInject: didInject) } - - private init(value: T?, required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> ()) { - self.valueBox = value.map(WeakBox.init) - super.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject) - } - + public func resolve(_ container: DependencyContainer) throws { - let resolved: T? = try super.resolve(container) + let resolved: T? = try super.resolve(with: container) valueBox = resolved.map(WeakBox.init) } - + /// Returns a new wrapper with provided value. public func setValue(_ value: T?) -> InjectedWeak { guard (required && value != nil) || !required else { fatalError("Can not set required property to nil.") } - + return InjectedWeak(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject) } + #else + init(value: T?, required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: (T) -> ()) { + self.valueBox = value.map(WeakBox.init) + super.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject) + } + #endif + } -private class _InjectedPropertyBox { +class _InjectedPropertyBox { let required: Bool let didInject: (T) -> () let tag: DependencyContainer.Tag? let overrideTag: Bool + #if swift(>=3.0) init(required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> () = { _ in }) { self.required = required self.tag = tag?.dependencyTag self.overrideTag = overrideTag self.didInject = didInject } + #else + init(required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: (T) -> () = { _ in }) { + self.required = required + self.tag = tag?.dependencyTag + self.overrideTag = overrideTag + self.didInject = didInject + } + #endif - fileprivate func resolve(_ container: DependencyContainer) throws -> T? { + func resolve(with container: DependencyContainer) throws -> T? { let tag = overrideTag ? self.tag : container.context.tag do { - container.context.key = container.context.key.tagged(tag) + container.context.key = container.context.key.tagged(with: tag) let key = DefinitionKey(type: T.self, typeOfArguments: Void.self, tag: tag?.dependencyTag) - return try resolve(container, key: key, builder: { factory in try factory() }) as? T + return try resolve(with: container, key: key, builder: { factory in try factory() }) as? T } catch { - let error = DipError.autoInjectionFailed(label: container.context.injectedInProperty, type: container.context.resolvingType, underlyingError: error) - + let error = DipError.autoInjectionFailed(label: container.context.injectedInProperty, type: container.context.resolvingType, underlyingError: error) if required { throw error } else { - log(.Errors, error) + log(level: .Errors, error) return nil } } } - private func resolve(_ container: DependencyContainer, key: DefinitionKey, builder: ((U) throws -> Any) throws -> Any) throws -> Any { - return try container.resolve(key: key, builder: { definition throws -> Any in + private func resolve(with container: DependencyContainer, key: DefinitionKey, builder: ((U) throws -> Any) throws -> Any) throws -> Any { + return try container._resolve(key: key, builder: { definition throws -> Any in try builder(definition.weakFactory) }) } diff --git a/Sources/AutoInjection_swift2.swift b/Sources/AutoInjection_swift2.swift new file mode 100644 index 0000000..a53e009 --- /dev/null +++ b/Sources/AutoInjection_swift2.swift @@ -0,0 +1,98 @@ +// +// 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. +// + +#if !swift(>=3.0) + extension Injected { + + /** + Creates a new wrapper for auto-injected property. + + - parameters: + - required: Defines if the property is required or not. + If container fails to inject required property it will als fail to resolve + the instance that defines that property. Default is `true`. + - tag: An optional tag to use to lookup definitions when injecting this property. Default is `nil`. + - didInject: block that will be called when concrete instance is injected in this property. + Similar to `didSet` property observer. Default value does nothing. + */ + public convenience init(required: Bool = true, didInject: (T) -> () = { _ in }) { + self.init(value: nil, required: required, tag: nil, overrideTag: false, didInject: didInject) + } + + public convenience init(required: Bool = true, tag: DependencyTagConvertible?, didInject: (T) -> () = { _ in }) { + self.init(value: nil, required: required, tag: tag, overrideTag: true, didInject: didInject) + } + + + public func resolve(container: DependencyContainer) throws { + let resolved: T? = try super.resolve(with: container) + value = resolved + } + + /// Returns a new wrapper with provided value. + public func setValue(value: T?) -> Injected { + guard (required && value != nil) || !required else { + fatalError("Can not set required property to nil.") + } + return Injected(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject) + } + + } + + extension InjectedWeak { + + /** + Creates a new wrapper for weak auto-injected property. + + - parameters: + - required: Defines if the property is required or not. + If container fails to inject required property it will als fail to resolve + the instance that defines that property. Default is `true`. + - tag: An optional tag to use to lookup definitions when injecting this property. Default is `nil`. + - didInject: block that will be called when concrete instance is injected in this property. + Similar to `didSet` property observer. Default value does nothing. + */ + public convenience init(required: Bool = true, didInject: (T) -> () = { _ in }) { + self.init(value: nil, required: required, tag: nil, overrideTag: false, didInject: didInject) + } + + public convenience init(required: Bool = true, tag: DependencyTagConvertible?, didInject: (T) -> () = { _ in }) { + self.init(value: nil, required: required, tag: tag, overrideTag: true, didInject: didInject) + } + + public func resolve(container: DependencyContainer) throws { + let resolved: T? = try super.resolve(with: container) + valueBox = resolved.map(WeakBox.init) + } + + /// Returns a new wrapper with provided value. + public func setValue(value: T?) -> InjectedWeak { + guard (required && value != nil) || !required else { + fatalError("Can not set required property to nil.") + } + return InjectedWeak(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject) + } + + } +#endif diff --git a/Sources/AutoWiring.swift b/Sources/AutoWiring.swift index a4c9874..a4cea35 100644 --- a/Sources/AutoWiring.swift +++ b/Sources/AutoWiring.swift @@ -30,7 +30,8 @@ protocol AutoWiringDefinition: DefinitionType { extension DependencyContainer { /// Tries to resolve instance using auto-wiring - func autowire(_ key: DefinitionKey) throws -> T { + func autowire(key aKey: DefinitionKey) throws -> T { + let key = aKey guard key.typeOfArguments == Void.self else { throw DipError.definitionNotFound(key: key) } @@ -38,8 +39,8 @@ extension DependencyContainer { let autoWiringKey = try autoWiringDefinition(byKey: key).key do { - let key = autoWiringKey.tagged(key.tag ?? context.tag) - return try resolve(key: key) { definition in + let key = autoWiringKey.tagged(with: key.tag ?? context.tag) + return try _resolve(key: key) { definition in try definition.autoWiringFactory!(self, key.tag) as! T } } @@ -51,7 +52,7 @@ extension DependencyContainer { private func autoWiringDefinition(byKey key: DefinitionKey) throws -> KeyDefinitionPair { var definitions = self.definitions.map({ (key: $0.0, definition: $0.1) }) - definitions = filter(definitions, byKey: key) + definitions = filter(definitions: definitions, byKey: key) definitions = definitions.sorted(by: { $0.definition.numberOfArguments > $1.definition.numberOfArguments }) guard definitions.count > 0 && definitions[0].definition.numberOfArguments > 0 else { @@ -60,7 +61,7 @@ extension DependencyContainer { let maximumNumberOfArguments = definitions.first?.definition.numberOfArguments definitions = definitions.filter({ $0.definition.numberOfArguments == maximumNumberOfArguments }) - definitions = order(definitions, byTag: key.tag) + definitions = order(definitions: definitions, byTag: key.tag) //when there are several definitions with the same number of arguments but different arguments types if definitions.count > 1 && definitions[0].key.typeOfArguments != definitions[1].key.typeOfArguments { diff --git a/Sources/Compatibility.swift b/Sources/Compatibility.swift new file mode 100644 index 0000000..3cb6d5d --- /dev/null +++ b/Sources/Compatibility.swift @@ -0,0 +1,75 @@ +#if swift(>=3.0) + extension Mirror { + var _superclassMirror: Mirror? { + return superclassMirror + } + } +#else + public typealias Error = ErrorType + public typealias ExpressibleByIntegerLiteral = IntegerLiteralConvertible + public typealias ExpressibleByStringLiteral = StringLiteralConvertible + + extension CollectionType { + func sorted(@noescape by isOrderedBefore: (Self.Generator.Element, Self.Generator.Element) -> Bool) -> [Self.Generator.Element] { + return sort(isOrderedBefore) + } + + func contains(@noescape where predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Bool { + return try contains(predicate) + } + } + + extension CollectionType where Index : RandomAccessIndexType { + @warn_unused_result + public func reversed() -> ReverseRandomAccessCollection { + return reverse() + } + } + + extension SequenceType where Generator.Element == String { + @warn_unused_result + public func joined(separator aSeparator: String) -> String { + return joinWithSeparator(aSeparator) + } + } + + extension Array { + public mutating func append(contentsOf newElements: C) { + appendContentsOf(newElements) + } + public mutating func append(contentsOf newElements: S) { + appendContentsOf(newElements) + } + } + + extension Mirror { + var _superclassMirror: Mirror? { + return superclassMirror() + } + } + + extension String { + init(describing thing: Any) { + self.init(thing) + } + } + +#endif + +#if _runtime(_ObjC) + extension String { + public func has(prefix aPrefix: String) -> Bool { + return hasPrefix(aPrefix) + } + } + +#else + + extension String { + public func has(prefix aPrefix: String) -> Bool { + return aPrefix == + String(self.characters.prefix(aPrefix.characters.count)) + } + + } +#endif diff --git a/Sources/ComponentScope.swift b/Sources/ComponentScope.swift new file mode 100644 index 0000000..5d0f880 --- /dev/null +++ b/Sources/ComponentScope.swift @@ -0,0 +1,124 @@ +// +// 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. +// + +#if swift(>=3.0) + + ///Component scope defines a strategy used by the `DependencyContainer` to manage resolved instances life cycle. + public enum ComponentScope { + + /** + A new instance will be created every time it's resolved. + This is a default strategy. Use this strategy when you don't want instances to be shared + between different consumers (i.e. if it is not thread safe). + + **Example**: + + ``` + container.register { ServiceImp() as Service } + container.register { + ServiceConsumerImp( + service1: try container.resolve() as Service + service2: try container.resolve() as Service + ) as ServiceConsumer + } + let consumer = container.resolve() as ServiceConsumer + consumer.service1 !== consumer.service2 //true + + ``` + */ + case unique + + /** + Instance resolved with the same definition will be reused until topmost `resolve(tag:)` method returns. + When you resolve the same object graph again the container will create new instances. + Use this strategy if you want different object in objects graph to share the same instance. + + - warning: Make sure this component is thread safe or accessed always from the same thread. + + **Example**: + + ``` + container.register { ServiceImp() as Service } + container.register { + ServiceConsumerImp( + service1: try container.resolve() as Service + service2: try container.resolve() as Service + ) as ServiceConsumer + } + let consumer1 = container.resolve() as ServiceConsumer + let consumer2 = container.resolve() as ServiceConsumer + consumer1.service1 === consumer1.service2 //true + consumer2.service1 === consumer2.service2 //true + consumer1.service1 !== consumer2.service1 //true + ``` + */ + case shared + + /** + Resolved instance will be retained by the container and always reused. + Do not mix this life cycle with _singleton pattern_. + Instance will be not shared between different containers unless they collaborate. + + - warning: Make sure this component is thread safe or accessed always from the same thread. + + - note: When you override or remove definition from the container an instance + that was resolved with this definition will be released. When you reset + the container it will release all singleton instances. + + **Example**: + + ``` + container.register(.singleton) { ServiceImp() as Service } + container.register { + ServiceConsumerImp( + service1: try container.resolve() as Service + service2: try container.resolve() as Service + ) as ServiceConsumer + } + let consumer1 = container.resolve() as ServiceConsumer + let consumer2 = container.resolve() as ServiceConsumer + consumer1.service1 === consumer1.service2 //true + consumer2.service1 === consumer2.service2 //true + consumer1.service1 === consumer2.service1 //true + ``` + */ + case singleton + + /** + The same scope as a `Singleton`, but instance will be created when container is bootstrapped. + + - seealso: `bootstrap()` + */ + case eagerSingleton + + /** + The same scope as a `Singleton`, but container stores week reference to the resolved instance. + While a strong reference to the resolved instance exists resolve will return the same instance. + After the resolved instance is deallocated next resolve will produce a new instance. + */ + case weakSingleton + + } + +#endif diff --git a/Sources/ComponentScope_swift2.swift b/Sources/ComponentScope_swift2.swift new file mode 100644 index 0000000..1321c97 --- /dev/null +++ b/Sources/ComponentScope_swift2.swift @@ -0,0 +1,129 @@ +// +// 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. +// + +#if !swift(>=3.0) + + ///Component scope defines a strategy used by the `DependencyContainer` to manage resolved instances life cycle. + public enum ComponentScope { + + /** + A new instance will be created every time it's resolved. + This is a default strategy. Use this strategy when you don't want instances to be shared + between different consumers (i.e. if it is not thread safe). + + **Example**: + + ``` + container.register { ServiceImp() as Service } + container.register { + ServiceConsumerImp( + service1: try container.resolve() as Service + service2: try container.resolve() as Service + ) as ServiceConsumer + } + let consumer = container.resolve() as ServiceConsumer + consumer.service1 !== consumer.service2 //true + + ``` + */ + case Unique + + /** + Instance resolved with the same definition will be reused until topmost `resolve(tag:)` method returns. + When you resolve the same object graph again the container will create new instances. + Use this strategy if you want different object in objects graph to share the same instance. + + - warning: Make sure this component is thread safe or accessed always from the same thread. + + **Example**: + + ``` + container.register { ServiceImp() as Service } + container.register { + ServiceConsumerImp( + service1: try container.resolve() as Service + service2: try container.resolve() as Service + ) as ServiceConsumer + } + let consumer1 = container.resolve() as ServiceConsumer + let consumer2 = container.resolve() as ServiceConsumer + consumer1.service1 === consumer1.service2 //true + consumer2.service1 === consumer2.service2 //true + consumer1.service1 !== consumer2.service1 //true + ``` + */ + case Shared + + /** + Resolved instance will be retained by the container and always reused. + Do not mix this life cycle with _singleton pattern_. + Instance will be not shared between different containers unless they collaborate. + + - warning: Make sure this component is thread safe or accessed always from the same thread. + + - note: When you override or remove definition from the container an instance + that was resolved with this definition will be released. When you reset + the container it will release all singleton instances. + + **Example**: + + ``` + container.register(.singleton) { ServiceImp() as Service } + container.register { + ServiceConsumerImp( + service1: try container.resolve() as Service + service2: try container.resolve() as Service + ) as ServiceConsumer + } + let consumer1 = container.resolve() as ServiceConsumer + let consumer2 = container.resolve() as ServiceConsumer + consumer1.service1 === consumer1.service2 //true + consumer2.service1 === consumer2.service2 //true + consumer1.service1 === consumer2.service1 //true + ``` + */ + case Singleton + + /** + The same scope as a `Singleton`, but instance will be created when container is bootstrapped. + + - seealso: `bootstrap()` + */ + case EagerSingleton + + /** + The same scope as a `Singleton`, but container stores week reference to the resolved instance. + While a strong reference to the resolved instance exists resolve will return the same instance. + After the resolved instance is deallocated next resolve will produce a new instance. + */ + case WeakSingleton + + static var unique: ComponentScope { return .Unique } + static var shared: ComponentScope { return .Shared } + static var singleton: ComponentScope { return .Singleton } + static var weakSingleton: ComponentScope { return .WeakSingleton } + static var eagerSingleton: ComponentScope { return .EagerSingleton } + } + +#endif diff --git a/Sources/Definition.swift b/Sources/Definition.swift index 77677e0..cd8ca96 100644 --- a/Sources/Definition.swift +++ b/Sources/Definition.swift @@ -42,7 +42,7 @@ public struct DefinitionKey : Hashable, CustomStringConvertible { return "type: \(type), arguments: \(typeOfArguments), tag: \(tag.desc)" } - func tagged(_ tag: DependencyContainer.Tag?) -> DefinitionKey { + func tagged(with tag: DependencyContainer.Tag?) -> DefinitionKey { var tagged = self tagged.tag = tag return tagged @@ -58,101 +58,6 @@ public func ==(lhs: DefinitionKey, rhs: DefinitionKey) -> Bool { lhs.tag == rhs.tag } -///Component scope defines a strategy used by the `DependencyContainer` to manage resolved instances life cycle. -public enum ComponentScope { - /** - A new instance will be created every time it's resolved. - This is a default strategy. Use this strategy when you don't want instances to be shared - between different consumers (i.e. if it is not thread safe). - - **Example**: - - ``` - container.register { ServiceImp() as Service } - container.register { - ServiceConsumerImp( - service1: try container.resolve() as Service - service2: try container.resolve() as Service - ) as ServiceConsumer - } - let consumer = container.resolve() as ServiceConsumer - consumer.service1 !== consumer.service2 //true - - ``` - */ - case unique - - /** - Instance resolved with the same definition will be reused until topmost `resolve(tag:)` method returns. - When you resolve the same object graph again the container will create new instances. - Use this strategy if you want different object in objects graph to share the same instance. - - - warning: Make sure this component is thread safe or accessed always from the same thread. - - **Example**: - - ``` - container.register { ServiceImp() as Service } - container.register { - ServiceConsumerImp( - service1: try container.resolve() as Service - service2: try container.resolve() as Service - ) as ServiceConsumer - } - let consumer1 = container.resolve() as ServiceConsumer - let consumer2 = container.resolve() as ServiceConsumer - consumer1.service1 === consumer1.service2 //true - consumer2.service1 === consumer2.service2 //true - consumer1.service1 !== consumer2.service1 //true - ``` - */ - case shared - - /** - Resolved instance will be retained by the container and always reused. - Do not mix this life cycle with _singleton pattern_. - Instance will be not shared between different containers unless they collaborate. - - - warning: Make sure this component is thread safe or accessed always from the same thread. - - - note: When you override or remove definition from the container an instance - that was resolved with this definition will be released. When you reset - the container it will release all singleton instances. - - **Example**: - - ``` - container.register(.singleton) { ServiceImp() as Service } - container.register { - ServiceConsumerImp( - service1: try container.resolve() as Service - service2: try container.resolve() as Service - ) as ServiceConsumer - } - let consumer1 = container.resolve() as ServiceConsumer - let consumer2 = container.resolve() as ServiceConsumer - consumer1.service1 === consumer1.service2 //true - consumer2.service1 === consumer2.service2 //true - consumer1.service1 === consumer2.service1 //true - ``` - */ - case singleton - - /** - The same scope as a `Singleton`, but instance will be created when container is bootstrapped. - - - seealso: `bootstrap()` - */ - case eagerSingleton - - /** - The same scope as a `Singleton`, but container stores week reference to the resolved instance. - While a strong reference to the resolved instance exists resolve will return the same instance. - After the resolved instance is deallocated next resolve will produce a new instance. - */ - case weakSingleton -} - ///Dummy protocol to store definitions for different types in collection public protocol DefinitionType: class { } @@ -167,20 +72,28 @@ public protocol DefinitionType: class { } public final class Definition: DefinitionType { public typealias F = (U) throws -> T - init(scope: ComponentScope, factory: @escaping F) { - self.factory = factory - self.scope = scope - } - //MARK: - _Definition weak var container: DependencyContainer? let factory: F let scope: ComponentScope - fileprivate(set) var weakFactory: ((Any) throws -> Any)! - fileprivate(set) var resolveProperties: ((DependencyContainer, Any) throws -> ())? + var weakFactory: ((Any) throws -> Any)! + var resolveProperties: ((DependencyContainer, Any) throws -> ())? + #if swift(>=3.0) + init(scope: ComponentScope, factory: @escaping F) { + self.factory = factory + self.scope = scope + } + #else + init(scope: ComponentScope, factory: F) { + self.factory = factory + self.scope = scope + } + #endif + + #if swift(>=3.0) /** Set the block that will be used to resolve dependencies of the instance. This block will be called before `resolve(tag:)` returns. @@ -219,6 +132,46 @@ public final class Definition: DefinitionType { } return self } + #else + /** + Set the block that will be used to resolve dependencies of the instance. + This block will be called before `resolve(tag:)` returns. + + - parameter block: The block to resolve property dependencies of the instance. + + - returns: modified definition + + - note: To resolve circular dependencies at least one of them should use this block + to resolve its dependencies. Otherwise the application will enter an infinite loop and crash. + + - note: You can call this method several times on the same definition. + Container will call all provided blocks in the same order. + + **Example** + + ```swift + container.register { ClientImp(service: try container.resolve() as Service) as Client } + + container.register { ServiceImp() as Service } + .resolvingProperties { container, service in + service.client = try container.resolve() as Client + } + ``` + + */ + public func resolvingProperties(block: (DependencyContainer, T) throws -> ()) -> Definition { + if let oldBlock = self.resolveProperties { + self.resolveProperties = { + try oldBlock($0, $1 as! T) + try block($0, $1 as! T) + } + } + else { + self.resolveProperties = { try block($0, $1 as! T) } + } + return self + } + #endif /// Calls `resolveDependencies` block if it was set. func resolveProperties(of instance: Any, container: DependencyContainer) throws { @@ -233,40 +186,40 @@ public final class Definition: DefinitionType { //MARK: - AutoWiringDefinition - fileprivate(set) var autoWiringFactory: ((DependencyContainer, DependencyContainer.Tag?) throws -> Any)? - fileprivate(set) var numberOfArguments: Int = 0 + var autoWiringFactory: ((DependencyContainer, DependencyContainer.Tag?) throws -> Any)? + var numberOfArguments: Int = 0 //MARK: - TypeForwardingDefinition /// Types that can be resolved using this definition. - fileprivate(set) var implementingTypes: [Any.Type] = [(T?).self, (T!).self] + private(set) var implementingTypes: [Any.Type] = [(T?).self, (T!).self] /// Return `true` if type can be resolved using this definition - func doesImplements(_ type: Any.Type) -> Bool { - return implementingTypes.contains(where: { $0 == type }) + func doesImplements(type aType: Any.Type) -> Bool { + return implementingTypes.contains(where: { $0 == aType }) } //MARK: - _TypeForwardingDefinition /// Adds type as being able to be resolved using this definition - fileprivate func _implements(_ type: Any.Type) { - _implements([type]) + func _implements(type aType: Any.Type) { + _implements(types: [aType]) } /// Adds types as being able to be resolved using this definition - fileprivate func _implements(_ types: [Any.Type]) { - implementingTypes.append(contentsOf: types.filter({ !doesImplements($0) })) + func _implements(types aTypes: [Any.Type]) { + implementingTypes.append(contentsOf: aTypes.filter({ !doesImplements(type: $0) })) } /// Definition to which resolution will be forwarded to - fileprivate weak var forwardsTo: _TypeForwardingDefinition? { + weak var forwardsTo: _TypeForwardingDefinition? { didSet { //both definitions (self and forwardsTo) can resolve //each other types and each other implementing types //this relationship can be used to reuse previously resolved instances if let forwardsTo = forwardsTo { - _implements(forwardsTo.type) - _implements(forwardsTo.implementingTypes) + _implements(type: forwardsTo.type) + _implements(types: forwardsTo.implementingTypes) //definitions for types that can be resolved by `forwardsTo` definition //can also be used to resolve self type and it's implementing types @@ -274,20 +227,20 @@ public final class Definition: DefinitionType { //when there are several forwarded definitions //see testThatItReusesInstanceResolvedByTypeForwarding) for definition in forwardsTo.forwardsFrom { - definition._implements(type) - definition._implements(implementingTypes) + definition._implements(type: type) + definition._implements(types: implementingTypes) } //forwardsTo can be used to resolve self type and it's implementing types - forwardsTo._implements(type) - forwardsTo._implements(implementingTypes) + forwardsTo._implements(type: type) + forwardsTo._implements(types: implementingTypes) forwardsTo.forwardsFrom.append(self) } } } /// Definitions that will forward resolution to this definition - fileprivate var forwardsFrom: [_TypeForwardingDefinition] = [] + var forwardsFrom: [_TypeForwardingDefinition] = [] } @@ -303,11 +256,11 @@ protocol _Definition: DefinitionType, AutoWiringDefinition, TypeForwardingDefini //MARK: - Type Forwarding -private protocol _TypeForwardingDefinition: TypeForwardingDefinition, _Definition { - weak var forwardsTo: _TypeForwardingDefinition? { get set } +protocol _TypeForwardingDefinition: TypeForwardingDefinition, _Definition { + weak var forwardsTo: _TypeForwardingDefinition? { get } var forwardsFrom: [_TypeForwardingDefinition] { get set } - func _implements(_ type: Any.Type) - func _implements(_ type: [Any.Type]) + func _implements(type aType: Any.Type) + func _implements(types aTypes: [Any.Type]) } extension Definition: _TypeForwardingDefinition { @@ -366,9 +319,9 @@ private func ~=(lhs: KeyDefinitionPair, rhs: KeyDefinitionPair) -> Bool { /// Returns key-defintion pairs with definitions able to resolve that type (directly or via type forwarding) /// and which tag matches provided key's tag or is nil. /// In the end filters defintions by type of runtime arguments. -func filter(_ definitions: [KeyDefinitionPair], byKey key: DefinitionKey, byTypeOfArguments: Bool = false) -> [KeyDefinitionPair] { - let definitions = definitions - .filter({ $0.key.type == key.type || $0.definition.doesImplements(key.type) }) +func filter(definitions _definitions: [KeyDefinitionPair], byKey key: DefinitionKey, byTypeOfArguments: Bool = false) -> [KeyDefinitionPair] { + let definitions = _definitions + .filter({ $0.key.type == key.type || $0.definition.doesImplements(type: key.type) }) .filter({ $0.key.tag == key.tag || $0.key.tag == nil }) if byTypeOfArguments { return definitions.filter({ $0.key.typeOfArguments == key.typeOfArguments }) @@ -379,8 +332,8 @@ func filter(_ definitions: [KeyDefinitionPair], byKey key: DefinitionKey, byType } /// Orders key-definition pairs putting first definitions registered for provided tag. -func order(_ definitions: [KeyDefinitionPair], byTag tag: DependencyContainer.Tag?) -> [KeyDefinitionPair] { +func order(definitions _definitions: [KeyDefinitionPair], byTag tag: DependencyContainer.Tag?) -> [KeyDefinitionPair] { return - definitions.filter({ $0.key.tag == tag }) + - definitions.filter({ $0.key.tag != tag }) + _definitions.filter({ $0.key.tag == tag }) + + _definitions.filter({ $0.key.tag != tag }) } diff --git a/Sources/Dip.swift b/Sources/Dip.swift index 0429018..66c5a37 100644 --- a/Sources/Dip.swift +++ b/Sources/Dip.swift @@ -41,14 +41,14 @@ public final class DependencyContainer { internal(set) public var context: Context! var definitions = [DefinitionKey: _Definition]() - fileprivate var resolvedInstances = ResolvedInstances() + var resolvedInstances = ResolvedInstances() private let lock = RecursiveLock() - fileprivate(set) var bootstrapped = false - fileprivate var bootstrapQueue: [() throws -> ()] = [] + var bootstrapped = false + var bootstrapQueue: [() throws -> ()] = [] private var _weakCollaborators: [WeakBox] = [] - fileprivate(set) var _collaborators: [DependencyContainer] { + var _collaborators: [DependencyContainer] { get { return _weakCollaborators.flatMap({ $0.value }) } @@ -99,13 +99,19 @@ public final class DependencyContainer { } } - fileprivate func threadSafe(_ closure: () throws -> T) rethrows -> T { + #if swift(>=3.0) + func threadSafe(_ closure: () throws -> T) rethrows -> T { lock.lock() - defer { - lock.unlock() - } + defer { lock.unlock() } + return try closure() + } + #else + func threadSafe(@noescape closure: () throws -> T) rethrows -> T { + lock.lock() + defer { lock.unlock() } return try closure() } + #endif } @@ -198,7 +204,8 @@ extension DependencyContainer { /// Pushes new context created with provided values and calls block. When block returns previous context is restored. /// When popped to initial (root) context will release all references to resolved instances and call `Resolvable` callbacks. - func inContext(_ key: DefinitionKey, injectedInType: Any.Type?, injectedInProperty: String? = nil, logErrors: Bool! = nil, block: () throws -> T) rethrows -> T { + func inContext(key aKey: DefinitionKey, injectedInType: Any.Type?, injectedInProperty: String? = nil, logErrors: Bool! = nil, block: () throws -> T) rethrows -> T { + let key = aKey return try threadSafe { let currentContext = self.context @@ -213,8 +220,6 @@ extension DependencyContainer { resolvedInstances.weakSingletons[key] = WeakBox(instance) } - // We call didResolveDependencies only at this point - // because this is a point when dependencies graph is complete. for resolvedInstance in resolvedInstances.resolvableInstances.reversed() { resolvedInstance.didResolveDependencies() } @@ -233,7 +238,7 @@ extension DependencyContainer { return try block() } catch { - if context.logErrors { log(.Errors, error) } + if context.logErrors { log(level: .Errors, error) } throw error } } @@ -241,318 +246,6 @@ extension DependencyContainer { } -// MARK: - Registering definitions - -extension DependencyContainer { - - /** - Register factory for type `T` and associate it with an optional tag. - - - parameters: - - scope: The scope to use for instance created by the factory. Default value is `Shared`. - - type: Type to register definition for. Default value is return value of factory. - - tag: The arbitrary tag to associate this factory with. Pass `nil` to associate with any tag. Default value is `nil`. - - factory: The factory that produces instance of `type`. Will be used to resolve instances of `type`. - - - returns: A registered definition. - - - note: You should cast the factory return type to the protocol you want to register it for - (unless you want to register concrete type) or provide `type` parameter. - - - seealso: `Definition`, `ComponentScope`, `DependencyTagConvertible` - - **Example**: - ```swift - //Register ServiceImp as Service - container.register { ServiceImp() as Service } - - //Register ServiceImp as Service named by "service" - container.register(tag: "service") { ServiceImp() as Service } - - //Register unique ServiceImp as Service - container.register(.unique) { ServiceImp() as Service } - - //Register ClientImp as Client and resolve it's service dependency - container.register { try ClientImp(service: container.resolve() as Service) as Client } - - //Register ServiceImp as concrete type - container.register { ServiceImp() } - container.register(factory: ServiceImp.init) - - //Register ServiceImp as Service - container.register(Service.self, factory: ServiceImp.init) - - //Register ClientImp as Client - container.register(Client.self, factory: ClientImp.init(service:)) - ``` - */ - @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping () throws -> T) -> Definition { - let definition = DefinitionBuilder { - $0.scope = scope - $0.factory = factory - }.build() - register(definition, tag: tag) - return definition - } - - /** - Register generic factory and auto-wiring factory and associate it with an optional tag. - - - parameters: - - tag: The arbitrary tag to associate this factory with. Pass `nil` to associate with any tag. Default value is `nil`. - - scope: The scope to use for instance created by the factory. - - factory: The factory to register. - - numberOfArguments: The number of factory arguments. Will be used on auto-wiring to sort definitions. - - autoWiringFactory: The factory to be used on auto-wiring to resolve component. - - - returns: A registered definition. - - - note: You _should not_ call this method directly, instead call any of other `register` methods. - You _should_ use this method only to register dependency with more runtime arguments - than _Dip_ supports (currently it's up to six) like in the following example: - - ```swift - public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: Tag? = nil, factory: (A, B, C, ...) throws -> T) -> Definition { - return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: ...) { container, tag in - try factory(container.resolve(tag: tag), ...) - } - } - ``` - - Though before you do so you should probably review your design and try to reduce number of depnedencies. - */ - public func register(scope: ComponentScope, type: T.Type, tag: DependencyTagConvertible?, factory: @escaping (U) throws -> T, numberOfArguments: Int, autoWiringFactory: @escaping (DependencyContainer, Tag?) throws -> T) -> Definition { - let definition = DefinitionBuilder { - $0.scope = scope - $0.factory = factory - $0.numberOfArguments = numberOfArguments - $0.autoWiringFactory = autoWiringFactory - }.build() - register(definition, tag: tag) - return definition - } - - /** - Register definiton in the container and associate it with an optional tag. - Will override already registered definition for the same type and factory, associated with the same tag. - - - parameters: - - tag: The arbitrary tag to associate this definition with. Pass `nil` to associate with any tag. Default value is `nil`. - - definition: The definition to register in the container. - - */ - public func register(_ definition: Definition, tag: DependencyTagConvertible? = nil) { - precondition(!bootstrapped, "You can not modify container's definitions after it was bootstrapped.") - - threadSafe { - let key = DefinitionKey(type: T.self, typeOfArguments: U.self, tag: tag?.dependencyTag) - if let _ = definitions[key] { - remove(definitionForKey: key) - } - - definition.container = self - definitions[key] = definition - resolvedInstances.singletons[key] = nil - resolvedInstances.weakSingletons[key] = nil - - if case .eagerSingleton = definition.scope { - bootstrapQueue.append({ let _ = try self.resolve(tag: tag) as T }) - } - } - } - -} - -// MARK: - Resolve dependencies - -extension DependencyContainer { - - /** - Resolve an instance of type `T`. - - If no matching definition was registered with provided `tag`, - container will lookup definition associated with `nil` tag. - - - parameter tag: The arbitrary tag to use to lookup definition. - - - throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`, `DipError.InvalidType` - - - returns: An instance of type `T`. - - **Example**: - ```swift - let service = try! container.resolve() as Service - let service = try! container.resolve(tag: "service") as Service - let service: Service = try! container.resolve() - ``` - - - seealso: `register(_:type:tag:factory:)` - */ - public func resolve(tag: DependencyTagConvertible? = nil) throws -> T { - return try resolve(tag: tag) { factory in try factory() } - } - - /** - Resolve an instance of requested type. Weakly-typed alternative of `resolve(tag:)` - - - warning: This method does not make any type checks, so there is no guaranty that - resulting instance is actually an instance of requested type. - That can happen if you register forwarded type that is not implemented by resolved instance. - - - parameters: - - type: Type to resolve - - tag: The arbitrary tag to use to lookup definition. - - - throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`, `DipError.InvalidType` - - - returns: An instance of requested type. - - **Example**: - ```swift - let service = try! container.resolve(Service.self) as! Service - let service = try! container.resolve(Service.self, tag: "service") as! Service - ``` - - - seealso: `resolve(tag:)`, `register(_:type:tag:factory:)` - */ - public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil) throws -> Any { - return try resolve(type, tag: tag) { factory in try factory() } - } - - /** - Resolve an instance of type `T` using generic builder closure that accepts generic factory and returns created instance. - - - parameters: - - tag: The arbitrary tag to use to lookup definition. - - builder: Generic closure that accepts generic factory and returns inctance created by that factory. - - - throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`, `DipError.InvalidType` - - - returns: An instance of type `T`. - - - note: You _should not_ call this method directly, instead call any of other - `resolve(tag:)` or `resolve(tag:withArguments:)` methods. - You _should_ use this method only to resolve dependency with more runtime arguments than - _Dip_ supports (currently it's up to six) like in the following example: - - ```swift - public func resolve(tag: Tag? = nil, _ arg1: A, _ arg2: B, _ arg3: C, ...) throws -> T { - return try resolve(tag: tag) { factory in factory(arg1, arg2, arg3, ...) } - } - ``` - - Though before you do so you should probably review your design and try to reduce the number of dependencies. - */ - public func resolve(tag: DependencyTagConvertible? = nil, builder: ((U) throws -> T) throws -> T) throws -> T { - return try resolve(T.self, tag: tag, builder: { factory in - try builder({ try factory($0) as! T }) - }) as! T - } - - /** - Resolve an instance of provided type using builder closure. Weakly-typed alternative of `resolve(tag:builder:)` - - - seealso: `resolve(tag:builder:)` - */ - public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, builder: ((U) throws -> Any) throws -> Any) throws -> Any { - let key = DefinitionKey(type: type, typeOfArguments: U.self, tag: tag?.dependencyTag) - - return try inContext(key, injectedInType: context?.resolvingType) { - try resolve(key: key, builder: { definition in - try builder(definition.weakFactory) - }) - } - } - - /// Lookup definition by the key and use it to resolve instance. Fallback to the key with `nil` tag. - func resolve(key: DefinitionKey, builder: (_Definition) throws -> T) throws -> T { - guard let matching = self.definition(matching: key) else { - return try resolveCollaborating(key, builder: builder) ?? autowire(key) - } - - let (key, definition) = matching - log(.Verbose, context) - - //first search for already resolved instance for this type or any of forwarding types - if let previouslyResolved: T = previouslyResolved(definition, key: key) { - log(.Verbose, "Reusing previously resolved instance \(previouslyResolved)") - return previouslyResolved - } - - var resolvedInstance = try builder(definition) - - /* - Strongly-typed `resolve(tag:builder:)` calls weakly-typed `resolve(_:tag:builder:)`, - so `T` will be `Any` at runtime, erasing type information when this method returns. - When we try to cast result of `Any` to generic type T Swift fails to cast it. - The same happens in the following code snippet: - - let optService: Service? = ServiceImp() - let anyService: Any = optService - let service: Service = anyService as! Service - - That happens because when Optional is casted to Any Swift can not implicitly unwrap it with as operator. - As a workaround we detect boxing here and unwrap it so that we return not a box, but wrapped instance. - */ - if let box = resolvedInstance as? BoxType, let unboxed = box.unboxed as? T { - resolvedInstance = unboxed - } - - //when builder calls factory it will in turn resolve sub-dependencies (if there are any) - //when it returns instance that we try to resolve here can be already resolved - //so we return it, throwing away instance created by previous call to builder - if let previouslyResolved: T = previouslyResolved(definition, key: key) { - log(.Verbose, "Reusing previously resolved instance \(previouslyResolved)") - return previouslyResolved - } - - resolvedInstances[key: key, inScope: definition.scope] = resolvedInstance - - if let resolvable = resolvedInstance as? Resolvable { - resolvedInstances.resolvableInstances.append(resolvable) - resolvable.resolveDependencies(self) - } - - try autoInjectProperties(resolvedInstance) - try definition.resolveProperties(of: resolvedInstance, container: self) - - log(.Verbose, "Resolved type \(key.type) with \(resolvedInstance)") - return resolvedInstance - } - - private func previouslyResolved(_ definition: _Definition, key: DefinitionKey) -> T? { - //first check if exact key was already resolved - if let previouslyResolved = resolvedInstances[key: key, inScope: definition.scope] as? T { - return previouslyResolved - } - //then check if any related type was already resolved - let keys = definition.implementingTypes.map({ - DefinitionKey(type: $0, typeOfArguments: key.typeOfArguments, tag: key.tag) - }) - for key in keys { - if let previouslyResolved = resolvedInstances[key: key, inScope: definition.scope] as? T { - return previouslyResolved - } - } - return nil - } - - /// Searches for definition that matches provided key - private func definition(matching key: DefinitionKey) -> KeyDefinitionPair? { - if let definition = (self.definitions[key] ?? self.definitions[key.tagged(nil)]) { - return (key, definition) - } - - //if no definition registered for exact type try to find type-forwarding definition that can resolve the type - //that will actually happen only when resolving optionals - if definitions.filter({ $0.0.type == key.type }).isEmpty { - return typeForwardingDefinition(key) - } - return nil - } - -} - //MARK: - Collaborating containers extension DependencyContainer { @@ -578,15 +271,18 @@ extension DependencyContainer { } /// Tries to resolve key using collaborating containers - fileprivate func resolveCollaborating(_ key: DefinitionKey, builder: (_Definition) throws -> T) -> T? { + func collaboratingResolve(key aKey: DefinitionKey, builder: (_Definition) throws -> T) -> T? { + let key = aKey for collaborator in _collaborators { //if container is already in a context resolving this type //it means that it has been already called to resolve this type, //so there is probably a cercular reference between containers. //To break it skip this container - if let context = collaborator.context, - context.resolvingType == key.type && - context.tag == key.tag { continue } + #if swift(>=3.0) + if let context = collaborator.context, context.resolvingType == key.type && context.tag == key.tag { continue } + #else + if let context = collaborator.context where context.resolvingType == key.type && context.tag == key.tag { continue } + #endif do { //Pass current container's instances pool to collect instances resolved by collaborator @@ -600,8 +296,8 @@ extension DependencyContainer { collaborator.context = context } - let resolved = try collaborator.inContext(key, injectedInType: self.context.injectedInType, injectedInProperty: self.context.injectedInProperty, logErrors: false) { - try collaborator.resolve(key: key, builder: builder) + let resolved = try collaborator.inContext(key:key, injectedInType: self.context.injectedInType, injectedInProperty: self.context.injectedInProperty, logErrors: false) { + try collaborator._resolve(key: key, builder: builder) } return resolved @@ -617,19 +313,36 @@ extension DependencyContainer { extension DependencyContainer { + #if swift(>=3.0) /** Removes definition registered in the container. - + - parameters: - tag: The tag used to register definition. - definition: The definition to remove */ public func remove(_ definition: Definition, tag: DependencyTagConvertible? = nil) { + _remove(definition: definition, tag: tag) + } + #else + /** + Removes definition registered in the container. + + - parameters: + - tag: The tag used to register definition. + - definition: The definition to remove + */ + public func remove(definition: Definition, tag: DependencyTagConvertible? = nil) { + _remove(definition: definition, tag: tag) + } + #endif + + func _remove(definition aDefinition: Definition, tag: DependencyTagConvertible? = nil) { let key = DefinitionKey(type: T.self, typeOfArguments: U.self, tag: tag?.dependencyTag) - remove(definitionForKey: key) + _remove(definitionForKey: key) } - fileprivate func remove(definitionForKey key: DefinitionKey) { + func _remove(definitionForKey key: DefinitionKey) { precondition(!bootstrapped, "You can not modify container's definitions after it was bootstrapped.") threadSafe { @@ -655,8 +368,11 @@ extension DependencyContainer { } +// MARK: - Validation + extension DependencyContainer { + #if swift(>=3.0) /** Validates container configuration trying to resolve each registered definition one by one. If definition fails to be resolved without arguments will search provided arguments array @@ -665,15 +381,39 @@ extension DependencyContainer { - parameter arguments: set of arguments to use to resolve registered definitions. Use a tuple for registered factories that accept several runtime arguments. - */ + */ public func validate(_ arguments: Any...) throws { + try _validate(arguments: arguments) + } + #else + /** + Validates container configuration trying to resolve each registered definition one by one. + If definition fails to be resolved without arguments will search provided arguments array + for arguments matched by type and try to resolve this definition using these arguments. + If there are no matching arguments will rethrow original error. + + - parameter arguments: Set of arguments to use to resolve registered definitions. + Use a tuple for registered factories that accept several runtime arguments. + */ + public func validate(arguments: Any...) throws { + try _validate(arguments: arguments) + } + #endif + + func _validate(arguments _arguments: [Any]) throws { + let arguments = _arguments validateNextDefinition: for (key, _) in definitions { do { //try to resolve key using provided arguments - for argumentsSet in arguments where type(of: argumentsSet) == key.typeOfArguments { + for argumentsSet in arguments { + #if swift(>=3.0) + guard type(of: argumentsSet) == key.typeOfArguments else { continue } + #else + guard argumentsSet.dynamicType == key.typeOfArguments else { continue } + #endif do { - let _ = try inContext(key, injectedInType: nil) { - try resolve(key: key, builder: { definition throws -> Any in + let _ = try inContext(key:key, injectedInType: nil) { + try self._resolve(key: key, builder: { definition throws -> Any in try definition.weakFactory(argumentsSet) }) } @@ -683,7 +423,7 @@ extension DependencyContainer { throw error } //ignore other errors - catch { log(.Errors, error) } + catch { log(level: .Errors, error) } } //try to resolve key using auto-wiring @@ -694,52 +434,10 @@ extension DependencyContainer { throw error } //ignore other errors - catch { log(.Errors, error) } - } - } - } -} - -///Pool to hold instances, created during call to `resolve()`. -///Before `resolve()` returns pool is drained. -private class ResolvedInstances { - - var resolvedInstances = [DefinitionKey: Any]() - var resolvableInstances = [Resolvable]() - - //singletons are stored using reference type wrapper to be able to share them between containers - fileprivate var singletonsBox = Box<[DefinitionKey: Any]>([:]) - var singletons: [DefinitionKey: Any] { - get { return singletonsBox.unboxed } - set { singletonsBox.unboxed = newValue } - } - - fileprivate var weakSingletonsBox = Box<[DefinitionKey: Any]>([:]) - var weakSingletons: [DefinitionKey: Any] { - get { return weakSingletonsBox.unboxed } - set { weakSingletonsBox.unboxed = newValue } - } - subscript(key key: DefinitionKey, inScope scope: ComponentScope) -> Any? { - get { - switch scope { - case .singleton, .eagerSingleton: return singletons[key] - case .weakSingleton: - if let boxed = weakSingletons[key] as? WeakBoxType { return boxed.unboxed } - else { return weakSingletons[key] } - case .shared: return resolvedInstances[key] - case .unique: return nil - } - } - set { - switch scope { - case .singleton, .eagerSingleton: singletons[key] = newValue - case .weakSingleton: weakSingletons[key] = newValue - case .shared: resolvedInstances[key] = newValue - case .unique: break + catch { log(level: .Errors, error) } } } } - } extension DependencyContainer: CustomStringConvertible { @@ -750,22 +448,6 @@ extension DependencyContainer: CustomStringConvertible { } -//MARK: - Resolvable - -/// Resolvable protocol provides some extension points for resolving dependencies with property injection. -public protocol Resolvable { - /// This method will be called right after instance is created by the container. - func resolveDependencies(_ container: DependencyContainer) - /// This method will be called when all dependencies of the instance are resolved. - /// When resolving objects graph the last resolved instance will receive this callback first. - func didResolveDependencies() -} - -public extension Resolvable { - func resolveDependencies(_ container: DependencyContainer) { } - func didResolveDependencies() { } -} - //MARK: - DependencyTagConvertible /// Implement this protocol on your type if you want to use its instances as `DependencyContainer`'s tags. @@ -838,75 +520,3 @@ public func ==(lhs: DependencyContainer.Tag, rhs: DependencyContainer.Tag) -> Bo return false } } - -//MARK: - DipError - -/** - Errors thrown by `DependencyContainer`'s methods. - - - seealso: `resolve(tag:)` -*/ -public enum DipError: Error, CustomStringConvertible { - - /** - Thrown by `resolve(tag:)` if no matching definition was registered in container. - - - parameter key: definition key used to lookup matching definition - */ - case definitionNotFound(key: DefinitionKey) - - /** - Thrown by `resolve(tag:)` if failed to auto-inject required property. - - - parameters: - - label: The name of the property - - type: The type of the property - - underlyingError: The error that caused auto-injection to fail - */ - case autoInjectionFailed(label: String?, type: Any.Type, underlyingError: Error) - - /** - Thrown by `resolve(tag:)` if failed to auto-wire a type. - - - parameters: - - type: The type that failed to be resolved by auto-wiring - - underlyingError: The error that cause auto-wiring to fail - */ - case autoWiringFailed(type: Any.Type, underlyingError: Error) - - /** - Thrown when auto-wiring type if several definitions with the same number of runtime arguments - are registered for that type. - - - parameters: - - type: The type that failed to be resolved by auto-wiring - - definitions: Ambiguous definitions - */ - case ambiguousDefinitions(type: Any.Type, definitions: [DefinitionType]) - - /** - Thrown by `resolve(tag:)` if resolved instance does not implemenet resolved type (i.e. when type-forwarding). - - - parameters: - - resolved: Resolved instance - - key: Definition key used to resolve instance - */ - case invalidType(resolved: Any?, key: DefinitionKey) - - public var description: String { - switch self { - case let .definitionNotFound(key): - return "No definition registered for \(key).\nCheck the tag, type you try to resolve, number, order and types of runtime arguments passed to `resolve()` and match them with registered factories for type \(key.type)." - case let .autoInjectionFailed(label, type, error): - return "Failed to auto-inject property \"\(label.desc)\" of type \(type). \(error)" - case let .autoWiringFailed(type, error): - return "Failed to auto-wire type \"\(type)\". \(error)" - case let .ambiguousDefinitions(type, definitions): - return "Ambiguous definitions for \(type):\n" + - definitions.map({ "\($0)" }).joined(separator: ";\n") - case let .invalidType(resolved, key): - return "Resolved instance \(resolved ?? "nil") does not implement expected type \(key.type)." - } - } - -} diff --git a/Sources/DipError.swift b/Sources/DipError.swift new file mode 100644 index 0000000..d942883 --- /dev/null +++ b/Sources/DipError.swift @@ -0,0 +1,96 @@ +// +// 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. +// + +#if swift(>=3.0) +/** + Errors thrown by `DependencyContainer`'s methods. + + - seealso: `resolve(tag:)` + */ +public enum DipError: Error, CustomStringConvertible { + + /** + Thrown by `resolve(tag:)` if no matching definition was registered in container. + + - parameter key: definition key used to lookup matching definition + */ + case definitionNotFound(key: DefinitionKey) + + /** + Thrown by `resolve(tag:)` if failed to auto-inject required property. + + - parameters: + - label: The name of the property + - type: The type of the property + - underlyingError: The error that caused auto-injection to fail + */ + case autoInjectionFailed(label: String?, type: Any.Type, underlyingError: Error) + + /** + Thrown by `resolve(tag:)` if failed to auto-wire a type. + + - parameters: + - type: The type that failed to be resolved by auto-wiring + - underlyingError: The error that cause auto-wiring to fail + */ + case autoWiringFailed(type: Any.Type, underlyingError: Error) + + /** + Thrown when auto-wiring type if several definitions with the same number of runtime arguments + are registered for that type. + + - parameters: + - type: The type that failed to be resolved by auto-wiring + - definitions: Ambiguous definitions + */ + case ambiguousDefinitions(type: Any.Type, definitions: [DefinitionType]) + + /** + Thrown by `resolve(tag:)` if resolved instance does not implemenet resolved type (i.e. when type-forwarding). + + - parameters: + - resolved: Resolved instance + - key: Definition key used to resolve instance + */ + case invalidType(resolved: Any?, key: DefinitionKey) + + public var description: String { + switch self { + case let .definitionNotFound(key): + return "No definition registered for \(key).\nCheck the tag, type you try to resolve, number, order and types of runtime arguments passed to `resolve()` and match them with registered factories for type \(key.type)." + case let .autoInjectionFailed(label, type, error): + return "Failed to auto-inject property \"\(label.desc)\" of type \(type). \(error)" + case let .autoWiringFailed(type, error): + return "Failed to auto-wire type \"\(type)\". \(error)" + case let .ambiguousDefinitions(type, definitions): + return "Ambiguous definitions for \(type):\n" + + definitions.map({ "\($0)" }).joined(separator: ";\n") + case let .invalidType(resolved, key): + return "Resolved instance \(resolved ?? "nil") does not implement expected type \(key.type)." + } + } + +} +#endif + diff --git a/Sources/DipError_swift2.swift b/Sources/DipError_swift2.swift new file mode 100644 index 0000000..4496dbf --- /dev/null +++ b/Sources/DipError_swift2.swift @@ -0,0 +1,116 @@ +// +// 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. +// + +#if !swift(>=3.0) + +/** + Errors thrown by `DependencyContainer`'s methods. + + - seealso: `resolve(tag:)` + */ +public enum DipError: Error, CustomStringConvertible { + + /** + Thrown by `resolve(tag:)` if no matching definition was registered in container. + + - parameter key: definition key used to lookup matching definition + */ + case DefinitionNotFound(key: DefinitionKey) + + /** + Thrown by `resolve(tag:)` if failed to auto-inject required property. + + - parameters: + - label: The name of the property + - type: The type of the property + - underlyingError: The error that caused auto-injection to fail + */ + case AutoInjectionFailed(label: String?, type: Any.Type, underlyingError: Error) + + /** + Thrown by `resolve(tag:)` if failed to auto-wire a type. + + - parameters: + - type: The type that failed to be resolved by auto-wiring + - underlyingError: The error that cause auto-wiring to fail + */ + case AutoWiringFailed(type: Any.Type, underlyingError: Error) + + /** + Thrown when auto-wiring type if several definitions with the same number of runtime arguments + are registered for that type. + + - parameters: + - type: The type that failed to be resolved by auto-wiring + - definitions: Ambiguous definitions + */ + case AmbiguousDefinitions(type: Any.Type, definitions: [DefinitionType]) + + /** + Thrown by `resolve(tag:)` if resolved instance does not implemenet resolved type (i.e. when type-forwarding). + + - parameters: + - resolved: Resolved instance + - key: Definition key used to resolve instance + */ + case InvalidType(resolved: Any?, key: DefinitionKey) + + public var description: String { + switch self { + case let .DefinitionNotFound(key): + return "No definition registered for \(key).\nCheck the tag, type you try to resolve, number, order and types of runtime arguments passed to `resolve()` and match them with registered factories for type \(key.type)." + case let .AutoInjectionFailed(label, type, error): + return "Failed to auto-inject property \"\(label.desc)\" of type \(type). \(error)" + case let .AutoWiringFailed(type, error): + return "Failed to auto-wire type \"\(type)\". \(error)" + case let .AmbiguousDefinitions(type, definitions): + return "Ambiguous definitions for \(type):\n" + + definitions.map({ "\($0)" }).joined(separator: ";\n") + case let .InvalidType(resolved, key): + return "Resolved instance \(resolved ?? "nil") does not implement expected type \(key.type)." + } + } + + static func definitionNotFound(key aKey: DefinitionKey) -> DipError { + return DipError.DefinitionNotFound(key: aKey) + } + + static func autoInjectionFailed(label aLabel: String?, type: Any.Type, underlyingError: Error) -> DipError { + return DipError.AutoInjectionFailed(label: aLabel, type: type, underlyingError: underlyingError) + } + + static func autoWiringFailed(type aType: Any.Type, underlyingError: Error) -> DipError { + return DipError.AutoWiringFailed(type: aType, underlyingError: underlyingError) + } + + static func ambiguousDefinitions(type aType: Any.Type, definitions: [DefinitionType]) -> DipError { + return DipError.AmbiguousDefinitions(type: aType, definitions: definitions) + } + + static func invalidType(resolved _resolved: Any?, key: DefinitionKey) -> DipError { + return DipError.InvalidType(resolved: _resolved, key: key) + } +} +#endif + diff --git a/Sources/Register.swift b/Sources/Register.swift new file mode 100644 index 0000000..8274181 --- /dev/null +++ b/Sources/Register.swift @@ -0,0 +1,84 @@ +// +// 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. +// + +#if swift(>=3.0) + extension DependencyContainer { + /** + Registers definition for passed type. + + If instance created by factory of definition, passed as a first parameter, + does not implement type passed in a `type` parameter, + container will throw `DipError.DefinitionNotFound` error when trying to resolve that type. + + - parameters: + - definition: Definition to register + - type: Type to register definition for + - tag: Optional tag to associate definition with. Default is `nil`. + + - returns: New definition registered for passed type. + */ + @discardableResult public func register(_ definition: Definition, type: F.Type, tag: DependencyTagConvertible? = nil) -> Definition { + return _register(definition: definition, type: type, tag: tag) + } + + /** + Register definiton in the container and associate it with an optional tag. + Will override already registered definition for the same type and factory, associated with the same tag. + + - parameters: + - tag: The arbitrary tag to associate this definition with. Pass `nil` to associate with any tag. Default value is `nil`. + - definition: The definition to register in the container. + + */ + public func register(_ definition: Definition, tag: DependencyTagConvertible? = nil) { + _register(definition: definition, tag: tag) + } + + } +#endif + +extension DependencyContainer { + + func _register(definition aDefinition: Definition, tag: DependencyTagConvertible? = nil) { + precondition(!bootstrapped, "You can not modify container's definitions after it was bootstrapped.") + let definition = aDefinition + threadSafe { + let key = DefinitionKey(type: T.self, typeOfArguments: U.self, tag: tag?.dependencyTag) + if let _ = definitions[key] { + _remove(definitionForKey: key) + } + + definition.container = self + definitions[key] = definition + resolvedInstances.singletons[key] = nil + resolvedInstances.weakSingletons[key] = nil + + if .eagerSingleton == definition.scope { + bootstrapQueue.append({ _ = try self.resolve(tag: tag) as T }) + } + } + } + +} + diff --git a/Sources/Register_swift2.swift b/Sources/Register_swift2.swift new file mode 100644 index 0000000..ae496ad --- /dev/null +++ b/Sources/Register_swift2.swift @@ -0,0 +1,59 @@ +// +// 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. +// + +#if !swift(>=3.0) + extension DependencyContainer { + /** + Registers definition for passed type. + + If instance created by factory of definition, passed as a first parameter, + does not implement type passed in a `type` parameter, + container will throw `DipError.DefinitionNotFound` error when trying to resolve that type. + + - parameters: + - definition: Definition to register + - type: Type to register definition for + - tag: Optional tag to associate definition with. Default is `nil`. + + - returns: New definition registered for passed type. + */ + public func register(definition: Definition, type: F.Type, tag: DependencyTagConvertible? = nil) -> Definition { + return _register(definition: definition, type: type, tag: tag) + } + + /** + Register definiton in the container and associate it with an optional tag. + Will override already registered definition for the same type and factory, associated with the same tag. + + - parameters: + - tag: The arbitrary tag to associate this definition with. Pass `nil` to associate with any tag. Default value is `nil`. + - definition: The definition to register in the container. + + */ + public func register(definition: Definition, tag: DependencyTagConvertible? = nil) { + _register(definition: definition, tag: tag) + } + } +#endif + diff --git a/Sources/Resolve.swift b/Sources/Resolve.swift new file mode 100644 index 0000000..fc55c29 --- /dev/null +++ b/Sources/Resolve.swift @@ -0,0 +1,293 @@ +// +// 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. +// + +#if swift(>=3.0) + extension DependencyContainer { + + /** + Resolve an instance of type `T`. + + If no matching definition was registered with provided `tag`, + container will lookup definition associated with `nil` tag. + + - parameter tag: The arbitrary tag to use to lookup definition. + + - throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`, `DipError.InvalidType` + + - returns: An instance of type `T`. + + **Example**: + ```swift + let service = try! container.resolve() as Service + let service = try! container.resolve(tag: "service") as Service + let service: Service = try! container.resolve() + ``` + + - seealso: `register(_:type:tag:factory:)` + */ + public func resolve(tag: DependencyTagConvertible? = nil) throws -> T { + return try resolve(tag: tag) { factory in try factory() } + } + + /** + Resolve an instance of requested type. Weakly-typed alternative of `resolve(tag:)` + + - warning: This method does not make any type checks, so there is no guaranty that + resulting instance is actually an instance of requested type. + That can happen if you register forwarded type that is not implemented by resolved instance. + + - parameters: + - type: Type to resolve + - tag: The arbitrary tag to use to lookup definition. + + - throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`, `DipError.InvalidType` + + - returns: An instance of requested type. + + **Example**: + ```swift + let service = try! container.resolve(Service.self) as! Service + let service = try! container.resolve(Service.self, tag: "service") as! Service + ``` + + - seealso: `resolve(tag:)`, `register(_:type:tag:factory:)` + */ + public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory() } + } + + /** + Resolve an instance of type `T` using generic builder closure that accepts generic factory and returns created instance. + + - parameters: + - tag: The arbitrary tag to use to lookup definition. + - builder: Generic closure that accepts generic factory and returns inctance created by that factory. + + - throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`, `DipError.InvalidType` + + - returns: An instance of type `T`. + + - note: You _should not_ call this method directly, instead call any of other + `resolve(tag:)` or `resolve(tag:withArguments:)` methods. + You _should_ use this method only to resolve dependency with more runtime arguments than + _Dip_ supports (currently it's up to six) like in the following example: + + ```swift + public func resolve(tag: Tag? = nil, _ arg1: A, _ arg2: B, _ arg3: C, ...) throws -> T { + return try resolve(tag: tag) { factory in factory(arg1, arg2, arg3, ...) } + } + ``` + + Though before you do so you should probably review your design and try to reduce the number of dependencies. + */ + public func resolve(tag: DependencyTagConvertible? = nil, builder: ((U) throws -> T) throws -> T) throws -> T { + return try _resolve(tag: tag, builder: builder) + } + + /** + Resolve an instance of provided type using builder closure. Weakly-typed alternative of `resolve(tag:builder:)` + + - seealso: `resolve(tag:builder:)` + */ + public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, builder: ((U) throws -> Any) throws -> Any) throws -> Any { + return try _resolve(type: type, tag: tag, builder: builder) + } + + } +#endif + +extension DependencyContainer { + + func _resolve(tag aTag: DependencyTagConvertible? = nil, builder: ((U) throws -> T) throws -> T) throws -> T { + return try resolve(T.self, tag: aTag, builder: { factory in + try builder({ try factory($0) as! T }) + }) as! T + } + + func _resolve(type aType: Any.Type, tag: DependencyTagConvertible? = nil, builder: ((U) throws -> Any) throws -> Any) throws -> Any { + let key = DefinitionKey(type: aType, typeOfArguments: U.self, tag: tag?.dependencyTag) + + return try inContext(key:key, injectedInType: context?.resolvingType) { + try self._resolve(key: key, builder: { definition in + try builder(definition.weakFactory) + }) + } + } + + /// Lookup definition by the key and use it to resolve instance. Fallback to the key with `nil` tag. + func _resolve(key aKey: DefinitionKey, builder: (_Definition) throws -> T) throws -> T { + guard let matching = self.definition(matching: aKey) else { + return try collaboratingResolve(key: aKey, builder: builder) ?? autowire(key: aKey) + } + + let (key, definition) = matching + + //first search for already resolved instance for this type or any of forwarding types + if let previouslyResolved: T = previouslyResolved(for: definition, key: key) { + log(level: .Verbose, "Reusing previously resolved instance \(previouslyResolved)") + return previouslyResolved + } + + log(level: .Verbose, context) + var resolvedInstance = try builder(definition) + + /* + Strongly-typed `resolve(tag:builder:)` calls weakly-typed `resolve(_:tag:builder:)`, + so `T` will be `Any` at runtime, erasing type information when this method returns. + When we try to cast result of `Any` to generic type T Swift fails to cast it. + The same happens in the following code snippet: + + let optService: Service? = ServiceImp() + let anyService: Any = optService + let service: Service = anyService as! Service + + That happens because when Optional is casted to Any Swift can not implicitly unwrap it with as operator. + As a workaround we detect boxing here and unwrap it so that we return not a box, but wrapped instance. + */ + if let box = resolvedInstance as? BoxType, let unboxed = box.unboxed as? T { + resolvedInstance = unboxed + } + + //when builder calls factory it will in turn resolve sub-dependencies (if there are any) + //when it returns instance that we try to resolve here can be already resolved + //so we return it, throwing away instance created by previous call to builder + if let previouslyResolved: T = previouslyResolved(for: definition, key: key) { + log(level: .Verbose, "Reusing previously resolved instance \(previouslyResolved)") + return previouslyResolved + } + + resolvedInstances[key: key, inScope: definition.scope] = resolvedInstance + + if let resolvable = resolvedInstance as? Resolvable { + resolvedInstances.resolvableInstances.append(resolvable) + resolvable.resolveDependencies(self) + } + + try autoInjectProperties(in: resolvedInstance) + try definition.resolveProperties(of: resolvedInstance, container: self) + + log(level: .Verbose, "Resolved type \(key.type) with \(resolvedInstance)") + return resolvedInstance + } + + private func previouslyResolved(for definition: _Definition, key: DefinitionKey) -> T? { + //first check if exact key was already resolved + if let previouslyResolved = resolvedInstances[key: key, inScope: definition.scope] as? T { + return previouslyResolved + } + //then check if any related type was already resolved + let keys = definition.implementingTypes.map({ + DefinitionKey(type: $0, typeOfArguments: key.typeOfArguments, tag: key.tag) + }) + for key in keys { + if let previouslyResolved = resolvedInstances[key: key, inScope: definition.scope] as? T { + return previouslyResolved + } + } + return nil + } + + /// Searches for definition that matches provided key + private func definition(matching key: DefinitionKey) -> KeyDefinitionPair? { + if let definition = (self.definitions[key] ?? self.definitions[key.tagged(with: nil)]) { + return (key, definition) + } + + //if no definition registered for exact type try to find type-forwarding definition that can resolve the type + //that will actually happen only when resolving optionals + if definitions.filter({ $0.0.type == key.type }).isEmpty { + return typeForwardingDefinition(forKey: key) + } + return nil + } + +} + +///Pool to hold instances, created during call to `resolve()`. +///Before `resolve()` returns pool is drained. +class ResolvedInstances { + + var resolvedInstances = [DefinitionKey: Any]() + var resolvableInstances = [Resolvable]() + + //singletons are stored using reference type wrapper to be able to share them between containers + var singletonsBox = Box<[DefinitionKey: Any]>([:]) + var singletons: [DefinitionKey: Any] { + get { return singletonsBox.unboxed } + set { singletonsBox.unboxed = newValue } + } + + var weakSingletonsBox = Box<[DefinitionKey: Any]>([:]) + var weakSingletons: [DefinitionKey: Any] { + get { return weakSingletonsBox.unboxed } + set { weakSingletonsBox.unboxed = newValue } + } + + subscript(key key: DefinitionKey, inScope scope: ComponentScope) -> Any? { + get { + if scope == .singleton || scope == .eagerSingleton { + return singletons[key] + } + if scope == .weakSingleton { + if let boxed = weakSingletons[key] as? WeakBoxType { return boxed.unboxed } + else { return weakSingletons[key] } + } + if scope == .shared { + return resolvedInstances[key] + } + return nil + } + set { + if scope == .singleton || scope == .eagerSingleton { + singletons[key] = newValue + } + if scope == .weakSingleton { + weakSingletons[key] = newValue + } + if scope == .shared { + resolvedInstances[key] = newValue + } + } + } + +} + +//MARK: - Resolvable + +#if swift(>=3.0) + + /// Resolvable protocol provides some extension points for resolving dependencies with property injection. + public protocol Resolvable { + /// This method will be called right after instance is created by the container. + func resolveDependencies(_ container: DependencyContainer) + /// This method will be called when all dependencies of the instance are resolved. + /// When resolving objects graph the last resolved instance will receive this callback first. + func didResolveDependencies() + } + + extension Resolvable { + func resolveDependencies(_ container: DependencyContainer) {} + func didResolveDependencies() {} + } +#endif diff --git a/Sources/Resolve_swift2.swift b/Sources/Resolve_swift2.swift new file mode 100644 index 0000000..ce6514b --- /dev/null +++ b/Sources/Resolve_swift2.swift @@ -0,0 +1,135 @@ +// +// 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. +// + +#if !swift(>=3.0) + extension DependencyContainer { + + /** + Resolve an instance of type `T`. + + If no matching definition was registered with provided `tag`, + container will lookup definition associated with `nil` tag. + + - parameter tag: The arbitrary tag to use to lookup definition. + + - throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`, `DipError.InvalidType` + + - returns: An instance of type `T`. + + **Example**: + ```swift + let service = try! container.resolve() as Service + let service = try! container.resolve(tag: "service") as Service + let service: Service = try! container.resolve() + ``` + + - seealso: `register(_:type:tag:factory:)` + */ + public func resolve(tag tag: DependencyTagConvertible? = nil) throws -> T { + return try resolve(tag: tag) { factory in try factory() } + } + + /** + Resolve an instance of requested type. Weakly-typed alternative of `resolve(tag:)` + + - warning: This method does not make any type checks, so there is no guaranty that + resulting instance is actually an instance of requested type. + That can happen if you register forwarded type that is not implemented by resolved instance. + + - parameters: + - type: Type to resolve + - tag: The arbitrary tag to use to lookup definition. + + - throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`, `DipError.InvalidType` + + - returns: An instance of requested type. + + **Example**: + ```swift + let service = try! container.resolve(Service.self) as! Service + let service = try! container.resolve(Service.self, tag: "service") as! Service + ``` + + - seealso: `resolve(tag:)`, `register(_:type:tag:factory:)` + */ + public func resolve(type: Any.Type, tag: DependencyTagConvertible? = nil) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory() } + } + + /** + Resolve an instance of type `T` using generic builder closure that accepts generic factory and returns created instance. + + - parameters: + - tag: The arbitrary tag to use to lookup definition. + - builder: Generic closure that accepts generic factory and returns inctance created by that factory. + + - throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`, `DipError.InvalidType` + + - returns: An instance of type `T`. + + - note: You _should not_ call this method directly, instead call any of other + `resolve(tag:)` or `resolve(tag:withArguments:)` methods. + You _should_ use this method only to resolve dependency with more runtime arguments than + _Dip_ supports (currently it's up to six) like in the following example: + + ```swift + public func resolve(tag: Tag? = nil, _ arg1: A, _ arg2: B, _ arg3: C, ...) throws -> T { + return try resolve(tag: tag) { factory in factory(arg1, arg2, arg3, ...) } + } + ``` + + Though before you do so you should probably review your design and try to reduce the number of dependencies. + */ + public func resolve(tag tag: DependencyTagConvertible? = nil, builder: ((U) throws -> T) throws -> T) throws -> T { + return try _resolve(tag: tag, builder: builder) + } + + /** + Resolve an instance of provided type using builder closure. Weakly-typed alternative of `resolve(tag:builder:)` + + - seealso: `resolve(tag:builder:)` + */ + public func resolve(type: Any.Type, tag: DependencyTagConvertible? = nil, builder: ((U) throws -> Any) throws -> Any) throws -> Any { + return try _resolve(type: type, tag: tag, builder: builder) + } + + } + + /// Resolvable protocol provides some extension points for resolving dependencies with property injection. + public protocol Resolvable { + /// This method will be called right after instance is created by the container. + func resolveDependencies(container: DependencyContainer) + /// This method will be called when all dependencies of the instance are resolved. + /// When resolving objects graph the last resolved instance will receive this callback first. + func didResolveDependencies() + } + + extension Resolvable { + func resolveDependencies(container: DependencyContainer) {} + func didResolveDependencies() {} + } + +#endif + + diff --git a/Sources/RuntimeArguments.swift b/Sources/RuntimeArguments.swift index b46f015..6d0eb2f 100644 --- a/Sources/RuntimeArguments.swift +++ b/Sources/RuntimeArguments.swift @@ -22,146 +22,235 @@ // THE SOFTWARE. // -// MARK: - Register/resolve dependencies with runtime arguments - -extension DependencyContainer { - - // MARK: 1 Runtime Argument +#if swift(>=3.0) - /** - Register factory that accepts one runtime argument of type `A`. You can use up to six runtime arguments. + extension DependencyContainer { + + /** + Register factory for type `T` and associate it with an optional tag. + + - parameters: + - scope: The scope to use for instance created by the factory. Default value is `Shared`. + - type: Type to register definition for. Default value is return value of factory. + - tag: The arbitrary tag to associate this factory with. Pass `nil` to associate with any tag. Default value is `nil`. + - factory: The factory that produces instance of `type`. Will be used to resolve instances of `type`. + + - returns: A registered definition. + + - note: You should cast the factory return type to the protocol you want to register it for + (unless you want to register concrete type) or provide `type` parameter. + + - seealso: `Definition`, `ComponentScope`, `DependencyTagConvertible` + + **Example**: + ```swift + //Register ServiceImp as Service + container.register { ServiceImp() as Service } + + //Register ServiceImp as Service named by "service" + container.register(tag: "service") { ServiceImp() as Service } + + //Register unique ServiceImp as Service + container.register(.unique) { ServiceImp() as Service } + + //Register ClientImp as Client and resolve it's service dependency + container.register { try ClientImp(service: container.resolve() as Service) as Client } + + //Register ServiceImp as concrete type + container.register { ServiceImp() } + container.register(factory: ServiceImp.init) + + //Register ServiceImp as Service + container.register(Service.self, factory: ServiceImp.init) + + //Register ClientImp as Client + container.register(Client.self, factory: ClientImp.init(service:)) + ``` + */ + @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping () throws -> T) -> Definition { + let definition = DefinitionBuilder { + $0.scope = scope + $0.factory = factory + }.build() + register(definition, tag: tag) + return definition + } + + /** + Register generic factory and auto-wiring factory and associate it with an optional tag. + + - parameters: + - tag: The arbitrary tag to associate this factory with. Pass `nil` to associate with any tag. Default value is `nil`. + - scope: The scope to use for instance created by the factory. + - factory: The factory to register. + - numberOfArguments: The number of factory arguments. Will be used on auto-wiring to sort definitions. + - autoWiringFactory: The factory to be used on auto-wiring to resolve component. + + - returns: A registered definition. + + - note: You _should not_ call this method directly, instead call any of other `register` methods. + You _should_ use this method only to register dependency with more runtime arguments + than _Dip_ supports (currently it's up to six) like in the following example: + + ```swift + public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: Tag? = nil, factory: (A, B, C, ...) throws -> T) -> Definition { + return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: ...) { container, tag in + try factory(container.resolve(tag: tag), ...) + } + } + ``` + + Though before you do so you should probably review your design and try to reduce number of depnedencies. + */ + public func register(scope: ComponentScope, type: T.Type, tag: DependencyTagConvertible?, factory: @escaping (U) throws -> T, numberOfArguments: Int, autoWiringFactory: @escaping (DependencyContainer, Tag?) throws -> T) -> Definition { + let definition = DefinitionBuilder { + $0.scope = scope + $0.factory = factory + $0.numberOfArguments = numberOfArguments + $0.autoWiringFactory = autoWiringFactory + }.build() + register(definition, tag: tag) + return definition + } + + // MARK: 1 Runtime Argument + + /** + Register factory that accepts one runtime argument of type `A`. You can use up to six runtime arguments. - - note: You can have several factories with different number or types of arguments registered for same type, - optionally associated with some tags. When container resolves that type it matches the type, - __number__, __types__ and __order__ of runtime arguments and optional tag that you pass to `resolve(tag:withArguments:)` method. + - note: You can have several factories with different number or types of arguments registered for same type, + optionally associated with some tags. When container resolves that type it matches the type, + __number__, __types__ and __order__ of runtime arguments and optional tag that you pass to `resolve(tag:arguments:)` method. - - parameters: - - tag: The arbitrary tag to associate this factory with. Pass `nil` to associate with any tag. Default value is `nil`. - - scope: The scope to use for this component. Default value is `Shared`. - - factory: The factory to register. - - - seealso: `register(_:type:tag:factory:)` - */ - @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A) throws -> T) -> Definition { - return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: 1) { container, tag in try factory(container.resolve(tag: tag)) } - } + - parameters: + - tag: The arbitrary tag to associate this factory with. Pass `nil` to associate with any tag. Default value is `nil`. + - scope: The scope to use for this component. Default value is `Shared`. + - factory: The factory to register. + + - seealso: `register(_:type:tag:factory:)` + */ + @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A) throws -> T) -> Definition { + return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: 1) { container, tag in try factory(container.resolve(tag: tag)) } + } - /** - Resolve type `T` using one runtime argument. - - - note: When resolving a type container will first try to use definition - that exactly matches types of arguments that you pass to resolve method. - If it fails or no such definition is found container will try to _auto-wire_ component. - For that it will iterate through all the definitions registered for that type - which factories accept any number of runtime arguments and are tagged with the same tag, - passed to `resolve` method, or with no tag. Container will try to use these definitions - to resolve a component one by one until one of them succeeds, starting with tagged definitions - in order of decreasing their's factories number of arguments. If none of them succeds it will - throw an error. If it finds two definitions with the same number of arguments - it will throw - an error. - - - parameters: - - tag: The arbitrary tag to lookup registered definition. - - arg1: The first argument to pass to the definition's factory. - - - throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions` - - - returns: An instance of type `T`. - - - seealso: `register(tag:_:factory:)`, `resolve(tag:builder:)` - */ - public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A) throws -> T { - return try resolve(tag: tag) { factory in try factory(arg1) } - } + /** + Resolve type `T` using one runtime argument. + + - note: When resolving a type container will first try to use definition + that exactly matches types of arguments that you pass to resolve method. + If it fails or no such definition is found container will try to _auto-wire_ component. + For that it will iterate through all the definitions registered for that type + which factories accept any number of runtime arguments and are tagged with the same tag, + passed to `resolve` method, or with no tag. Container will try to use these definitions + to resolve a component one by one until one of them succeeds, starting with tagged definitions + in order of decreasing their's factories number of arguments. If none of them succeds it will + throw an error. If it finds two definitions with the same number of arguments - it will throw + an error. + + - parameters: + - tag: The arbitrary tag to lookup registered definition. + - arg1: The first argument to pass to the definition's factory. + + - throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions` - ///- seealso: `resolve(_:tag:)`, `resolve(tag:withArguments:)` - public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A) throws -> Any { - return try resolve(type, tag: tag) { factory in try factory(arg1) } - } + - returns: An instance of type `T`. - // MARK: 2 Runtime Arguments - - /// - seealso: `register(_:type:tag:factory:)` - @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B) throws -> T) -> Definition { - return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: 2) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag)) } - } + - seealso: `register(_:type:tag:factory:)`, `resolve(tag:builder:)` + */ + public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A) throws -> T { + return try resolve(tag: tag) { factory in try factory(arg1) } + } - /// - seealso: `resolve(tag:withArguments:)` - public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B) throws -> T { - return try resolve(tag: tag) { factory in try factory(arg1, arg2) } - } - - ///- seealso: `resolve(_:tag:)`, `resolve(tag:withArguments:)` - public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B) throws -> Any { - return try resolve(type, tag: tag) { factory in try factory((arg1, arg2)) } - } - - // MARK: 3 Runtime Arguments - - /// - seealso: `register(tag:scope:factory:)` - @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C) throws -> T) -> Definition { - return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: 3) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag)) } - } + ///- seealso: `resolve(_:tag:)`, `resolve(tag:arguments:)` + public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory(arg1) } + } - /// - seealso: `resolve(tag:withArguments:)` - public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C) throws -> T { - return try resolve(tag: tag) { factory in try factory(arg1, arg2, arg3) } - } - - ///- seealso: `resolve(_:tag:)`, `resolve(tag:withArguments:)` - public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C) throws -> Any { - return try resolve(type, tag: tag) { factory in try factory((arg1, arg2, arg3)) } - } + // MARK: 2 Runtime Arguments + + /// - seealso: `register(_:type:tag:factory:)` + @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B) throws -> T) -> Definition { + return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: 2) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag)) } + } - // MARK: 4 Runtime Arguments - - /// - seealso: `register(tag:scope:factory:)` - @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C, D) throws -> T) -> Definition { - return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: 4) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag)) } - } + /// - seealso: `resolve(tag:arguments:)` + public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B) throws -> T { + return try resolve(tag: tag) { factory in try factory(arg1, arg2) } + } + + ///- seealso: `resolve(_:tag:)`, `resolve(tag:arguments:)` + public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory((arg1, arg2)) } + } + + // MARK: 3 Runtime Arguments + + /// - seealso: `register(_:type:tag:factory:)` + @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C) throws -> T) -> Definition { + return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: 3) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag)) } + } - /// - seealso: `resolve(tag:withArguments:)` - public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D) throws -> T { - return try resolve(tag: tag) { factory in try factory(arg1, arg2, arg3, arg4) } - } + /// - seealso: `resolve(tag:arguments:)` + public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C) throws -> T { + return try resolve(tag: tag) { factory in try factory(arg1, arg2, arg3) } + } + + ///- seealso: `resolve(_:tag:)`, `resolve(tag:arguments:)` + public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory((arg1, arg2, arg3)) } + } - /// - seealso: `resolve(_:tag:)`, `resolve(tag:withArguments:)` - public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D) throws -> Any { - return try resolve(type, tag: tag) { factory in try factory((arg1, arg2, arg3, arg4)) } - } + // MARK: 4 Runtime Arguments + + /// - seealso: `register(_:type:tag:factory:)` + @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C, D) throws -> T) -> Definition { + return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: 4) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag)) } + } - // MARK: 5 Runtime Arguments - - /// - seealso: `register(tag:scope:factory:)` - @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C, D, E) throws -> T) -> Definition { - return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: 5) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag)) } - } + /// - seealso: `resolve(tag:arguments:)` + public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D) throws -> T { + return try resolve(tag: tag) { factory in try factory(arg1, arg2, arg3, arg4) } + } - /// - seealso: `resolve(tag:withArguments:)` - public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D, _ arg5: E) throws -> T { - return try resolve(tag: tag) { factory in try factory(arg1, arg2, arg3, arg4, arg5) } - } + /// - seealso: `resolve(_:tag:)`, `resolve(tag:arguments:)` + public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory((arg1, arg2, arg3, arg4)) } + } - ///- seealso: `resolve(_:tag:)`, `resolve(tag:withArguments:)` - public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D, _ arg5: E) throws -> Any { - return try resolve(type, tag: tag) { factory in try factory((arg1, arg2, arg3, arg4, arg5)) } - } + // MARK: 5 Runtime Arguments + + /// - seealso: `register(_:type:tag:factory:)` + @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C, D, E) throws -> T) -> Definition { + return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: 5) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag)) } + } - // MARK: 6 Runtime Arguments - - /// - seealso: `register(tag:scope:factory:)` - @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C, D, E, F) throws -> T) -> Definition { - return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: 6) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag)) } - } + /// - seealso: `resolve(tag:arguments:)` + public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D, _ arg5: E) throws -> T { + return try resolve(tag: tag) { factory in try factory(arg1, arg2, arg3, arg4, arg5) } + } - /// - seealso: `resolve(tag:withArguments:)` - public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D, _ arg5: E, _ arg6: F) throws -> T { - return try resolve(tag: tag) { factory in try factory(arg1, arg2, arg3, arg4, arg5, arg6) } - } + ///- seealso: `resolve(_:tag:)`, `resolve(tag:arguments:)` + public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D, _ arg5: E) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory((arg1, arg2, arg3, arg4, arg5)) } + } - /// - seealso: `resolve(_:tag:)`, `resolve(tag:withArguments:)` - public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D, _ arg5: E, _ arg6: F) throws -> Any { - return try resolve(type, tag: tag) { factory in try factory((arg1, arg2, arg3, arg4, arg5, arg6)) } - } + // MARK: 6 Runtime Arguments + + /// - seealso: `register(_:type:tag:factory:)` + @discardableResult public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C, D, E, F) throws -> T) -> Definition { + return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: 6) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag)) } + } -} + /// - seealso: `resolve(tag:arguments:)` + public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D, _ arg5: E, _ arg6: F) throws -> T { + return try resolve(tag: tag) { factory in try factory(arg1, arg2, arg3, arg4, arg5, arg6) } + } + + /// - seealso: `resolve(_:tag:)`, `resolve(tag:arguments:)` + public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D, _ arg5: E, _ arg6: F) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory((arg1, arg2, arg3, arg4, arg5, arg6)) } + } + + } + +#endif diff --git a/Sources/RuntimeArguments_swift2.swift b/Sources/RuntimeArguments_swift2.swift new file mode 100644 index 0000000..4586fee --- /dev/null +++ b/Sources/RuntimeArguments_swift2.swift @@ -0,0 +1,256 @@ +// +// 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. +// + +#if !swift(>=3.0) + + extension DependencyContainer { + + /** + Register factory for type `T` and associate it with an optional tag. + + - parameters: + - scope: The scope to use for instance created by the factory. Default value is `Shared`. + - type: Type to register definition for. Default value is return value of factory. + - tag: The arbitrary tag to associate this factory with. Pass `nil` to associate with any tag. Default value is `nil`. + - factory: The factory that produces instance of `type`. Will be used to resolve instances of `type`. + + - returns: A registered definition. + + - note: You should cast the factory return type to the protocol you want to register it for + (unless you want to register concrete type) or provide `type` parameter. + + - seealso: `Definition`, `ComponentScope`, `DependencyTagConvertible` + + **Example**: + ```swift + //Register ServiceImp as Service + container.register { ServiceImp() as Service } + + //Register ServiceImp as Service named by "service" + container.register(tag: "service") { ServiceImp() as Service } + + //Register unique ServiceImp as Service + container.register(.unique) { ServiceImp() as Service } + + //Register ClientImp as Client and resolve it's service dependency + container.register { try ClientImp(service: container.resolve() as Service) as Client } + + //Register ServiceImp as concrete type + container.register { ServiceImp() } + container.register(factory: ServiceImp.init) + + //Register ServiceImp as Service + container.register(Service.self, factory: ServiceImp.init) + + //Register ClientImp as Client + container.register(Client.self, factory: ClientImp.init(service:)) + ``` + */ + public func register(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: () throws -> T) -> Definition { + let definition = DefinitionBuilder { + $0.scope = scope + $0.factory = factory + }.build() + register(definition, tag: tag) + return definition + } + + /** + Register generic factory and auto-wiring factory and associate it with an optional tag. + + - parameters: + - tag: The arbitrary tag to associate this factory with. Pass `nil` to associate with any tag. Default value is `nil`. + - scope: The scope to use for instance created by the factory. + - factory: The factory to register. + - numberOfArguments: The number of factory arguments. Will be used on auto-wiring to sort definitions. + - autoWiringFactory: The factory to be used on auto-wiring to resolve component. + + - returns: A registered definition. + + - note: You _should not_ call this method directly, instead call any of other `register` methods. + You _should_ use this method only to register dependency with more runtime arguments + than _Dip_ supports (currently it's up to six) like in the following example: + + ```swift + public func register(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: Tag? = nil, factory: (A, B, C, ...) throws -> T) -> Definition { + return register(scope: scope, type: type, tag: tag, factory: factory, numberOfArguments: ...) { container, tag in + try factory(container.resolve(tag: tag), ...) + } + } + ``` + + Though before you do so you should probably review your design and try to reduce number of depnedencies. + */ + public func register(scope: ComponentScope, type: T.Type, tag: DependencyTagConvertible?, factory: (U) throws -> T, numberOfArguments: Int, autoWiringFactory: (DependencyContainer, Tag?) throws -> T) -> Definition { + let definition = DefinitionBuilder { + $0.scope = scope + $0.factory = factory + $0.numberOfArguments = numberOfArguments + $0.autoWiringFactory = autoWiringFactory + }.build() + register(definition, tag: tag) + return definition + } + + // MARK: 1 Runtime Argument + + /** + Register factory that accepts one runtime argument of type `A`. You can use up to six runtime arguments. + + - note: You can have several factories with different number or types of arguments registered for same type, + optionally associated with some tags. When container resolves that type it matches the type, + __number__, __types__ and __order__ of runtime arguments and optional tag that you pass to `resolve(tag:arguments:)` method. + + - parameters: + - tag: The arbitrary tag to associate this factory with. Pass `nil` to associate with any tag. Default value is `nil`. + - scope: The scope to use for this component. Default value is `Shared`. + - factory: The factory to register. + + - seealso: `register(_:type:tag:factory:)` + */ + public func register(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: (A) throws -> T) -> Definition { + return register(scope, type: type, tag: tag, factory: factory, numberOfArguments: 1) { container, tag in try factory(container.resolve(tag: tag)) } + } + + /** + Resolve type `T` using one runtime argument. + + - note: When resolving a type container will first try to use definition + that exactly matches types of arguments that you pass to resolve method. + If it fails or no such definition is found container will try to _auto-wire_ component. + For that it will iterate through all the definitions registered for that type + which factories accept any number of runtime arguments and are tagged with the same tag, + passed to `resolve` method, or with no tag. Container will try to use these definitions + to resolve a component one by one until one of them succeeds, starting with tagged definitions + in order of decreasing their's factories number of arguments. If none of them succeds it will + throw an error. If it finds two definitions with the same number of arguments - it will throw + an error. + + - parameters: + - tag: The arbitrary tag to lookup registered definition. + - arg1: The first argument to pass to the definition's factory. + + - throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions` + + - returns: An instance of type `T`. + + - seealso: `register(_:tag:factory:)`, `resolve(tag:builder:)` + */ + public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A) throws -> T { + return try resolve(tag: tag) { factory in try factory(arg1) } + } + + ///- seealso: `resolve(_:tag:)`, `resolve(tag:arguments:)` + public func resolve(type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory(arg1) } + } + + // MARK: 2 Runtime Arguments + + /// - seealso: `register(_:type:tag:factory:)` + public func register(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: (A, B) throws -> T) -> Definition { + return register(scope, type: type, tag: tag, factory: factory, numberOfArguments: 2) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag)) } + } + + /// - seealso: `resolve(tag:arguments:)` + public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B) throws -> T { + return try resolve(tag: tag) { factory in try factory(arg1, arg2) } + } + + ///- seealso: `resolve(_:tag:)`, `resolve(tag:arguments:)` + public func resolve(type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory((arg1, arg2)) } + } + + // MARK: 3 Runtime Arguments + + /// - seealso: `register(_:type:tag:factory:)` + public func register(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: (A, B, C) throws -> T) -> Definition { + return register(scope, type: type, tag: tag, factory: factory, numberOfArguments: 3) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag)) } + } + + /// - seealso: `resolve(tag:arguments:)` + public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C) throws -> T { + return try resolve(tag: tag) { factory in try factory(arg1, arg2, arg3) } + } + + ///- seealso: `resolve(_:tag:)`, `resolve(tag:arguments:)` + public func resolve(type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory((arg1, arg2, arg3)) } + } + + // MARK: 4 Runtime Arguments + + /// - seealso: `register(_:type:tag:factory:)` + public func register(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: (A, B, C, D) throws -> T) -> Definition { + return register(scope, type: type, tag: tag, factory: factory, numberOfArguments: 4) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag)) } + } + + /// - seealso: `resolve(tag:arguments:)` + public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D) throws -> T { + return try resolve(tag: tag) { factory in try factory(arg1, arg2, arg3, arg4) } + } + + /// - seealso: `resolve(_:tag:)`, `resolve(tag:arguments:)` + public func resolve(type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory((arg1, arg2, arg3, arg4)) } + } + + // MARK: 5 Runtime Arguments + + /// - seealso: `register(_:type:tag:factory:)` + public func register(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: (A, B, C, D, E) throws -> T) -> Definition { + return register(scope, type: type, tag: tag, factory: factory, numberOfArguments: 5) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag)) } + } + + /// - seealso: `resolve(tag:arguments:)` + public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D, _ arg5: E) throws -> T { + return try resolve(tag: tag) { factory in try factory(arg1, arg2, arg3, arg4, arg5) } + } + + ///- seealso: `resolve(_:tag:)`, `resolve(tag:arguments:)` + public func resolve(type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D, _ arg5: E) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory((arg1, arg2, arg3, arg4, arg5)) } + } + + // MARK: 6 Runtime Arguments + + /// - seealso: `register(_:type:tag:factory:)` + public func register(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: (A, B, C, D, E, F) throws -> T) -> Definition { + return register(scope, type: type, tag: tag, factory: factory, numberOfArguments: 6) { container, tag in try factory(container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag), container.resolve(tag: tag)) } + } + + /// - seealso: `resolve(tag:arguments:)` + public func resolve(tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D, _ arg5: E, _ arg6: F) throws -> T { + return try resolve(tag: tag) { factory in try factory(arg1, arg2, arg3, arg4, arg5, arg6) } + } + + /// - seealso: `resolve(_:tag:)`, `resolve(tag:arguments:)` + public func resolve(type: Any.Type, tag: DependencyTagConvertible? = nil, arguments arg1: A, _ arg2: B, _ arg3: C, _ arg4: D, _ arg5: E, _ arg6: F) throws -> Any { + return try resolve(type, tag: tag) { factory in try factory((arg1, arg2, arg3, arg4, arg5, arg6)) } + } + + } + +#endif diff --git a/Sources/TypeForwarding.swift b/Sources/TypeForwarding.swift index 0c7b32b..d841870 100644 --- a/Sources/TypeForwarding.swift +++ b/Sources/TypeForwarding.swift @@ -24,9 +24,11 @@ protocol TypeForwardingDefinition: DefinitionType { var implementingTypes: [Any.Type] { get } - func doesImplements(_ type: Any.Type) -> Bool + func doesImplements(type aType: Any.Type) -> Bool } +#if swift(>=3.0) + extension Definition { /** @@ -85,26 +87,15 @@ extension Definition { @discardableResult public func implements(_ a: A.Type, _ b: B.Type, c: C.Type, d: D.Type) -> Definition { return implements(a).implements(b).implements(c).implements(d) } - + } +#endif + extension DependencyContainer { - /** - Registers definition for passed type. - - If instance created by factory of definition, passed as a first parameter, - does not implement type passed in a `type` parameter, - container will throw `DipError.DefinitionNotFound` error when trying to resolve that type. - - - parameters: - - definition: Definition to register - - type: Type to register definition for - - tag: Optional tag to associate definition with. Default is `nil`. - - - returns: New definition registered for passed type. - */ - @discardableResult public func register(_ definition: Definition, type: F.Type, tag: DependencyTagConvertible? = nil) -> Definition { + func _register(definition aDefinition: Definition, type: F.Type, tag: DependencyTagConvertible? = nil) -> Definition { + let definition = aDefinition precondition(definition.container === self, "Definition should be registered in the container.") let key = DefinitionKey(type: F.self, typeOfArguments: U.self) @@ -119,7 +110,7 @@ extension DependencyContainer { return resolved } else { - throw DipError.invalidType(resolved: resolved, key: key.tagged(self.context.tag)) + throw DipError.invalidType(resolved: resolved, key: key.tagged(with: self.context.tag)) } } @@ -131,7 +122,7 @@ extension DependencyContainer { return resolved } else { - throw DipError.invalidType(resolved: resolved, key: key.tagged(self.context.tag)) + throw DipError.invalidType(resolved: resolved, key: key.tagged(with: self.context.tag)) } } }) @@ -143,14 +134,14 @@ extension DependencyContainer { } /// Searches for definition that forwards requested type - func typeForwardingDefinition(_ key: DefinitionKey) -> KeyDefinitionPair? { + func typeForwardingDefinition(forKey key: DefinitionKey) -> KeyDefinitionPair? { var forwardingDefinitions = self.definitions.map({ (key: $0.0, definition: $0.1) }) - forwardingDefinitions = filter(forwardingDefinitions, byKey: key, byTypeOfArguments: true) - forwardingDefinitions = order(forwardingDefinitions, byTag: key.tag) + forwardingDefinitions = filter(definitions: forwardingDefinitions, byKey: key, byTypeOfArguments: true) + forwardingDefinitions = order(definitions: forwardingDefinitions, byTag: key.tag) //we need to carry on original tag - return forwardingDefinitions.first.map({ ($0.key.tagged(key.tag), $0.definition) }) + return forwardingDefinitions.first.map({ ($0.key.tagged(with: key.tag), $0.definition) }) } } diff --git a/Sources/TypeForwarding_swift2.swift b/Sources/TypeForwarding_swift2.swift new file mode 100644 index 0000000..f18832f --- /dev/null +++ b/Sources/TypeForwarding_swift2.swift @@ -0,0 +1,88 @@ +// +// 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. +// + +#if !swift(>=3.0) + +extension Definition { + + /** + Registers definition for passed type. + + If instance created by factory of definition on which method is called + does not implement type passed in a `type` parameter, + container will throw `DipError.DefinitionNotFound` error when trying to resolve that type. + + - parameters: + - type: Type to register definition for + - tag: Optional tag to associate definition with. Default is `nil`. + + - returns: definition on which `implements` was called + */ + public func implements(type: F.Type, tag: DependencyTagConvertible? = nil) -> Definition { + precondition(container != nil, "Definition should be registered in the container.") + + container!.register(self, type: type, tag: tag) + return self + } + + /** + Registers definition for passed type. + + If instance created by factory of definition on which method is called + does not implement type passed in a `type` parameter, + container will throw `DipError.DefinitionNotFound` error when trying to resolve that type. + + - parameters: + - type: Type to register definition for + - tag: Optional tag to associate definition with. Default is `nil`. + - resolvingProperties: Optional block to be called to resolve instance property dependencies + + - returns: definition on which `implements` was called + */ + public func implements(type: F.Type, tag: DependencyTagConvertible? = nil, resolvingProperties: (DependencyContainer, F) throws -> ()) -> Definition { + precondition(container != nil, "Definition should be registered in the container.") + + let forwardDefinition = container!.register(self, type: type, tag: tag) + forwardDefinition.resolvingProperties(resolvingProperties) + return self + } + + ///Registers definition for types passed as parameters + public func implements(a: A.Type, _ b: B.Type) -> Definition { + return implements(a).implements(b) + } + + ///Registers definition for types passed as parameters + public func implements(a: A.Type, _ b: B.Type, _ c: C.Type) -> Definition { + return implements(a).implements(b).implements(c) + } + + ///Registers definition for types passed as parameters + public func implements(a: A.Type, _ b: B.Type, c: C.Type, d: D.Type) -> Definition { + return implements(a).implements(b).implements(c).implements(d) + } + +} + +#endif diff --git a/Sources/Utils.swift b/Sources/Utils.swift index c2a7184..3811e99 100644 --- a/Sources/Utils.swift +++ b/Sources/Utils.swift @@ -30,7 +30,7 @@ public enum LogLevel: Int { public var logLevel: LogLevel = .Errors -func log(_ logLevel: LogLevel, _ message: Any) { +func log(level logLevel: LogLevel, _ message: Any) { guard logLevel.rawValue <= Dip.logLevel.rawValue else { return } print(message) } @@ -42,19 +42,13 @@ protocol BoxType { extension Optional: BoxType { var unboxed: Any? { - switch self { - case let .some(value): return value - default: return nil - } + return self ?? nil } } extension ImplicitlyUnwrappedOptional: BoxType { var unboxed: Any? { - switch self { - case let .some(value): return value - default: return nil - } + return self ?? nil } } @@ -76,7 +70,7 @@ class WeakBox: WeakBoxType { } init(_ value: T) { - #if os(Linux) + #if !_runtime(_ObjC) || !swift(>=3.0) weak var value: AnyObject? = value as? AnyObject #else weak var value: AnyObject? = value as AnyObject @@ -107,25 +101,7 @@ extension Optional { } } -extension Collection where Index: Comparable, Self.Indices.Index == Index { - subscript(safe index: Index) -> Generator.Element? { - guard indices.startIndex.. Generator.Element? { - return self[safe: indices.index(after: index)] - } -} - -#if os(Linux) - - extension String { - public func hasPrefix(_ prefix: String) -> Bool { - return prefix == - String(self.characters.prefix(prefix.characters.count)) - } - } - +#if !_runtime(_ObjC) import Glibc class RecursiveLock { private var _lock = _initializeRecursiveMutex() diff --git a/Tests/DipTests/AutoInjectionTests.swift b/Tests/DipTests/AutoInjectionTests.swift index 7a0a40a..1b78eae 100644 --- a/Tests/DipTests/AutoInjectionTests.swift +++ b/Tests/DipTests/AutoInjectionTests.swift @@ -31,7 +31,7 @@ private protocol Server: class { } private protocol Client: class { - var server: Server! {get} + var server: Server? {get} var anotherServer: Server! {get set} } @@ -56,7 +56,7 @@ private class ClientImp: Client { AutoInjectionTests.serverDidInjectCalled = true } - var server: Server! { + var server: Server? { return _server.value }