Compare commits

...

16 Commits

Author SHA1 Message Date
Ilya Puchka 7af8957a01 Merge pull request #135 from AliSoftware/hotfix/swift2.3-api-diff
Hotfix - swift2.3 api diff
2016-11-01 18:23:04 +04:00
Ilya Puchka 6b68bea55d bumped version to 5.0.4 2016-11-01 14:52:37 +03:00
Ilya Puchka 5b2168ecef fixed broken swift 2.3 api 2016-11-01 14:47:33 +03:00
Ilya Puchka bdf4477774 Merge pull request #131 from AliSoftware/release/5.0.3
Release 5.0.3
2016-10-24 00:53:32 +04:00
Ilya Puchka 84573d967b fixed typo 2016-10-23 22:19:19 +03:00
Ilya Puchka 83511d601b Merge branch 'master' into release/5.0.3 2016-10-21 22:01:23 +04:00
Ilya Puchka b24734e1c9 fixed access levels 2016-10-13 00:04:31 +02:00
Ilya Puchka ab9abbe7ab bumped version to 5.0.3 2016-10-12 23:54:30 +02:00
Ilya Puchka 298cf83be3 updated README and CHANGELOG 2016-10-12 23:54:30 +02:00
Ilya Puchka 455456e817 Merge pull request #127 from AliSoftware/feature/swift-compatibility
Swift 2.3 compatibility
2016-10-12 22:59:02 +02:00
Ilya Puchka 905cbdadb0 swift 2.3 compatibility 2016-10-12 20:47:33 +02:00
Ilya Puchka 945caa451a sligtly improved logging 2016-10-11 13:40:10 +02:00
Ilya Puchka bfacca6fd0 Merge pull request #129 from AliSoftware/features/fixed-reusing-released-weak-singletons
Fixed reusing instances for weak singletons
2016-10-10 15:08:03 +02:00
Ilya Puchka 21673d1f4d added some tests 2016-10-10 15:07:22 +02:00
Ilya Puchka 65175fa372 fixed reusing instances for weak singletons when underlying instance was already released 2016-10-10 14:02:11 +02:00
Ilya Puchka 99fb4ea081 Merge branch 'release/5.0.2' into develop 2016-10-09 11:34:29 +02:00
26 changed files with 2166 additions and 839 deletions
+19
View File
@@ -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
+17
View File
@@ -1,5 +1,22 @@
# CHANGELOG
## 5.0.4
#### Fixed
* Fixed broken compatibility for Swift 2.3 API in `resolve(tag:arguments:)` method.
[#135](https://github.com/AliSoftware/Dip/issues/135), [@ilyapuchka](https://github.com/ilyapuchka)
## 5.0.3
* Added Swift 2.3 compatibility. `swift2.3` brunch is no longer maintained.
[#127](https://github.com/AliSoftware/Dip/issues/127), [@ilyapuchka](https://github.com/ilyapuchka)
#### Fixed
* Fixed reusing instances registered with `WeakSingleton` scope
[#129](https://github.com/AliSoftware/Dip/issues/129), [@ilyapuchka](https://github.com/ilyapuchka)
## 5.0.2
#### Fixed
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Dip"
s.version = "5.0.2"
s.version = "5.0.4"
s.summary = "Dependency Injection for Swift made easy."
s.description = <<-DESC
+50 -2
View File
@@ -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 = "<group>"; };
095F829B1D043B41008CD706 /* TypeForwarding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TypeForwarding.swift; path = ../../Sources/TypeForwarding.swift; sourceTree = "<group>"; };
0982AF0B1C5183A000B62463 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Utils.swift; path = ../../Sources/Utils.swift; sourceTree = "<group>"; };
09871B401DAA6BF300B40B91 /* Compatibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Compatibility.swift; path = ../../Sources/Compatibility.swift; sourceTree = "<group>"; };
09873F551C1E0237000C02F6 /* AutoInjection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutoInjection.swift; path = ../../Sources/AutoInjection.swift; sourceTree = "<group>"; };
09B035FF1C5D2B83001EA5B7 /* AutoWiring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutoWiring.swift; path = ../../Sources/AutoWiring.swift; sourceTree = "<group>"; };
09BD35041D84E30D00B33E53 /* AutoInjectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutoInjectionTests.swift; path = ../../Tests/DipTests/AutoInjectionTests.swift; sourceTree = "<group>"; };
@@ -61,6 +74,17 @@
09BD350B1D84E30D00B33E53 /* ThreadSafetyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ThreadSafetyTests.swift; path = ../../Tests/DipTests/ThreadSafetyTests.swift; sourceTree = "<group>"; };
09BD350C1D84E30D00B33E53 /* TypeForwardingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TypeForwardingTests.swift; path = ../../Tests/DipTests/TypeForwardingTests.swift; sourceTree = "<group>"; };
09BD350D1D84E30D00B33E53 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Utils.swift; path = ../../Tests/DipTests/Utils.swift; sourceTree = "<group>"; };
09FC48041DAA9AC700566AA8 /* Resolve_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Resolve_swift2.swift; path = ../../Sources/Resolve_swift2.swift; sourceTree = "<group>"; };
09FC48051DAA9AC700566AA8 /* Resolve.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Resolve.swift; path = ../../Sources/Resolve.swift; sourceTree = "<group>"; };
09FC480C1DAA9CAF00566AA8 /* Register.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Register.swift; path = ../../Sources/Register.swift; sourceTree = "<group>"; };
09FC480D1DAA9CAF00566AA8 /* Register_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Register_swift2.swift; path = ../../Sources/Register_swift2.swift; sourceTree = "<group>"; };
09FC48121DAA9E0200566AA8 /* AutoInjection_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutoInjection_swift2.swift; path = ../../Sources/AutoInjection_swift2.swift; sourceTree = "<group>"; };
09FC48161DAAA53100566AA8 /* DipError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DipError.swift; path = ../../Sources/DipError.swift; sourceTree = "<group>"; };
09FC48171DAAA53100566AA8 /* DipError_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DipError_swift2.swift; path = ../../Sources/DipError_swift2.swift; sourceTree = "<group>"; };
09FC481A1DAAA82800566AA8 /* RuntimeArguments_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RuntimeArguments_swift2.swift; path = ../../Sources/RuntimeArguments_swift2.swift; sourceTree = "<group>"; };
09FC481C1DAAA8F900566AA8 /* ComponentScope.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ComponentScope.swift; path = ../../Sources/ComponentScope.swift; sourceTree = "<group>"; };
09FC481D1DAAA8F900566AA8 /* ComponentScope_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ComponentScope_swift2.swift; path = ../../Sources/ComponentScope_swift2.swift; sourceTree = "<group>"; };
09FC48201DAAAC4700566AA8 /* TypeForwarding_swift2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TypeForwarding_swift2.swift; path = ../../Sources/TypeForwarding_swift2.swift; sourceTree = "<group>"; };
/* 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;
};
@@ -379,7 +427,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 5.0.2;
CURRENT_PROJECT_VERSION = 5.0.4;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@@ -434,7 +482,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 5.0.2;
CURRENT_PROJECT_VERSION = 5.0.4;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
+16 -5
View File
@@ -5,7 +5,7 @@
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![License](https://img.shields.io/cocoapods/l/Dip.svg?style=flat)](http://cocoapods.org/pods/Dip)
[![Platform](https://img.shields.io/cocoapods/p/Dip.svg?style=flat)](http://cocoapods.org/pods/Dip)
[![Swift Version](https://img.shields.io/badge/Swift-2.2--3.0-F16D39.svg?style=flat)](https://developer.apple.com/swift)
[![Swift Version](https://img.shields.io/badge/Swift-2.3--3.0-F16D39.svg?style=flat)](https://developer.apple.com/swift)
[![Swift Version](https://img.shields.io/badge/Linux-3.0--RELEASE-4BC51D.svg?style=flat)](https://developer.apple.com/swift)
![Animated Dipping GIF](cinnamon-pretzels-caramel-dipping.gif)
@@ -148,16 +148,25 @@ File an issue if you have any question. Pull requests are warmly welcome too.
## Installation
Since version 5.0.0 Dip is built with Swift 3.0. For Swift 2.2-2.3 compatible version use "swift2.3" branch.
You can install Dip using your favorite dependency manager:
Since version 5.0.0 Dip is built with Swift 3.0. You can install Dip using your favorite dependency manager:
<details>
<summary>CocoaPods</summary>
`pod "Dip"`
To build for Swift 2.3 add this code to the bottom of your Podfile
```ruby
pod "Dip"
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '2.3'
end
end
end
```
> You need at least 1.1.0.rc.2 version of CocoaPods.
</details>
@@ -169,6 +178,8 @@ pod "Dip"
github "AliSoftware/Dip"
```
To build for Swift 2.3 run Carthage with `--toolchain com.apple.dt.toolchain.Swift_2_3` option.
</details>
<details>
+82 -40
View File
@@ -27,29 +27,36 @@ extension DependencyContainer {
/**
Resolves properties of passed object wrapped with `Injected<T>` or `InjectedWeak<T>`
*/
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<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox
}
///Wrapped value.
public private(set) var value: T? {
public internal(set) 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<T>: _InjectedPropertyBox<T>, 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<T>: _InjectedPropertyBox<T>, AutoInjectedPropert
return T.self
}
private var valueBox: WeakBox<T>? = nil {
var valueBox: WeakBox<T>? = nil {
didSet {
if let value = value { didInject(value) }
}
@@ -215,6 +242,11 @@ public final class InjectedWeak<T>: _InjectedPropertyBox<T>, 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<T>: _InjectedPropertyBox<T>, 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<T> {
class _InjectedPropertyBox<T> {
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<U>(_ 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<U>(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)
})
}
+98
View File
@@ -0,0 +1,98 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
//
// 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
+6 -5
View File
@@ -30,7 +30,8 @@ protocol AutoWiringDefinition: DefinitionType {
extension DependencyContainer {
/// Tries to resolve instance using auto-wiring
func autowire<T>(_ key: DefinitionKey) throws -> T {
func autowire<T>(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 {
+75
View File
@@ -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
func reversed() -> ReverseRandomAccessCollection<Self> {
return reverse()
}
}
extension SequenceType where Generator.Element == String {
@warn_unused_result
func joined(separator aSeparator: String) -> String {
return joinWithSeparator(aSeparator)
}
}
extension Array {
mutating func append<C : CollectionType where C.Generator.Element == Element>(contentsOf newElements: C) {
appendContentsOf(newElements)
}
mutating func append<S : SequenceType where S.Generator.Element == Element>(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 {
func has(prefix aPrefix: String) -> Bool {
return hasPrefix(aPrefix)
}
}
#else
extension String {
func has(prefix aPrefix: String) -> Bool {
return aPrefix ==
String(self.characters.prefix(aPrefix.characters.count))
}
}
#endif
+124
View File
@@ -0,0 +1,124 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
//
// 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
+129
View File
@@ -0,0 +1,129 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
//
// 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
+83 -130
View File
@@ -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<T, U>: 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<T, U>: 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<T, U>: 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<T, U>: 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 })
}
+80 -468
View File
@@ -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<DependencyContainer>] = []
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<T>(_ closure: () throws -> T) rethrows -> T {
#if swift(>=3.0)
func threadSafe<T>(_ closure: () throws -> T) rethrows -> T {
lock.lock()
defer {
lock.unlock()
}
defer { lock.unlock() }
return try closure()
}
#else
func threadSafe<T>(@noescape closure: () throws -> T) rethrows -> T {
lock.lock()
defer { lock.unlock() }
return try closure()
}
#endif
}
@@ -182,7 +188,7 @@ extension DependencyContainer {
}
public var description: String {
let resolvingDescription = "Resolving type \(key.type) with arguments \(key.typeOfArguments) tagged with \(key.tag.desc)"
let resolvingDescription = "Resolving type \(key.type) with arguments \(key.typeOfArguments) \(key.tag != nil ? "tagged with \(key.tag!)" : "")"
if injectedInProperty != nil {
return "\(resolvingDescription) while auto-injecting property \(injectedInProperty.desc) of \(injectedInType.desc)"
}
@@ -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<T>(_ key: DefinitionKey, injectedInType: Any.Type?, injectedInProperty: String? = nil, logErrors: Bool! = nil, block: () throws -> T) rethrows -> T {
func inContext<T>(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<T>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping () throws -> T) -> Definition<T, ()> {
let definition = DefinitionBuilder<T, ()> {
$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<T, A, B, C, ...>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: Tag? = nil, factory: (A, B, C, ...) throws -> T) -> Definition<T, (A, B, C, ...)> {
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<T, U>(scope: ComponentScope, type: T.Type, tag: DependencyTagConvertible?, factory: @escaping (U) throws -> T, numberOfArguments: Int, autoWiringFactory: @escaping (DependencyContainer, Tag?) throws -> T) -> Definition<T, U> {
let definition = DefinitionBuilder<T, U> {
$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<T, U>(_ definition: Definition<T, U>, 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<T>(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<T, A, B, C, ...>(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<T, U>(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<U>(_ 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<T>(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
//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
}
log(.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(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<T>(_ 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<T>(_ key: DefinitionKey, builder: (_Definition) throws -> T) -> T? {
func collaboratingResolve<T>(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<T, U>(_ definition: Definition<T, U>, 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<T, U>(definition: Definition<T, U>, tag: DependencyTagConvertible? = nil) {
_remove(definition: definition, tag: tag)
}
#endif
func _remove<T, U>(definition aDefinition: Definition<T, U>, 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,12 @@ extension DependencyContainer {
throw error
}
//ignore other errors
catch { log(.Errors, error) }
catch { log(level: .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: return (weakSingletons[key] as? WeakBoxType)?.unboxed ?? 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
}
}
}
}
extension DependencyContainer: CustomStringConvertible {
public var description: String {
@@ -748,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.
@@ -836,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)."
}
}
}
+96
View File
@@ -0,0 +1,96 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
//
// 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
+116
View File
@@ -0,0 +1,116 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
//
// 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
+84
View File
@@ -0,0 +1,84 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
//
// 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<T, U, F>(_ definition: Definition<T, U>, type: F.Type, tag: DependencyTagConvertible? = nil) -> Definition<F, U> {
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<T, U>(_ definition: Definition<T, U>, tag: DependencyTagConvertible? = nil) {
_register(definition: definition, tag: tag)
}
}
#endif
extension DependencyContainer {
func _register<T, U>(definition aDefinition: Definition<T, U>, 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 })
}
}
}
}
+59
View File
@@ -0,0 +1,59 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
//
// 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<T, U, F>(definition: Definition<T, U>, type: F.Type, tag: DependencyTagConvertible? = nil) -> Definition<F, U> {
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<T, U>(definition: Definition<T, U>, tag: DependencyTagConvertible? = nil) {
_register(definition: definition, tag: tag)
}
}
#endif
+293
View File
@@ -0,0 +1,293 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
//
// 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<T>(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<T, A, B, C, ...>(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<T, U>(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<U>(_ 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<T, U>(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<U>(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<T>(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<T>(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
+135
View File
@@ -0,0 +1,135 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
//
// 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<T>(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<T, A, B, C, ...>(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<T, U>(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<U>(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
+221 -132
View File
@@ -22,146 +22,235 @@
// THE SOFTWARE.
//
// MARK: - Register/resolve dependencies with runtime arguments
extension DependencyContainer {
#if swift(>=3.0)
// MARK: 1 Runtime Argument
/**
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<T>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping () throws -> T) -> Definition<T, ()> {
let definition = DefinitionBuilder<T, ()> {
$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<T, A, B, C, ...>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: Tag? = nil, factory: (A, B, C, ...) throws -> T) -> Definition<T, (A, B, C, ...)> {
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<T, U>(scope: ComponentScope, type: T.Type, tag: DependencyTagConvertible?, factory: @escaping (U) throws -> T, numberOfArguments: Int, autoWiringFactory: @escaping (DependencyContainer, Tag?) throws -> T) -> Definition<T, U> {
let definition = DefinitionBuilder<T, U> {
$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<T, A>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A) throws -> T) -> Definition<T, A> {
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<T, A>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A) throws -> T) -> Definition<T, A> {
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`
/**
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`.
- returns: An instance of type `T`.
- seealso: `register(tag:_:factory:)`, `resolve(tag:builder:)`
*/
public func resolve<T, A>(tag: DependencyTagConvertible? = nil, arguments arg1: A) throws -> T {
return try resolve(tag: tag) { factory in try factory(arg1) }
}
- seealso: `register(_:type:tag:factory:)`, `resolve(tag:builder:)`
*/
public func resolve<T, A>(tag: DependencyTagConvertible? = nil, arguments arg1: A) throws -> T {
return try resolve(tag: tag) { factory in try factory(arg1) }
}
///- seealso: `resolve(_:tag:)`, `resolve(tag:withArguments:)`
public func resolve<A>(_ 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:)`, `resolve(tag:arguments:)`
public func resolve<A>(_ 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:)`
@discardableResult public func register<T, A, B>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B) throws -> T) -> Definition<T, (A, B)> {
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: 2 Runtime Arguments
/// - seealso: `register(_:type:tag:factory:)`
@discardableResult public func register<T, A, B>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B) throws -> T) -> Definition<T, (A, B)> {
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: `resolve(tag:arguments:)`
public func resolve<T, A, B>(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<A, B>(_ 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<T, A, B, C>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C) throws -> T) -> Definition<T, (A, B, C)> {
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:arguments:)`
public func resolve<T, A, B, C>(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<A, B, C>(_ 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:)`
@discardableResult public func register<T, A, B, C, D>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C, D) throws -> T) -> Definition<T, (A, B, C, D)> {
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<T, A, B, C, D>(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<A, B, C, D>(_ 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:)`
@discardableResult public func register<T, A, B, C, D, E>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C, D, E) throws -> T) -> Definition<T, (A, B, C, D, E)> {
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<T, A, B, C, D, E>(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<A, B, C, D, E>(_ 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:)`
@discardableResult public func register<T, A, B, C, D, E, F>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C, D, E, F) throws -> T) -> Definition<T, (A, B, C, D, E, F)> {
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<T, A, B, C, D, E, F>(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<A, B, C, D, E, F>(_ 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)) }
}
/// - seealso: `resolve(tag:withArguments:)`
public func resolve<T, A, B>(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<A, B>(_ 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<T, A, B, C>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C) throws -> T) -> Definition<T, (A, B, C)> {
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<T, A, B, C>(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<A, B, C>(_ 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(tag:scope:factory:)`
@discardableResult public func register<T, A, B, C, D>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C, D) throws -> T) -> Definition<T, (A, B, C, D)> {
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:withArguments:)`
public func resolve<T, A, B, C, D>(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:withArguments:)`
public func resolve<A, B, C, D>(_ 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(tag:scope:factory:)`
@discardableResult public func register<T, A, B, C, D, E>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C, D, E) throws -> T) -> Definition<T, (A, B, C, D, E)> {
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:withArguments:)`
public func resolve<T, A, B, C, D, E>(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:withArguments:)`
public func resolve<A, B, C, D, E>(_ 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(tag:scope:factory:)`
@discardableResult public func register<T, A, B, C, D, E, F>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: @escaping (A, B, C, D, E, F) throws -> T) -> Definition<T, (A, B, C, D, E, F)> {
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:withArguments:)`
public func resolve<T, A, B, C, D, E, F>(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:withArguments:)`
public func resolve<A, B, C, D, E, F>(_ 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
+256
View File
@@ -0,0 +1,256 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
//
// 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<T>(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: () throws -> T) -> Definition<T, ()> {
let definition = DefinitionBuilder<T, ()> {
$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<T, A, B, C, ...>(_ scope: ComponentScope = .shared, type: T.Type = T.self, tag: Tag? = nil, factory: (A, B, C, ...) throws -> T) -> Definition<T, (A, B, C, ...)> {
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<T, U>(scope: ComponentScope, type: T.Type, tag: DependencyTagConvertible?, factory: (U) throws -> T, numberOfArguments: Int, autoWiringFactory: (DependencyContainer, Tag?) throws -> T) -> Definition<T, U> {
let definition = DefinitionBuilder<T, U> {
$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<T, A>(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: (A) throws -> T) -> Definition<T, A> {
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<T, A>(tag 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<A>(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<T, A, B>(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: (A, B) throws -> T) -> Definition<T, (A, B)> {
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<T, A, B>(tag 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<A, B>(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<T, A, B, C>(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: (A, B, C) throws -> T) -> Definition<T, (A, B, C)> {
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<T, A, B, C>(tag 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<A, B, C>(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<T, A, B, C, D>(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: (A, B, C, D) throws -> T) -> Definition<T, (A, B, C, D)> {
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<T, A, B, C, D>(tag 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<A, B, C, D>(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<T, A, B, C, D, E>(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: (A, B, C, D, E) throws -> T) -> Definition<T, (A, B, C, D, E)> {
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<T, A, B, C, D, E>(tag 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<A, B, C, D, E>(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<T, A, B, C, D, E, F>(scope: ComponentScope = .Shared, type: T.Type = T.self, tag: DependencyTagConvertible? = nil, factory: (A, B, C, D, E, F) throws -> T) -> Definition<T, (A, B, C, D, E, F)> {
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<T, A, B, C, D, E, F>(tag 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<A, B, C, D, E, F>(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
+14 -23
View File
@@ -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, B, C, D>(_ 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<T, U, F>(_ definition: Definition<T, U>, type: F.Type, tag: DependencyTagConvertible? = nil) -> Definition<F, U> {
func _register<T, U, F>(definition aDefinition: Definition<T, U>, type: F.Type, tag: DependencyTagConvertible? = nil) -> Definition<F, U> {
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) })
}
}
+88
View File
@@ -0,0 +1,88 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
//
// 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<F>(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<F>(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, B>(a: A.Type, _ b: B.Type) -> Definition {
return implements(a).implements(b)
}
///Registers definition for types passed as parameters
public func implements<A, B, C>(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, B, C, D>(a: A.Type, _ b: B.Type, c: C.Type, d: D.Type) -> Definition {
return implements(a).implements(b).implements(c).implements(d)
}
}
#endif
+5 -29
View File
@@ -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<T>: 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..<indices.endIndex ~= index else { return nil }
return self[index]
}
subscript(next index: Index) -> 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()
+2 -2
View File
@@ -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
}
+17 -2
View File
@@ -62,8 +62,9 @@ class ComponentScopeTests: XCTestCase {
("testThatItDoesNotReuseInstanceInSharedScopeResolvedForNilTag", testThatItDoesNotReuseInstanceInSharedScopeResolvedForNilTagWhenResolvingForAnotherTag),
("testThatItReusesInstanceInSharedScopeResolvedForNilTag", testThatItReusesInstanceInSharedScopeResolvedForNilTag),
("testThatItReusesResolvedInstanceWhenResolvingOptional", testThatItReusesResolvedInstanceWhenResolvingOptional),
("testThatItHoldsWeakReferenceToWeakSingletonInstance",
testThatItHoldsWeakReferenceToWeakSingletonInstance)
("testThatItHoldsWeakReferenceToWeakSingletonInstance", testThatItHoldsWeakReferenceToWeakSingletonInstance),
("testThatItResolvesWeakSingletonAgainAfterItWasReleased", testThatItResolvesWeakSingletonAgainAfterItWasReleased),
("testThatCollaboratingContainersReuseSingletonsResolvedByAnotherContainer", testThatCollaboratingContainersReuseSingletonsResolvedByAnotherContainer)
]
}()
@@ -335,6 +336,20 @@ class ComponentScopeTests: XCTestCase {
XCTAssertNil(weakSingleton)
}
func testThatItResolvesWeakSingletonAgainAfterItWasReleased() {
Dip.logLevel = .Verbose
//given
let service = container.register(.weakSingleton) { ServiceImp1() }
container.register(service, type: Service.self)
//when
//resolve and realease reight away
_ = try? container.resolve() as ServiceImp1
//then
AssertNoThrow(expression: try container.resolve() as Service, "Weak singleton should be resolved again.")
}
func testThatCollaboratingContainersReuseSingletonsResolvedByAnotherContainer() {
func test(_ scope: ComponentScope, line: UInt = #line) {
let container1 = DependencyContainer()