Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bdf4477774 | |||
| 84573d967b | |||
| 83511d601b | |||
| b24734e1c9 | |||
| ab9abbe7ab | |||
| 298cf83be3 | |||
| 455456e817 | |||
| 905cbdadb0 | |||
| 945caa451a | |||
| bfacca6fd0 | |||
| 21673d1f4d | |||
| 65175fa372 | |||
| 99fb4ea081 |
+19
@@ -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
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 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
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "Dip"
|
||||
s.version = "5.0.2"
|
||||
s.version = "5.0.3"
|
||||
s.summary = "Dependency Injection for Swift made easy."
|
||||
|
||||
s.description = <<-DESC
|
||||
|
||||
@@ -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.3;
|
||||
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.3;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](http://cocoapods.org/pods/Dip)
|
||||
[](http://cocoapods.org/pods/Dip)
|
||||
[](https://developer.apple.com/swift)
|
||||
[](https://developer.apple.com/swift)
|
||||
[](https://developer.apple.com/swift)
|
||||
|
||||

|
||||
@@ -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
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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)."
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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: 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: 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: 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: 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: 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: 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
|
||||
@@ -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) })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user