Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 142a348b20 | |||
| 88f101639d | |||
| 5e70633ece | |||
| af57aa26ed | |||
| 6d6d77d603 | |||
| 8d2f6ab8fb | |||
| 2cc5310d4b | |||
| 5cb03a11bb | |||
| aff01c541b | |||
| a8dd47cee5 | |||
| 12738a665f | |||
| 9e7bd51bcd | |||
| ce394e7d2b | |||
| cd2c66f4a8 | |||
| 5464f00bad | |||
| 5b9cc50190 | |||
| a7c8616300 | |||
| 8349fbd8d8 | |||
| 4b1ecbcb8e | |||
| 2f0e1fdb10 | |||
| 4a8a1daeca | |||
| 56554147cb | |||
| a554454afe | |||
| dde2e98953 | |||
| a2c04ed61e | |||
| c64cf720b8 | |||
| 3429c6ae0b | |||
| 451fb03bc8 | |||
| b1ad2a65b2 | |||
| 3f8b83b87c | |||
| b7bf4904e9 | |||
| 199b1aec8f | |||
| 22d19697f1 | |||
| d809177273 | |||
| 420c866726 | |||
| 6e6cc0b1df | |||
| b71f50cef5 | |||
| 462d528474 | |||
| 01c318bd5c | |||
| a0c890c931 | |||
| 9b2a7918d8 | |||
| 218b5a4e9a | |||
| 0b221f3368 | |||
| 3b943c10ef | |||
| a1da60bd14 | |||
| 1825cd7d4c | |||
| e7d8fb41e1 | |||
| 1c398defeb | |||
| ab561546f0 | |||
| c0fb925ab8 | |||
| 80ee4865ce | |||
| d95df1343e | |||
| a83d866cbd | |||
| cc1dcba4b9 | |||
| 0ceb6a3503 | |||
| 4e641a6465 | |||
| 35b6da8556 | |||
| 47bc1913e3 | |||
| 24d341503e | |||
| 3c1331089b | |||
| 7af8957a01 | |||
| 6b68bea55d | |||
| 5b2168ecef | |||
| 5eb0eece56 | |||
| bdf4477774 | |||
| 84573d967b | |||
| 83511d601b | |||
| b24734e1c9 | |||
| ab9abbe7ab | |||
| 298cf83be3 | |||
| 455456e817 | |||
| 905cbdadb0 | |||
| 945caa451a | |||
| bfacca6fd0 | |||
| 21673d1f4d | |||
| 65175fa372 | |||
| 99fb4ea081 | |||
| a1ece4b0ab | |||
| 0b00e13e00 | |||
| 4aec626a1f | |||
| d477392deb | |||
| 3b23dc1a6f | |||
| 1d7d0052bf | |||
| b1fee5c1db | |||
| e19b65d9c2 | |||
| 86bb28da14 | |||
| e4545e3ed3 | |||
| 52455e663c | |||
| 7a10d1e46e | |||
| e95df85503 | |||
| a028c05271 |
@@ -0,0 +1 @@
|
||||
4.1
|
||||
+10
-13
@@ -1,7 +1,3 @@
|
||||
env:
|
||||
global:
|
||||
- MODULE_NAME=Dip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- os: linux
|
||||
@@ -9,16 +5,15 @@ matrix:
|
||||
- script:
|
||||
- set -o pipefail && xcodebuild test -workspace Dip.xcworkspace -scheme Dip -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' ONLY_ACTIVE_ARCH=NO | xcpretty -c
|
||||
- set -o pipefail && xcodebuild test -workspace Dip.xcworkspace -scheme Dip -sdk macosx -destination 'platform=macOS,arch=x86_64' ONLY_ACTIVE_ARCH=NO | xcpretty -c
|
||||
- set -o pipefail && xcodebuild test -workspace Dip.xcworkspace -scheme Dip -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV 1080p,OS=latest' ONLY_ACTIVE_ARCH=NO | xcpretty -c
|
||||
- set -o pipefail && xcodebuild -workspace Dip.xcworkspace -scheme Dip -sdk watchsimulator -destination 'platform=watchOS Simulator,name=Apple Watch - 38mm,OS=latest' ONLY_ACTIVE_ARCH=NO | xcpretty - c
|
||||
- set -o pipefail && xcodebuild test -workspace Dip.xcworkspace -scheme DipSampleApp -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' ONLY_ACTIVE_ARCH=NO | xcpretty -c
|
||||
- pod lib lint --quick
|
||||
- pod spec lint --allow-warnings
|
||||
- carthage build --no-skip-current
|
||||
os: osx
|
||||
osx_image: xcode8
|
||||
osx_image: xcode9.3
|
||||
language: objective-c
|
||||
before_install:
|
||||
- gem install cocoapods --version 1.1.0.rc.2 --no-document
|
||||
- script:
|
||||
- swift build --clean && swift build && swift test
|
||||
- swift package clean && swift build && swift test
|
||||
os: linux
|
||||
dist: trusty
|
||||
sudo: required
|
||||
@@ -26,11 +21,13 @@ matrix:
|
||||
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
|
||||
- export SWIFT_VERSION=swift-4.1-RELEASE
|
||||
- wget https://swift.org/builds/swift-4.1-release/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}"
|
||||
- cd $MODULE_NAME
|
||||
- cd Dip
|
||||
- script:
|
||||
- swift package clean && swift build && swift test
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
@@ -1,5 +1,63 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 6.1
|
||||
|
||||
* Swift 4.1 support
|
||||
* Fixed crashes resolving singletons using collaborating containers
|
||||
[#179](https://github.com/AliSoftware/Dip/pull/179), [@trimmurrti](https://github.com/trimmurrti)
|
||||
[#182](https://github.com/AliSoftware/Dip/pull/182), [@tapsandswipes](https://github.com/tapsandswipes)
|
||||
|
||||
## 6.0
|
||||
|
||||
* Swift 4 support
|
||||
* Fixed unneeded reuse of singletons in collaborating containers.
|
||||
Containers now first attempt to autowire components and fallback to collaboration when it fails.
|
||||
[#169](https://github.com/AliSoftware/Dip/issues/169), [@ilyapuchka](https://github.com/ilyapuchka)
|
||||
|
||||
## 5.1
|
||||
|
||||
* Dropped Swift 2.3 support.
|
||||
[#150](https://github.com/AliSoftware/Dip/issues/150), [@ilyapuchka](https://github.com/ilyapuchka)
|
||||
* Added custom logging function.
|
||||
[#146](https://github.com/AliSoftware/Dip/issues/146), [@Pr0Ger](https://github.com/Pr0Ger)
|
||||
|
||||
#### Fixed
|
||||
|
||||
* Fixed Swift 3.1 warnings.
|
||||
[#145](https://github.com/AliSoftware/Dip/issues/145), [@DenHeadless](https://github.com/DenHeadless)
|
||||
* Fixed collaboration shared references.
|
||||
[#151](https://github.com/AliSoftware/Dip/issues/151), [@ilyapuchka](https://github.com/ilyapuchka)
|
||||
* Fixed autowiring when using tags.
|
||||
[#154](https://github.com/AliSoftware/Dip/issues/154), [@ilyapuchka](https://github.com/ilyapuchka)
|
||||
|
||||
## 5.0.4
|
||||
|
||||
#### Fixed
|
||||
|
||||
* Fixed broken compatibility for Swift 2.3 API in `resolve(tag:arguments:)` method.
|
||||
[#135](https://github.com/AliSoftware/Dip/issues/135), [@ilyapuchka](https://github.com/ilyapuchka)
|
||||
|
||||
## 5.0.3
|
||||
|
||||
* Added Swift 2.3 compatibility. `swift2.3` brunch is no longer maintained.
|
||||
[#127](https://github.com/AliSoftware/Dip/issues/127), [@ilyapuchka](https://github.com/ilyapuchka)
|
||||
|
||||
#### Fixed
|
||||
|
||||
* Fixed reusing instances registered with `WeakSingleton` scope
|
||||
[#129](https://github.com/AliSoftware/Dip/issues/129), [@ilyapuchka](https://github.com/ilyapuchka)
|
||||
|
||||
## 5.0.2
|
||||
|
||||
#### Fixed
|
||||
|
||||
* Fixed Swift 3 issues related to reflection and IUO
|
||||
[#125](https://github.com/AliSoftware/Dip/issues/125), [@ilyapuchka](https://github.com/ilyapuchka)
|
||||
|
||||
## 5.0.1
|
||||
|
||||
This release is the same as 5.0.0 and only fixes CocoaPods speck pushed to trunk without macOS, tvOS and watchOS deployment targets. Please use this release instead of 5.0.0 if you integrate Dip via Cocoapods.
|
||||
|
||||
## 5.0.0
|
||||
|
||||
* Migrated to Swift 3.0
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "Dip"
|
||||
s.version = "5.0.0"
|
||||
s.version = "6.1"
|
||||
s.summary = "Dependency Injection for Swift made easy."
|
||||
|
||||
s.description = <<-DESC
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -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,10 @@
|
||||
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 */; };
|
||||
09FC48071DAA9AC700566AA8 /* Resolve.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC48051DAA9AC700566AA8 /* Resolve.swift */; };
|
||||
09FC480F1DAA9CAF00566AA8 /* Register.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC480C1DAA9CAF00566AA8 /* Register.swift */; };
|
||||
09FC48181DAAA53100566AA8 /* DipError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC48161DAAA53100566AA8 /* DipError.swift */; };
|
||||
09FC481E1DAAA8F900566AA8 /* ComponentScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FC481C1DAAA8F900566AA8 /* ComponentScope.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -49,6 +54,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 +67,10 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
09FC48161DAAA53100566AA8 /* DipError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DipError.swift; path = ../../Sources/DipError.swift; sourceTree = "<group>"; };
|
||||
09FC481C1DAAA8F900566AA8 /* ComponentScope.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ComponentScope.swift; path = ../../Sources/ComponentScope.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -87,12 +97,17 @@
|
||||
children = (
|
||||
0919F4C91C16417000DC3B10 /* Dip.h */,
|
||||
0919F4CA1C16417000DC3B10 /* Dip.swift */,
|
||||
09FC48161DAAA53100566AA8 /* DipError.swift */,
|
||||
09FC480C1DAA9CAF00566AA8 /* Register.swift */,
|
||||
09FC48051DAA9AC700566AA8 /* Resolve.swift */,
|
||||
0919F4C81C16417000DC3B10 /* Definition.swift */,
|
||||
09FC481C1DAAA8F900566AA8 /* ComponentScope.swift */,
|
||||
0919F4CC1C16417000DC3B10 /* RuntimeArguments.swift */,
|
||||
09873F551C1E0237000C02F6 /* AutoInjection.swift */,
|
||||
09B035FF1C5D2B83001EA5B7 /* AutoWiring.swift */,
|
||||
095F829B1D043B41008CD706 /* TypeForwarding.swift */,
|
||||
0982AF0B1C5183A000B62463 /* Utils.swift */,
|
||||
09871B401DAA6BF300B40B91 /* Compatibility.swift */,
|
||||
0919F4CB1C16417000DC3B10 /* Info.plist */,
|
||||
);
|
||||
path = Dip;
|
||||
@@ -193,7 +208,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0730;
|
||||
LastUpgradeCheck = 0800;
|
||||
LastUpgradeCheck = 0930;
|
||||
ORGANIZATIONNAME = AliSoftware;
|
||||
TargetAttributes = {
|
||||
0903B3571C161543002241C1 = {
|
||||
@@ -246,12 +261,17 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
09871B411DAA6BF300B40B91 /* Compatibility.swift in Sources */,
|
||||
0982AF0C1C5183A000B62463 /* Utils.swift in Sources */,
|
||||
0919F4D51C16417B00DC3B10 /* Definition.swift in Sources */,
|
||||
09FC481E1DAAA8F900566AA8 /* ComponentScope.swift in Sources */,
|
||||
09B036001C5D2B83001EA5B7 /* AutoWiring.swift in Sources */,
|
||||
0919F4D41C16417B00DC3B10 /* Dip.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 */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -287,6 +307,7 @@
|
||||
0903B3691C161544002241C1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=*]" = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
@@ -310,6 +331,7 @@
|
||||
0903B36A1C161544002241C1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=*]" = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
@@ -364,20 +386,28 @@
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 5.0.0;
|
||||
CURRENT_PROJECT_VERSION = 6.1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
@@ -402,7 +432,7 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SUPPORTED_PLATFORMS = "macosx watchsimulator iphonesimulator appletvsimulator watchos appletvos iphoneos";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,3,4";
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -419,20 +449,28 @@
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 5.0.0;
|
||||
CURRENT_PROJECT_VERSION = 6.1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -450,7 +488,7 @@
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SUPPORTED_PLATFORMS = "macosx watchsimulator iphonesimulator appletvsimulator watchos appletvos iphoneos";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,3,4";
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "0930"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -40,8 +40,8 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
codeCoverageEnabled = "YES"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<string>6.1</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<string>6.1</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,176 +1,2 @@
|
||||
[
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 54,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 111,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 168,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 228,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 269,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 332,
|
||||
"remove": 3,
|
||||
"text": " ",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 472,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 508,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 613,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 649,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 754,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 808,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 879,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 924,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 1092,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 1149,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 1208,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 1265,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 1473,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 1516,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 1569,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 1643,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 1687,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 1762,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 1806,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 1848,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 2146,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 2315,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
},
|
||||
{
|
||||
"file": "/Users/ilya/Documents/Developer/iPhoneProjects/Dip/DipPlayground.playground/Sources/Models.swift",
|
||||
"offset": 2357,
|
||||
"remove": 6,
|
||||
"text": "open",
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -113,10 +113,7 @@ class ServerImp: Server {
|
||||
weak var client: ServerClient?
|
||||
}
|
||||
|
||||
//There is currently a bug in Swift that causes runtime crash
|
||||
//when trying to auto-inject not-NSObject weak property.
|
||||
//https://bugs.swift.org/browse/SR-2144
|
||||
class ServerClientImp: NSObject, ServerClient {
|
||||
class ServerClientImp: ServerClient {
|
||||
var server: Server?
|
||||
|
||||
init(server: Server) {
|
||||
@@ -149,10 +146,7 @@ class InjectedServerImp: Server {
|
||||
var client: ServerClient? { return injectedClient.value }
|
||||
}
|
||||
|
||||
//There is currently a bug in Swift that causes runtime crash
|
||||
//when trying to auto-inject not-NSObject weak property.
|
||||
//https://bugs.swift.org/browse/SR-2144
|
||||
class InjectedClientImp: NSObject, ServerClient {
|
||||
class InjectedClientImp: ServerClient {
|
||||
private var injectedServer = Injected<Server>()
|
||||
var server: Server? { get { return injectedServer.value } }
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ Dip supports three different scopes of objects: _Unique_, _Shared_ and _Singleto
|
||||
* The `Unique` scope will make the `DependencyContainer` resolve your type as __a new instance every time__ you call `resolve`. This is the default scope.
|
||||
* The `Shared` scope is like `Unique` scope, but it will make the `DependencyContainer` to reuse resolved instances during one (recursive) call to `resolve` method. When this call returns, all resolved instances will be discarded and next call to `resolve` will produce new instances. This scope should be used to resolve [circular dependencies](Circular%20dependencies).
|
||||
* The `Singleton` scope will make the `DependencyContainer` retain the instance once resolved the first time, and reuse it in the next calls to `resolve` during the container lifetime.
|
||||
* The `EagerSingleton` scope is the same as `Singleton` scope but instances with this cope will be created when you call `bootstrap()` method on the container.
|
||||
* The `EagerSingleton` scope is the same as `Singleton` scope but instances with this scope will be created when you call `bootstrap()` method on the container.
|
||||
* The `WeakSingleton` scope is the same as `Singleton` scope but instances are stored in container as weak references. This scope can be usefull when you need to recreate object graph without reseting container.
|
||||
|
||||
The `Unique` scope is the default. To set a scope you pass it as an argument to `register` method.
|
||||
|
||||
@@ -107,7 +107,7 @@ viewController = try MyViewController(apiClient: container.resolve())
|
||||
viewController.apiClient = try container.resolve()
|
||||
|
||||
/*:
|
||||
Of cource `DependencyContainer` should not be a singleton too. There is just no need for that because you never should call `DependencyContainer` from inside of your components. That will make it a [service locator antipatter]((http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/)). You may only call `DependencyContainer` from the _Composition root_ - the place where all the components are configured and wired together.
|
||||
Of cource `DependencyContainer` should not be a singleton too. There is just no need for that because you never should call `DependencyContainer` from inside of your components. That will make it a [service locator antipattern]((http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/)). You may only call `DependencyContainer` from the _Composition root_ - the place where all the components are configured and wired together.
|
||||
|
||||
Dependency Injection is a pattern (more precisely - a set of patterns) as well as a singleton. And any pattern can be abused. DI can be used in a [wrong way]((http://www.loosecouplings.com/2011/01/dependency-injection-using-di-container.html)), container can easily become a service locator. You should carefully decide when to use DI, you should not inject everything and everywhere and define a protocol for every single class you use. For every tool there is a right time and the same way as singleton can harm you the same way DI and protocols abuse can make your code unnececerry complex.
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
[](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)
|
||||
[](https://developer.apple.com/swift)
|
||||
|
||||

|
||||
_Photo courtesy of [www.kevinandamanda.com](http://www.kevinandamanda.com/recipes/appetizer/homemade-soft-cinnamon-sugar-pretzel-bites-with-salted-caramel-dipping-sauce.html)_
|
||||
@@ -17,46 +17,12 @@ _Photo courtesy of [www.kevinandamanda.com](http://www.kevinandamanda.com/recipe
|
||||
|
||||
It's aimed to be as simple as possible yet provide rich functionality usual for DI containers on other platforms. It's inspired by `.NET`'s [Unity Container](https://msdn.microsoft.com/library/ff647202.aspx) and other DI containers.
|
||||
|
||||
* You start by creating `let container = DependencyContainer()` and **registering your dependencies, by associating a _protocol_ or _type_ to a `factory`**.
|
||||
* Then you can call `container.resolve()` to **resolve an instance of _protocol_ or _type_** using that `DependencyContainer`.
|
||||
* You start by creating `let container = DependencyContainer()` and **registering your dependencies, by associating a _protocol_ or _type_ to a `factory`** using `container.register { MyService() as Service }`.
|
||||
* Then you can call `container.resolve() as Service` to **resolve an instance of _protocol_ or _type_** using that `DependencyContainer`.
|
||||
* You can easily use Dip along with **Storyboards and Nibs** - checkout **[Dip-UI](https://github.com/AliSoftware/Dip-UI)** extensions. There is also a **[code generator](https://github.com/ilyapuchka/dipgen)** that can help to simplify registering new components.
|
||||
|
||||
> You can easily use Dip along with Storyboards and Nibs - checkout [Dip-UI](https://github.com/AliSoftware/Dip-UI) extensions.
|
||||
|
||||
## Documentation & Usage Examples
|
||||
|
||||
Dip is completely [documented](http://cocoadocs.org/docsets/Dip/5.0.0/) and comes with a Playground that lets you try all its features and become familiar with API. You can find it in `Dip.xcworkspace`.
|
||||
|
||||
> Note: it may happen that you will need to build Dip framework before playground will be able to use it. For that select `Dip-iOS` scheme and build.
|
||||
|
||||
You can find bunch of usage examples in a [wiki](../../wiki).
|
||||
|
||||
If your are using [VIPER](https://www.objc.io/issues/13-architecture/viper/) architecture - [here](https://github.com/ilyapuchka/VIPER-SWIFT) is VIPER demo app that uses Dip instead of manual dependency injection.
|
||||
|
||||
There are also several blog posts that describe how to use Dip and some of its implementation details:
|
||||
|
||||
- [IoC container in Swift](http://ilya.puchka.me/ioc-container-in-swift/)
|
||||
- [IoC container in Swift. Circular dependencies and auto-injection](http://ilya.puchka.me/ioc-container-in-swift-circular-dependencies-and-auto-injection/)
|
||||
- [Dependency injection with Dip](http://ilya.puchka.me/dependency-injecinjection-with-dip/)
|
||||
|
||||
File an issue if you have any question.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **[Scopes](../../wiki/scopes)**. Dip supports 5 different scopes (or life cycle strategies): _Unique_, _Shared_, _Singleton_, _EagerSingleton_, _WeakSingleton_;
|
||||
- **[Named definitions](../../wiki/named-definitions)**. You can register different factories for the same protocol or type by registering them with [tags]();
|
||||
- **[Runtime arguments](../../wiki/runtime-arguments)**. You can register factories that accept up to 6 runtime arguments;
|
||||
- **[Circular dependencies](../../wiki/circular-dependencies)**. Dip can resolve circular dependencies;
|
||||
- **[Auto-wiring](../../wiki/auto-wiring)** & **[Auto-injection](../../wiki/auto-injection)**. Dip can infer your components' dependencies injected in constructor and automatically resolve them as well as dependencies injected with properties.
|
||||
- **[Resolving optionals](../../wiki/resolving-optionals)**. Dip is able to resolve constructor or property dependencies defined as optionals.
|
||||
- **[Type forwarding](../../wiki/type-forwarding)**. You can register the same factory to resolve different types.
|
||||
- **[Storyboards integration](../../wiki/storyboards-integration)**. You can easily use Dip along with storyboards and Xibs without ever referencing container in your view controller's code;
|
||||
- **Weakly typed components**. Dip can resolve weak types when they are unknown at compile time.
|
||||
- **[Easy configuration](../../wiki/containers-collaboration)**. No complex container hierarchy, no unneeded functionality;
|
||||
- **Thread safety**. Registering and resolving components is thread safe;
|
||||
- **Helpful error messages and configuration validation**. You can validate your container configuration. If something can not be resolved at runtime Dip throws an error that completely describes the issue;
|
||||
|
||||
## Basic usage
|
||||
<details>
|
||||
<summary>Basic usage</summary>
|
||||
|
||||
```swift
|
||||
import Dip
|
||||
@@ -82,7 +48,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
```
|
||||
|
||||
## More sophisticated example
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>More sophisticated example</summary>
|
||||
|
||||
```swift
|
||||
import Dip
|
||||
@@ -100,6 +69,9 @@ extension DependencyContainer {
|
||||
|
||||
static func configure() -> DependencyContainer {
|
||||
return DependencyContainer { container in
|
||||
unowned let container = container
|
||||
DependencyContainer.uiContainers = [container]
|
||||
|
||||
container.register(tag: "ViewController") { ViewController() }
|
||||
.resolvingProperties { container, controller in
|
||||
controller.animationsFactory = try container.resolve() as AnimatonsFactory
|
||||
@@ -137,49 +109,95 @@ class ViewController {
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Documentation & Usage Examples
|
||||
|
||||
Dip is completely [documented](http://cocoadocs.org/docsets/Dip/5.0.0/) and comes with a Playground that lets you try all its features and become familiar with API. You can find it in `Dip.xcworkspace`.
|
||||
|
||||
> Note: it may happen that you will need to build Dip framework before playground will be able to use it. For that select `Dip` scheme and build for iPhone Simulator.
|
||||
|
||||
You can find bunch of usage examples and usfull tips in a [wiki](../../wiki).
|
||||
|
||||
If your are using [VIPER](https://www.objc.io/issues/13-architecture/viper/) architecture - [here](https://github.com/ilyapuchka/VIPER-SWIFT) is VIPER demo app that uses Dip instead of manual dependency injection.
|
||||
|
||||
There are also several blog posts that describe how to use Dip and some of its implementation details:
|
||||
|
||||
- [IoC container in Swift](http://ilya.puchka.me/ioc-container-in-swift/)
|
||||
- [IoC container in Swift. Circular dependencies and auto-injection](http://ilya.puchka.me/ioc-container-in-swift-circular-dependencies-and-auto-injection/)
|
||||
- [Dependency injection with Dip](http://ilya.puchka.me/dependency-injecinjection-with-dip/)
|
||||
|
||||
File an issue if you have any question. Pull requests are warmly welcome too.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **[Scopes](../../wiki/scopes)**. Dip supports 5 different scopes (or life cycle strategies): _Unique_, _Shared_, _Singleton_, _EagerSingleton_, _WeakSingleton_;
|
||||
- **[Auto-wiring](../../wiki/auto-wiring)** & **[Auto-injection](../../wiki/auto-injection)**. Dip can infer your components' dependencies injected in constructor and automatically resolve them as well as dependencies injected with properties.
|
||||
- **[Resolving optionals](../../wiki/resolving-optionals)**. Dip is able to resolve constructor or property dependencies defined as optionals.
|
||||
- **[Type forwarding](../../wiki/type-forwarding)**. You can register the same factory to resolve different types implemeted by a single class.
|
||||
- **[Circular dependencies](../../wiki/circular-dependencies)**. Dip will be able to resolve circular dependencies if you will follow some simple rules;
|
||||
- **[Storyboards integration](../../wiki/storyboards-integration)**. You can easily use Dip along with storyboards and Xibs without ever referencing container in your view controller's code;
|
||||
- **[Named definitions](../../wiki/named-definitions)**. You can register different factories for the same protocol or type by registering them with [tags]();
|
||||
- **[Runtime arguments](../../wiki/runtime-arguments)**. You can register factories that accept up to 6 runtime arguments (and extend it if you need);
|
||||
- **[Easy configuration](../../wiki/containers-collaboration)** & **Code generation**. No complex containers hierarchy, no unneeded functionality. Tired of writing all registrations by hand? There is a [cool code generator](https://github.com/ilyapuchka/dipgen) that will create them for you. The only thing you need is to annotate your code with some comments.
|
||||
- **Weakly typed components**. Dip can resolve "weak" types when they are unknown at compile time.
|
||||
- **Thread safety**. Registering and resolving components is thread safe;
|
||||
- **Helpful error messages and configuration validation**. You can validate your container configuration. If something can not be resolved at runtime Dip throws an error that completely describes the issue;
|
||||
|
||||
|
||||
## 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.
|
||||
Since version 5.0.0 Dip is built with Swift 3.0. You can install Dip using your favorite dependency manager:
|
||||
|
||||
Dip is available through [CocoaPods](http://cocoapods.org). To install
|
||||
it, simply add the following line to your Podfile:
|
||||
<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
|
||||
```
|
||||
|
||||
If you use [Carthage](https://github.com/Carthage/Carthage) add this line to your Cartfile:
|
||||
> You need at least 1.1.0.rc.2 version of CocoaPods.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Carthage</summary>
|
||||
|
||||
```
|
||||
github "AliSoftware/Dip"
|
||||
```
|
||||
|
||||
If you use [Swift Package Manager](https://swift.org/package-manager/) add Dip as dependency to you `Package.swift`:
|
||||
To build for Swift 2.3 run Carthage with `--toolchain com.apple.dt.toolchain.Swift_2_3` option.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Swift Package Manager</summary>
|
||||
|
||||
```swift
|
||||
let package = Package(
|
||||
name: "MyPackage",
|
||||
dependencies: [
|
||||
.Package(url: "https://github.com/AliSoftware/Dip.git", "5.0.0")
|
||||
]
|
||||
)
|
||||
.Package(url: "https://github.com/AliSoftware/Dip", majorVersion: 5, minor: 0)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Running tests
|
||||
|
||||
On OSX you can run tests from Xcode. On Linux you need to have Swift Package Manager installed and use it to build and run tests:
|
||||
|
||||
```
|
||||
cd Dip
|
||||
swift build && swift test
|
||||
```
|
||||
|
||||
> Note: Swift Package Manager is destributed with Swift development snapshots only, so it builds packages using Swift 3. To build Dip you will need to build it with Swift 2.2, for that you need to set [`$SWIFT_EXEC`](https://github.com/apple/swift-package-manager#choosing-swift-version) environment variable.
|
||||
On OSX you can run tests from Xcode. On Linux you need to have Swift Package Manager installed and use it to build and run tests using this command: `swift build --clean && swift build && swift test`
|
||||
|
||||
## Credits
|
||||
|
||||
This library has been created by [**Olivier Halligon**](olivier@halligon.net).
|
||||
I'd also like to thank [**Ilya Puchka**](https://twitter.com/ilyapuchka) for his big contribution to it, as he added a lot of great features to it.
|
||||
This library has been created by [**Olivier Halligon**](olivier@halligon.net) and is maintained by [**Ilya Puchka**](https://twitter.com/ilyapuchka).
|
||||
|
||||
**Dip** is available under the **MIT license**. See the `LICENSE` file for more info.
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0700;
|
||||
LastUpgradeCheck = 0800;
|
||||
LastUpgradeCheck = 0930;
|
||||
ORGANIZATIONNAME = AliSoftware;
|
||||
TargetAttributes = {
|
||||
0990225E1BC123C000E76F43 = {
|
||||
@@ -427,13 +427,23 @@
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -461,7 +471,7 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -473,13 +483,23 @@
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -498,7 +518,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 4.0;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "0930"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -40,8 +40,8 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
codeCoverageEnabled = "YES"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
@@ -31,6 +41,16 @@
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
@@ -62,6 +82,16 @@
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-152.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "83.5x83.5",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"size" : "1024x1024",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
//
|
||||
// BaseCell.swift
|
||||
// Dip
|
||||
//
|
||||
// Created by Olivier Halligon on 10/09/2015.
|
||||
// Copyright © 2015 AliSoftware. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
protocol BaseCell {
|
||||
static var identifier: String { get }
|
||||
static var nib: UINib? { get }
|
||||
|
||||
static func register(_ tableView: UITableView)
|
||||
static func dequeueFromTableView(_ tableView: UITableView, forIndexPath indexPath: IndexPath) -> Self
|
||||
}
|
||||
|
||||
extension BaseCell where Self : UITableViewCell {
|
||||
static var identifier: String {
|
||||
return "\(Self.self)"
|
||||
}
|
||||
static var nib: UINib? { return nil }
|
||||
|
||||
static func register(_ tableView: UITableView) {
|
||||
if let cellNib = self.nib {
|
||||
tableView.register(cellNib, forCellReuseIdentifier: identifier)
|
||||
} else {
|
||||
tableView.register(Self.self as AnyClass, forCellReuseIdentifier: identifier)
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
static func dequeueFromTableView(tableView: UITableView, forIndexPath indexPath: NSIndexPath) -> Self {
|
||||
=======
|
||||
static func dequeueFromTableView(_ tableView: UITableView, forIndexPath indexPath: IndexPath) -> Self {
|
||||
>>>>>>> feature/swift3
|
||||
return tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as! Self
|
||||
}
|
||||
}
|
||||
|
||||
protocol FillableCell: BaseCell {
|
||||
associatedtype ObjectType
|
||||
func fillWithObject(object: ObjectType)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
//
|
||||
// UserCell.swift
|
||||
// Dip
|
||||
//
|
||||
// Created by Olivier Halligon on 10/09/2015.
|
||||
// Copyright © 2015 AliSoftware. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class PersonCell : UITableViewCell, FillableCell {
|
||||
@IBOutlet weak var nameLabel: UILabel!
|
||||
@IBOutlet weak var genderImageView: UIImageView!
|
||||
@IBOutlet weak var heightLabel: UILabel!
|
||||
@IBOutlet weak var massLabel: UILabel!
|
||||
@IBOutlet weak var hairLabel: UILabel!
|
||||
@IBOutlet weak var eyesLabel: UILabel!
|
||||
|
||||
<<<<<<< HEAD
|
||||
let heightFormatter: NSLengthFormatter = {
|
||||
let f = NSLengthFormatter()
|
||||
f.isForPersonHeightUse = true
|
||||
return f
|
||||
}()
|
||||
let massFormatter: NSMassFormatter = {
|
||||
let f = NSMassFormatter()
|
||||
=======
|
||||
let heightFormatter: LengthFormatter = {
|
||||
let f = LengthFormatter()
|
||||
f.isForPersonHeightUse = true
|
||||
return f
|
||||
}()
|
||||
let massFormatter: MassFormatter = {
|
||||
let f = MassFormatter()
|
||||
>>>>>>> feature/swift3
|
||||
f.isForPersonMassUse = true
|
||||
return f
|
||||
}()
|
||||
|
||||
func fillWithObject(object person: Person) {
|
||||
nameLabel.text = person.name
|
||||
genderImageView.image = person.gender.flatMap { UIImage(named: $0.rawValue) }
|
||||
heightLabel.text = heightFormatter.string(fromValue: Double(person.height), unit: .centimeter)
|
||||
massLabel.text = massFormatter.string(fromValue: Double(person.mass), unit: .kilogram)
|
||||
hairLabel.text = person.hairColor
|
||||
eyesLabel.text = person.eyeColor
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<string>6.1</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
//
|
||||
// PlistPersonProvider.swift
|
||||
// Dip
|
||||
//
|
||||
// Created by Ilya Puchka on 12/09/2015.
|
||||
// Copyright © 2015 AliSoftware. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
///Provides some dummy Person entities
|
||||
struct DummyPilotProvider : PersonProviderAPI {
|
||||
|
||||
func fetchIDs(completion: ([Int]) -> Void) {
|
||||
completion(Array(0..<5))
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func fetch(id: Int, completion: Person? -> Void) {
|
||||
=======
|
||||
func fetch(id: Int, completion: (Person?) -> Void) {
|
||||
>>>>>>> feature/swift3
|
||||
completion(dummyPerson(idx: id))
|
||||
}
|
||||
|
||||
private func dummyPerson(idx: Int) -> Person {
|
||||
let colors = ["blue", "brown", "yellow", "orange", "red", "dark"]
|
||||
let genders: [Gender?] = [Gender.Male, Gender.Female, nil]
|
||||
return Person(
|
||||
name: "John Dummy Doe #\(idx)",
|
||||
height: 150 + (idx*27%40),
|
||||
mass: 50 + (idx*7%30),
|
||||
hairColor: colors[idx*3%colors.count],
|
||||
eyeColor: colors[idx*2%colors.count],
|
||||
gender: genders[idx%3],
|
||||
starshipIDs: [idx % 3, 2*idx % 4]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
///Provides Person entities reading then from plist file
|
||||
class PlistPersonProvider : PersonProviderAPI {
|
||||
let people: [Person]
|
||||
|
||||
init(plist basename: String) {
|
||||
guard
|
||||
<<<<<<< HEAD
|
||||
let path = NSBundle.main().pathForResource(basename, ofType: "plist"),
|
||||
=======
|
||||
let path = Bundle.main().pathForResource(basename, ofType: "plist"),
|
||||
>>>>>>> feature/swift3
|
||||
let list = NSArray(contentsOfFile: path),
|
||||
peopleDict = list as? [[String:AnyObject]]
|
||||
else {
|
||||
fatalError("PLIST for \(basename) not found")
|
||||
}
|
||||
|
||||
self.people = peopleDict.map(PlistPersonProvider.personFromDict)
|
||||
}
|
||||
|
||||
func fetchIDs(completion: ([Int]) -> Void) {
|
||||
completion(Array(0..<people.count))
|
||||
}
|
||||
|
||||
func fetch(id: Int, completion: (Person?) -> Void) {
|
||||
guard id < people.count else {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
completion(people[id])
|
||||
}
|
||||
|
||||
private static func personFromDict(dict: [String:AnyObject]) -> Person {
|
||||
guard
|
||||
let name = dict["name"] as? String,
|
||||
height = dict["height"] as? Int,
|
||||
mass = dict["mass"] as? Int,
|
||||
hairColor = dict["hairColor"] as? String,
|
||||
eyeColor = dict["eyeColor"] as? String,
|
||||
genderStr = dict["gender"] as? String,
|
||||
starshipsIDs = dict["starships"] as? [Int]
|
||||
else {
|
||||
fatalError("Invalid Plist")
|
||||
}
|
||||
|
||||
return Person(
|
||||
name: name,
|
||||
height: height,
|
||||
mass: mass,
|
||||
hairColor: hairColor,
|
||||
eyeColor: eyeColor,
|
||||
gender: Gender(rawValue: genderStr),
|
||||
starshipIDs: starshipsIDs
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FakePersonsProvider: PersonProviderAPI {
|
||||
|
||||
let dummyProvider: PersonProviderAPI
|
||||
var plistProvider: PersonProviderAPI!
|
||||
|
||||
//In this class we use both constructor injection and property injection,
|
||||
//nil is a valid local default
|
||||
init(dummyProvider: PersonProviderAPI) {
|
||||
self.dummyProvider = dummyProvider
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func fetchIDs(completion: [Int] -> Void) {
|
||||
=======
|
||||
func fetchIDs(completion: ([Int]) -> Void) {
|
||||
>>>>>>> feature/swift3
|
||||
dummyProvider.fetchIDs(completion: completion)
|
||||
}
|
||||
|
||||
func fetch(id: Int, completion: (Person?) -> Void) {
|
||||
if let plistProvider = plistProvider where id == 0 {
|
||||
plistProvider.fetch(id: id, completion: completion)
|
||||
}
|
||||
else {
|
||||
dummyProvider.fetch(id: id, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@ struct DummyStarshipProvider : StarshipProviderAPI {
|
||||
var pilotName: String
|
||||
|
||||
func fetchIDs(completion: @escaping ([Int]) -> Void) {
|
||||
let nbShips = pilotName.characters.count
|
||||
let nbShips = pilotName.count
|
||||
completion(Array(0..<nbShips))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
//
|
||||
// FakeStarshipProvider.swift
|
||||
// DipSampleApp
|
||||
//
|
||||
// Created by Ilya Puchka on 20.01.16.
|
||||
// Copyright © 2016 AliSoftware. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
///Provides some dummy Starship entities
|
||||
struct DummyStarshipProvider : StarshipProviderAPI {
|
||||
var pilotName: String
|
||||
|
||||
func fetchIDs(completion: ([Int]) -> Void) {
|
||||
let nbShips = pilotName.characters.count
|
||||
completion(Array(0..<nbShips))
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func fetch(id: Int, completion: Starship? -> Void) {
|
||||
=======
|
||||
func fetch(id: Int, completion: (Starship?) -> Void) {
|
||||
>>>>>>> feature/swift3
|
||||
completion(dummyStarship(idx: id))
|
||||
}
|
||||
|
||||
private func dummyStarship(idx: Int) -> Starship {
|
||||
return Starship(
|
||||
name: "\(pilotName)'s awesome starship #\(idx)",
|
||||
model: "\(pilotName)Ship",
|
||||
manufacturer: "Dummy Industries",
|
||||
crew: 1 + (idx%3),
|
||||
passengers: 10 + (idx*7 % 40),
|
||||
pilotIDs: [idx]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
///Provides hardcoded Starship entities stored in memory
|
||||
class HardCodedStarshipProvider : StarshipProviderAPI {
|
||||
|
||||
let starships = [
|
||||
Starship(name: "First Ship", model: "AwesomeShip", manufacturer: "HardCoded Inc.", crew: 3, passengers: 20, pilotIDs: [1,2]),
|
||||
Starship(name: "Second Ship", model: "AwesomeShip Express", manufacturer: "HardCoded Inc.", crew: 4, passengers: 10, pilotIDs: [1]),
|
||||
Starship(name: "Third Ship", model: "AwesomeShip Cargo", manufacturer: "HardCoded Inc.", crew: 12, passengers: 150, pilotIDs: [2]),
|
||||
] + Array(4..<75).map { Starship(name: "Ship #\($0)", model: "AwesomeShip Fighter", manufacturer: "HardCoded Inc.", crew: 1, passengers: 2, pilotIDs: [1]) }
|
||||
|
||||
func fetchIDs(completion: ([Int]) -> Void) {
|
||||
completion(Array(0..<starships.count))
|
||||
}
|
||||
|
||||
func fetch(id: Int, completion: (Starship?) -> Void) {
|
||||
guard id < starships.count else {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
completion(starships[id])
|
||||
}
|
||||
}
|
||||
|
||||
class FakeStarshipProvider: StarshipProviderAPI {
|
||||
|
||||
let dummyProvider: StarshipProviderAPI
|
||||
let hardCodedProvider: StarshipProviderAPI
|
||||
|
||||
//Constructor injection again here
|
||||
init(dummyProvider: StarshipProviderAPI, hardCodedProvider: StarshipProviderAPI) {
|
||||
self.dummyProvider = dummyProvider
|
||||
self.hardCodedProvider = hardCodedProvider
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func fetchIDs(completion: [Int] -> Void) {
|
||||
=======
|
||||
func fetchIDs(completion: ([Int]) -> Void) {
|
||||
>>>>>>> feature/swift3
|
||||
hardCodedProvider.fetchIDs(completion: completion)
|
||||
}
|
||||
|
||||
func fetch(id: Int, completion: (Starship?) -> Void) {
|
||||
if id == 0 {
|
||||
dummyProvider.fetch(id: id, completion: completion)
|
||||
}
|
||||
else {
|
||||
hardCodedProvider.fetch(id: id, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
//
|
||||
// NetworkLayer.swift
|
||||
// Dip
|
||||
//
|
||||
// Created by Olivier Halligon on 10/10/2015.
|
||||
// Copyright © 2015 AliSoftware. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum NetworkResponse {
|
||||
case Success(Data, HTTPURLResponse)
|
||||
case Error(NSError)
|
||||
|
||||
func unwrap() throws -> (Data, HTTPURLResponse) {
|
||||
switch self {
|
||||
case Success(let data, let response):
|
||||
return (data, response)
|
||||
case Error(let error):
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
func json<T>() throws -> T {
|
||||
let (data, _) = try self.unwrap()
|
||||
<<<<<<< HEAD
|
||||
let obj = try NSJSONSerialization.jsonObject(with: data, options: [])
|
||||
=======
|
||||
let obj = try JSONSerialization.jsonObject(with: data, options: [])
|
||||
>>>>>>> feature/swift3
|
||||
guard let json = obj as? T else {
|
||||
throw SWAPIError.InvalidJSON
|
||||
}
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
protocol NetworkLayer {
|
||||
func request(path: String, completion: (NetworkResponse) -> Void)
|
||||
}
|
||||
@@ -27,8 +27,8 @@ struct SWAPIPersonProvider : PersonProviderAPI {
|
||||
guard let results = dict["results"] as? [NSDictionary] else { throw SWAPIError.InvalidJSON }
|
||||
|
||||
// Extract URLs (flatten to ignore invalid ones)
|
||||
let urlStrings = results.flatMap({ $0["url"] as? String })
|
||||
let ids = urlStrings.flatMap(idFromURLString)
|
||||
let urlStrings = results.compactMap({ $0["url"] as? String })
|
||||
let ids = urlStrings.compactMap(idFromURLString)
|
||||
|
||||
completion(ids)
|
||||
}
|
||||
@@ -61,7 +61,7 @@ struct SWAPIPersonProvider : PersonProviderAPI {
|
||||
hairColor: hairColor,
|
||||
eyeColor: eyeColor,
|
||||
gender: Gender(rawValue: gender),
|
||||
starshipIDs: starshipURLStrings.flatMap(idFromURLString)
|
||||
starshipIDs: starshipURLStrings.compactMap(idFromURLString)
|
||||
)
|
||||
completion(person)
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
//
|
||||
// SWAPIPersonProvider.swift
|
||||
// Dip
|
||||
//
|
||||
// Created by Olivier Halligon on 10/10/2015.
|
||||
// Copyright © 2015 AliSoftware. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
///Provides Person entitis fetching them with web service
|
||||
struct SWAPIPersonProvider : PersonProviderAPI {
|
||||
let ws: NetworkLayer
|
||||
|
||||
//Here we inject dependency using _constructor injection_ pattern.
|
||||
//The alternative way is a _property injection_
|
||||
//but it should be used only for optional dependencies
|
||||
//where there is a good local default implementation
|
||||
init(webService: NetworkLayer) {
|
||||
self.ws = webService
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func fetchIDs(completion: [Int] -> Void) {
|
||||
=======
|
||||
func fetchIDs(completion: ([Int]) -> Void) {
|
||||
>>>>>>> feature/swift3
|
||||
ws.request(path: "people") { response in
|
||||
do {
|
||||
let dict = try response.json() as NSDictionary
|
||||
guard let results = dict["results"] as? [NSDictionary] else { throw SWAPIError.InvalidJSON }
|
||||
|
||||
// Extract URLs (flatten to ignore invalid ones)
|
||||
let urlStrings = results.flatMap({ $0["url"] as? String })
|
||||
let ids = urlStrings.flatMap(idFromURLString)
|
||||
|
||||
completion(ids)
|
||||
}
|
||||
catch {
|
||||
completion([])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func fetch(id: Int, completion: Person? -> Void) {
|
||||
=======
|
||||
func fetch(id: Int, completion: (Person?) -> Void) {
|
||||
>>>>>>> feature/swift3
|
||||
ws.request(path: "people/\(id)") { response in
|
||||
do {
|
||||
let json = try response.json() as NSDictionary
|
||||
guard
|
||||
let name = json["name"] as? String,
|
||||
let heightStr = json["height"] as? String, height = Int(heightStr),
|
||||
let massStr = json["mass"] as? String, mass = Int(massStr),
|
||||
let hairColor = json["hair_color"] as? String,
|
||||
let eyeColor = json["eye_color"] as? String,
|
||||
let gender = json["gender"] as? String,
|
||||
let starshipURLStrings = json["starships"] as? [String]
|
||||
else {
|
||||
throw SWAPIError.InvalidJSON
|
||||
}
|
||||
|
||||
let person = Person(
|
||||
name: name,
|
||||
height: height,
|
||||
mass: mass,
|
||||
hairColor: hairColor,
|
||||
eyeColor: eyeColor,
|
||||
gender: Gender(rawValue: gender),
|
||||
starshipIDs: starshipURLStrings.flatMap(idFromURLString)
|
||||
)
|
||||
completion(person)
|
||||
}
|
||||
catch {
|
||||
completion(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,8 @@ struct SWAPIStarshipProvider : StarshipProviderAPI {
|
||||
guard let results = dict["results"] as? [NSDictionary] else { throw SWAPIError.InvalidJSON }
|
||||
|
||||
// Extract URLs (flatten to ignore invalid ones)
|
||||
let urlStrings = results.flatMap({ $0["url"] as? String })
|
||||
let ids = urlStrings.flatMap(idFromURLString)
|
||||
let urlStrings = results.compactMap({ $0["url"] as? String })
|
||||
let ids = urlStrings.compactMap(idFromURLString)
|
||||
|
||||
completion(ids)
|
||||
}
|
||||
@@ -59,7 +59,7 @@ struct SWAPIStarshipProvider : StarshipProviderAPI {
|
||||
manufacturer: manufacturer,
|
||||
crew: crew,
|
||||
passengers: passengers,
|
||||
pilotIDs: pilotIDStrings.flatMap(idFromURLString)
|
||||
pilotIDs: pilotIDStrings.compactMap(idFromURLString)
|
||||
)
|
||||
completion(ship)
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
//
|
||||
// SWAPIStarshipProvider.swift
|
||||
// Dip
|
||||
//
|
||||
// Created by Olivier Halligon on 10/10/2015.
|
||||
// Copyright © 2015 AliSoftware. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
///Provides Starship entities fetching them using web service
|
||||
struct SWAPIStarshipProvider : StarshipProviderAPI {
|
||||
let ws: NetworkLayer
|
||||
|
||||
//Here we inject dependency using _constructor injection_ pattern.
|
||||
//The alternative way is a _property injection_
|
||||
//but it should be used only for optional dependencies
|
||||
//where there is a good local default implementation
|
||||
init(webService: NetworkLayer) {
|
||||
self.ws = webService
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func fetchIDs(completion: [Int] -> Void) {
|
||||
=======
|
||||
func fetchIDs(completion: ([Int]) -> Void) {
|
||||
>>>>>>> feature/swift3
|
||||
ws.request(path: "starships") { response in
|
||||
do {
|
||||
let dict = try response.json() as NSDictionary
|
||||
guard let results = dict["results"] as? [NSDictionary] else { throw SWAPIError.InvalidJSON }
|
||||
|
||||
// Extract URLs (flatten to ignore invalid ones)
|
||||
let urlStrings = results.flatMap({ $0["url"] as? String })
|
||||
let ids = urlStrings.flatMap(idFromURLString)
|
||||
|
||||
completion(ids)
|
||||
}
|
||||
catch {
|
||||
completion([])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func fetch(id: Int, completion: Starship? -> Void) {
|
||||
=======
|
||||
func fetch(id: Int, completion: (Starship?) -> Void) {
|
||||
>>>>>>> feature/swift3
|
||||
ws.request(path: "starships/\(id)") { response in
|
||||
do {
|
||||
let json = try response.json() as NSDictionary
|
||||
guard
|
||||
let name = json["name"] as? String,
|
||||
let model = json["model"] as? String,
|
||||
let manufacturer = json["manufacturer"] as? String,
|
||||
let crewStr = json["crew"] as? String, crew = Int(crewStr),
|
||||
let passengersStr = json["passengers"] as? String, passengers = Int(passengersStr),
|
||||
let pilotIDStrings = json["pilots"] as? [String]
|
||||
else {
|
||||
throw SWAPIError.InvalidJSON
|
||||
}
|
||||
|
||||
let ship = Starship(
|
||||
name: name,
|
||||
model: model,
|
||||
manufacturer: manufacturer,
|
||||
crew: crew,
|
||||
passengers: passengers,
|
||||
pilotIDs: pilotIDStrings.flatMap(idFromURLString)
|
||||
)
|
||||
completion(ship)
|
||||
}
|
||||
catch {
|
||||
completion(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
//
|
||||
// URLSessionNetworkLayer.swift
|
||||
// Dip
|
||||
//
|
||||
// Created by Olivier Halligon on 10/10/2015.
|
||||
// Copyright © 2015 AliSoftware. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
///NetworkLayer implementation on top of NSURLSession
|
||||
struct URLSessionNetworkLayer : NetworkLayer {
|
||||
let baseURL: URL
|
||||
let session: URLSession
|
||||
let responseQueue: DispatchQueue
|
||||
|
||||
<<<<<<< HEAD
|
||||
init?(baseURL: String, session: NSURLSession = NSURLSession.shared(), responseQueue: dispatch_queue_t = dispatch_get_main_queue()) {
|
||||
guard let url = NSURL(string: baseURL) else { return nil }
|
||||
self.init(baseURL: url, session: session)
|
||||
}
|
||||
|
||||
init(baseURL: NSURL, session: NSURLSession = .shared(), responseQueue: dispatch_queue_t = dispatch_get_main_queue()) {
|
||||
=======
|
||||
init?(baseURL: String, session: URLSession = .shared(), responseQueue: DispatchQueue = DispatchQueue.main) {
|
||||
guard let url = URL(string: baseURL) else { return nil }
|
||||
self.init(baseURL: url, session: session)
|
||||
}
|
||||
|
||||
init(baseURL: URL, session: URLSession = .shared(), responseQueue: DispatchQueue = DispatchQueue.main) {
|
||||
>>>>>>> feature/swift3
|
||||
self.baseURL = baseURL
|
||||
self.session = session
|
||||
self.responseQueue = responseQueue
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func request(path: String, completion: NetworkResponse -> Void) {
|
||||
let url = self.baseURL.appendingPathComponent(path)
|
||||
let task = session.dataTask(with: url) { data, response, error in
|
||||
if let data = data, let response = response as? NSHTTPURLResponse {
|
||||
dispatch_async(self.responseQueue) {
|
||||
=======
|
||||
func request(path: String, completion: (NetworkResponse) -> Void) {
|
||||
guard let url = try? self.baseURL.appendingPathComponent(path) else { return }
|
||||
let task = session.dataTask(with: url) { data, response, error in
|
||||
if let data = data, let response = response as? HTTPURLResponse {
|
||||
self.responseQueue.async() {
|
||||
>>>>>>> feature/swift3
|
||||
completion(NetworkResponse.Success(data, response))
|
||||
}
|
||||
}
|
||||
else {
|
||||
let err = error ?? NSError(domain: NSURLErrorDomain, code: NSURLError.unknown.rawValue, userInfo: nil)
|
||||
<<<<<<< HEAD
|
||||
dispatch_async(self.responseQueue) {
|
||||
=======
|
||||
self.responseQueue.async() {
|
||||
>>>>>>> feature/swift3
|
||||
completion(NetworkResponse.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
task.resume()
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
//
|
||||
// PersonListViewController.swift
|
||||
// Dip
|
||||
//
|
||||
// Created by Olivier Halligon on 09/10/2015.
|
||||
// Copyright © 2015 AliSoftware. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class PersonListViewController: UITableViewController, FetchableTrait {
|
||||
var objects: [Person]?
|
||||
var batchRequestID = 0
|
||||
|
||||
var personProvider: PersonProviderAPI!
|
||||
var starshipProvider: StarshipProviderAPI!
|
||||
<<<<<<< HEAD
|
||||
|
||||
func fetchIDs(completion: [Int] -> Void) {
|
||||
return personProvider.fetchIDs(completion: completion)
|
||||
}
|
||||
|
||||
func fetchOne(id personID: Int, completion: Person? -> Void) {
|
||||
=======
|
||||
|
||||
func fetchIDs(completion: ([Int]) -> Void) {
|
||||
return personProvider.fetchIDs(completion: completion)
|
||||
}
|
||||
|
||||
func fetchOne(id personID: Int, completion: (Person?) -> Void) {
|
||||
>>>>>>> feature/swift3
|
||||
return personProvider.fetch(id: personID, completion: completion)
|
||||
}
|
||||
|
||||
var fetchProgress: (current: Int, total: Int?) = (0, nil) {
|
||||
didSet {
|
||||
displayProgressInNavBar(navigationItem: self.navigationItem)
|
||||
}
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
|
||||
guard
|
||||
let id = segue.identifier, segueID = UIStoryboard.Segue.Main(rawValue: id)
|
||||
where segueID == .StarshipsSegue,
|
||||
let indexPath = self.tableView.indexPathForSelectedRow,
|
||||
let destVC = segue.destinationViewController as? StarshipListViewController,
|
||||
let person = self.objects?[indexPath.row]
|
||||
else {
|
||||
fatalError()
|
||||
}
|
||||
destVC.starshipProvider = starshipProvider
|
||||
destVC.loadObjects(objectIDs: person.starshipIDs)
|
||||
}
|
||||
}
|
||||
|
||||
extension PersonListViewController {
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return objects?.count ?? 0
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: NSIndexPath) -> UITableViewCell {
|
||||
guard let object = self.objects?[indexPath.row] else { fatalError() }
|
||||
let cell = PersonCell.dequeueFromTableView(tableView: tableView, forIndexPath: indexPath)
|
||||
=======
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
guard let object = self.objects?[indexPath.row] else { fatalError() }
|
||||
let cell = PersonCell.dequeueFromTableView(tableView, forIndexPath: indexPath)
|
||||
>>>>>>> feature/swift3
|
||||
cell.fillWithObject(object: object)
|
||||
return cell
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
//
|
||||
// StarshipListViewController.swift
|
||||
// Dip
|
||||
//
|
||||
// Created by Olivier Halligon on 09/10/2015.
|
||||
// Copyright © 2015 AliSoftware. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Dip
|
||||
|
||||
class StarshipListViewController : UITableViewController, FetchableTrait {
|
||||
var objects: [Starship]?
|
||||
var batchRequestID = 0
|
||||
|
||||
var starshipProvider: StarshipProviderAPI!
|
||||
var personProvider: PersonProviderAPI!
|
||||
|
||||
<<<<<<< HEAD
|
||||
func fetchIDs(completion: [Int] -> Void) {
|
||||
starshipProvider.fetchIDs(completion: completion)
|
||||
}
|
||||
func fetchOne(id shipID:Int, completion: Starship? -> Void) {
|
||||
=======
|
||||
func fetchIDs(completion: ([Int]) -> Void) {
|
||||
starshipProvider.fetchIDs(completion: completion)
|
||||
}
|
||||
func fetchOne(id shipID:Int, completion: (Starship?) -> Void) {
|
||||
>>>>>>> feature/swift3
|
||||
starshipProvider.fetch(id: shipID, completion: completion)
|
||||
}
|
||||
|
||||
var fetchProgress: (current: Int, total: Int?) = (0, nil) {
|
||||
didSet {
|
||||
displayProgressInNavBar(navigationItem: self.navigationItem)
|
||||
}
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
|
||||
guard
|
||||
let id = segue.identifier, segueID = UIStoryboard.Segue.Main(rawValue: id)
|
||||
where segueID == .PilotsSegue,
|
||||
let indexPath = self.tableView.indexPathForSelectedRow,
|
||||
let destVC = segue.destinationViewController as? PersonListViewController,
|
||||
let starship = self.objects?[indexPath.row]
|
||||
else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
destVC.personProvider = personProvider
|
||||
destVC.loadObjects(objectIDs: starship.pilotIDs)
|
||||
}
|
||||
}
|
||||
|
||||
extension StarshipListViewController {
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return objects?.count ?? 0
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: NSIndexPath) -> UITableViewCell {
|
||||
guard let object = self.objects?[indexPath.row] else { fatalError() }
|
||||
let cell = StarshipCell.dequeueFromTableView(tableView: tableView, forIndexPath: indexPath)
|
||||
=======
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
guard let object = self.objects?[indexPath.row] else { fatalError() }
|
||||
let cell = StarshipCell.dequeueFromTableView(tableView, forIndexPath: indexPath)
|
||||
>>>>>>> feature/swift3
|
||||
cell.fillWithObject(object: object)
|
||||
return cell
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<string>6.1</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
//
|
||||
// NetworkMock.swift
|
||||
// Dip
|
||||
//
|
||||
// Created by Olivier Halligon on 11/10/2015.
|
||||
// Copyright © 2015 AliSoftware. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Dip
|
||||
|
||||
var wsDependencies = DependencyContainer()
|
||||
|
||||
// MARK: - Mock object used for tests
|
||||
|
||||
struct NetworkMock : NetworkLayer {
|
||||
let fakeData: Data?
|
||||
|
||||
init(json: AnyObject) {
|
||||
do {
|
||||
<<<<<<< HEAD
|
||||
fakeData = try NSJSONSerialization.data(withJSONObject: json, options: [])
|
||||
=======
|
||||
fakeData = try JSONSerialization.data(withJSONObject: json, options: [])
|
||||
>>>>>>> feature/swift3
|
||||
} catch {
|
||||
fakeData = nil
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func request(path: String, completion: NetworkResponse -> Void) {
|
||||
let fakeURL = NSURL(string: "stub://")!.appendingPathComponent(path)
|
||||
if let data = fakeData {
|
||||
let response = NSHTTPURLResponse(url: fakeURL, statusCode: 200, httpVersion: "1.1", headerFields:nil)!
|
||||
completion(.Success(data, response))
|
||||
} else {
|
||||
let response = NSHTTPURLResponse(url: fakeURL, statusCode: 204, httpVersion: "1.1", headerFields:nil)!
|
||||
completion(.Success(NSData(), response))
|
||||
=======
|
||||
func request(path: String, completion: (NetworkResponse) -> Void) {
|
||||
let fakeURL = try! URL(string: "stub://")!.appendingPathComponent(path)
|
||||
if let data = fakeData {
|
||||
let response = HTTPURLResponse(url: fakeURL, statusCode: 200, httpVersion: "1.1", headerFields:nil)!
|
||||
completion(.Success(data, response))
|
||||
} else {
|
||||
let response = HTTPURLResponse(url: fakeURL, statusCode: 204, httpVersion: "1.1", headerFields:nil)!
|
||||
completion(.Success(Data(), response))
|
||||
>>>>>>> feature/swift3
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
-39
@@ -27,7 +27,7 @@ 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
|
||||
@@ -43,11 +43,14 @@ extension DependencyContainer {
|
||||
}
|
||||
|
||||
private func resolveChild(child: Mirror.Child) throws {
|
||||
//HOTFIX for https://bugs.swift.org/browse/SR-2282
|
||||
guard !String(describing: type(of: child.value)).has(prefix: "ImplicitlyUnwrappedOptional") else { return }
|
||||
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)
|
||||
let wrappedType = type(of: injectedPropertyBox).wrappedType
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,41 +118,41 @@ 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) }
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
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) -> ()) {
|
||||
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 {
|
||||
@@ -158,7 +161,7 @@ public final class Injected<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox
|
||||
|
||||
return Injected(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,7 +205,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) }
|
||||
}
|
||||
@@ -213,6 +216,11 @@ public final class InjectedWeak<T>: _InjectedPropertyBox<T>, AutoInjectedPropert
|
||||
return valueBox?.value
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
@@ -221,39 +229,34 @@ 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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class _InjectedPropertyBox<T> {
|
||||
public class _InjectedPropertyBox<T> {
|
||||
|
||||
let required: Bool
|
||||
let didInject: (T) -> ()
|
||||
@@ -267,28 +270,27 @@ private class _InjectedPropertyBox<T> {
|
||||
self.didInject = didInject
|
||||
}
|
||||
|
||||
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: (Any) throws -> Any) 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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -49,19 +50,30 @@ extension DependencyContainer {
|
||||
}
|
||||
|
||||
private func autoWiringDefinition(byKey key: DefinitionKey) throws -> KeyDefinitionPair {
|
||||
do {
|
||||
return try autoWiringDefinition(byKey: key, strictByTag: true)
|
||||
} catch {
|
||||
if key.tag != nil {
|
||||
return try autoWiringDefinition(byKey: key, strictByTag: false)
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func autoWiringDefinition(byKey key: DefinitionKey, strictByTag: Bool) throws -> KeyDefinitionPair {
|
||||
var definitions = self.definitions.map({ (key: $0.0, definition: $0.1) })
|
||||
|
||||
definitions = filter(definitions, byKey: key)
|
||||
definitions = filter(definitions: definitions, byKey: key, strictByTag: strictByTag)
|
||||
definitions = definitions.sorted(by: { $0.definition.numberOfArguments > $1.definition.numberOfArguments })
|
||||
|
||||
|
||||
guard definitions.count > 0 && definitions[0].definition.numberOfArguments > 0 else {
|
||||
throw DipError.definitionNotFound(key: key)
|
||||
}
|
||||
|
||||
let maximumNumberOfArguments = definitions.first?.definition.numberOfArguments
|
||||
definitions = definitions.filter({ $0.definition.numberOfArguments == maximumNumberOfArguments })
|
||||
definitions = order(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 {
|
||||
let error = DipError.ambiguousDefinitions(type: key.type, definitions: definitions.map({ $0.definition }))
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#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,120 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
///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
|
||||
|
||||
}
|
||||
+45
-140
@@ -23,7 +23,7 @@
|
||||
//
|
||||
|
||||
///A key used to store definitons in a container.
|
||||
public struct DefinitionKey : Hashable, CustomStringConvertible {
|
||||
public struct DefinitionKey: Hashable, CustomStringConvertible {
|
||||
public let type: Any.Type
|
||||
public let typeOfArguments: Any.Type
|
||||
public private(set) var tag: DependencyContainer.Tag?
|
||||
@@ -35,122 +35,27 @@ public struct DefinitionKey : Hashable, CustomStringConvertible {
|
||||
}
|
||||
|
||||
public var hashValue: Int {
|
||||
return "\(type)-\(typeOfArguments)-\(tag)".hashValue
|
||||
return "\(type)-\(typeOfArguments)-\(tag.desc)".hashValue
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Check two definition keys on equality by comparing their `type`, `factoryType` and `tag` properties.
|
||||
public func ==(lhs: DefinitionKey, rhs: DefinitionKey) -> Bool {
|
||||
return
|
||||
lhs.type == rhs.type &&
|
||||
/// Check two definition keys on equality by comparing their `type`, `factoryType` and `tag` properties.
|
||||
public static func ==(lhs: DefinitionKey, rhs: DefinitionKey) -> Bool {
|
||||
return
|
||||
lhs.type == rhs.type &&
|
||||
lhs.typeOfArguments == rhs.typeOfArguments &&
|
||||
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
|
||||
@@ -167,20 +72,20 @@ 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 -> ())?
|
||||
|
||||
init(scope: ComponentScope, factory: @escaping F) {
|
||||
self.factory = factory
|
||||
self.scope = scope
|
||||
}
|
||||
|
||||
/**
|
||||
Set the block that will be used to resolve dependencies of the instance.
|
||||
This block will be called before `resolve(tag:)` returns.
|
||||
@@ -233,40 +138,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,26 +179,26 @@ 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] = []
|
||||
|
||||
}
|
||||
|
||||
//MARK: - _Definition
|
||||
|
||||
protocol _Definition: DefinitionType, AutoWiringDefinition, TypeForwardingDefinition {
|
||||
protocol _Definition: AutoWiringDefinition, TypeForwardingDefinition {
|
||||
var type: Any.Type { get }
|
||||
var scope: ComponentScope { get }
|
||||
var weakFactory: ((Any) throws -> Any)! { get }
|
||||
@@ -303,11 +208,11 @@ protocol _Definition: DefinitionType, AutoWiringDefinition, TypeForwardingDefini
|
||||
|
||||
//MARK: - Type Forwarding
|
||||
|
||||
private protocol _TypeForwardingDefinition: TypeForwardingDefinition, _Definition {
|
||||
weak var forwardsTo: _TypeForwardingDefinition? { get set }
|
||||
protocol _TypeForwardingDefinition: _Definition {
|
||||
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 {
|
||||
@@ -364,12 +269,12 @@ 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.
|
||||
/// and which tag matches provided key's tag or is nil if strictByTag is false.
|
||||
/// 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) })
|
||||
.filter({ $0.key.tag == key.tag || $0.key.tag == nil })
|
||||
func filter(definitions _definitions: [KeyDefinitionPair], byKey key: DefinitionKey, strictByTag: Bool = false, 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 || (!strictByTag && $0.key.tag == nil) })
|
||||
if byTypeOfArguments {
|
||||
return definitions.filter({ $0.key.typeOfArguments == key.typeOfArguments })
|
||||
}
|
||||
@@ -379,8 +284,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 })
|
||||
}
|
||||
|
||||
+89
-482
@@ -34,23 +34,23 @@ public final class DependencyContainer {
|
||||
|
||||
- seealso: `DependencyTagConvertible`
|
||||
*/
|
||||
public enum Tag: Equatable {
|
||||
public enum Tag {
|
||||
case String(StringLiteralType)
|
||||
case Int(IntegerLiteralType)
|
||||
}
|
||||
|
||||
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 })
|
||||
return _weakCollaborators.compactMap({ $0.value })
|
||||
}
|
||||
set {
|
||||
_weakCollaborators = newValue.filter({ $0 !== self }).map(WeakBox.init)
|
||||
@@ -99,11 +99,9 @@ public final class DependencyContainer {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func threadSafe<T>(_ closure: () throws -> T) rethrows -> T {
|
||||
func threadSafe<T>(_ closure: () throws -> T) rethrows -> T {
|
||||
lock.lock()
|
||||
defer {
|
||||
lock.unlock()
|
||||
}
|
||||
defer { lock.unlock() }
|
||||
return try closure()
|
||||
}
|
||||
|
||||
@@ -169,12 +167,15 @@ extension DependencyContainer {
|
||||
/// The label of the property where resolved instance will be auto-injected.
|
||||
private(set) public var injectedInProperty: String?
|
||||
|
||||
let inCollaboration: Bool
|
||||
|
||||
var logErrors: Bool = true
|
||||
|
||||
init(key: DefinitionKey, injectedInType: Any.Type?, injectedInProperty: String?) {
|
||||
init(key: DefinitionKey, injectedInType: Any.Type?, injectedInProperty: String?, inCollaboration: Bool) {
|
||||
self.key = key
|
||||
self.injectedInType = injectedInType
|
||||
self.injectedInProperty = injectedInProperty
|
||||
self.inCollaboration = inCollaboration
|
||||
}
|
||||
|
||||
public var debugDescription: String {
|
||||
@@ -182,7 +183,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 +199,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, inCollaboration: Bool = false, container: DependencyContainer? = nil, logErrors: Bool! = nil, block: () throws -> T) rethrows -> T {
|
||||
let key = aKey
|
||||
return try threadSafe {
|
||||
let currentContext = self.context
|
||||
|
||||
@@ -208,13 +210,15 @@ extension DependencyContainer {
|
||||
//clean instances pool if it is owned not by other container
|
||||
if context == nil {
|
||||
resolvedInstances.resolvedInstances.removeAll()
|
||||
for (key, instance) in resolvedInstances.sharedWeakSingletons {
|
||||
if resolvedInstances.sharedWeakSingletons[key] is WeakBoxType { continue }
|
||||
resolvedInstances.sharedWeakSingletons[key] = WeakBox(instance)
|
||||
}
|
||||
for (key, instance) in resolvedInstances.weakSingletons {
|
||||
if resolvedInstances.weakSingletons[key] is WeakBoxType { continue }
|
||||
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()
|
||||
}
|
||||
@@ -225,7 +229,8 @@ extension DependencyContainer {
|
||||
context = Context(
|
||||
key: key,
|
||||
injectedInType: injectedInType,
|
||||
injectedInProperty: injectedInProperty
|
||||
injectedInProperty: injectedInProperty,
|
||||
inCollaboration: inCollaboration
|
||||
)
|
||||
context.logErrors = logErrors ?? currentContext?.logErrors ?? true
|
||||
|
||||
@@ -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 {
|
||||
@@ -572,21 +265,31 @@ extension DependencyContainer {
|
||||
public func collaborate(with containers: [DependencyContainer]) {
|
||||
_collaborators += containers
|
||||
for container in containers {
|
||||
container.resolvedInstances.singletonsBox = self.resolvedInstances.singletonsBox
|
||||
container.resolvedInstances.weakSingletonsBox = self.resolvedInstances.weakSingletonsBox
|
||||
container._collaborators += [self]
|
||||
container.resolvedInstances.sharedSingletonsBox = self.resolvedInstances.sharedSingletonsBox
|
||||
container.resolvedInstances.sharedWeakSingletonsBox = self.resolvedInstances.sharedWeakSingletonsBox
|
||||
updateCollaborationReferences(between: container, and: self)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateCollaborationReferences(between container: DependencyContainer, and collaborator: DependencyContainer) {
|
||||
for container in container._collaborators {
|
||||
guard container.resolvedInstances.sharedSingletonsBox !== collaborator.resolvedInstances.sharedSingletonsBox else { continue }
|
||||
container.resolvedInstances.sharedSingletonsBox = collaborator.resolvedInstances.sharedSingletonsBox
|
||||
container.resolvedInstances.sharedWeakSingletonsBox = collaborator.resolvedInstances.sharedWeakSingletonsBox
|
||||
updateCollaborationReferences(between: container, and: collaborator)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 let context = collaborator.context, context.resolvingType == key.type && context.tag == key.tag { continue }
|
||||
|
||||
do {
|
||||
//Pass current container's instances pool to collect instances resolved by collaborator
|
||||
@@ -596,12 +299,24 @@ extension DependencyContainer {
|
||||
let context = collaborator.context
|
||||
collaborator.context = self.context
|
||||
defer {
|
||||
collaborator.resolvedInstances = resolvedInstances
|
||||
collaborator.context = context
|
||||
collaborator.resolvedInstances = resolvedInstances
|
||||
|
||||
for (key, resolvedSingleton) in self.resolvedInstances.singletons {
|
||||
collaborator.resolvedInstances.singletons[key] = resolvedSingleton
|
||||
}
|
||||
for (key, resolvedSingleton) in self.resolvedInstances.weakSingletons {
|
||||
guard collaborator.definition(matching: key) == nil else { continue }
|
||||
collaborator.resolvedInstances.weakSingletons[key] = resolvedSingleton is WeakBoxType ? resolvedSingleton : WeakBox(resolvedSingleton)
|
||||
}
|
||||
for (key, resolved) in self.resolvedInstances.resolvedInstances {
|
||||
guard collaborator.definition(matching: key) == nil else { continue }
|
||||
collaborator.resolvedInstances.resolvedInstances[key] = resolved
|
||||
}
|
||||
}
|
||||
|
||||
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, inCollaboration: true, logErrors: false) {
|
||||
try collaborator._resolve(key: key, builder: builder)
|
||||
}
|
||||
|
||||
return resolved
|
||||
@@ -619,17 +334,21 @@ extension DependencyContainer {
|
||||
|
||||
/**
|
||||
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) {
|
||||
let key = DefinitionKey(type: T.self, typeOfArguments: U.self, tag: tag?.dependencyTag)
|
||||
remove(definitionForKey: key)
|
||||
_remove(definition: definition, tag: tag)
|
||||
}
|
||||
|
||||
fileprivate func remove(definitionForKey key: DefinitionKey) {
|
||||
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)
|
||||
}
|
||||
|
||||
func _remove(definitionForKey key: DefinitionKey) {
|
||||
precondition(!bootstrapped, "You can not modify container's definitions after it was bootstrapped.")
|
||||
|
||||
threadSafe {
|
||||
@@ -637,6 +356,8 @@ extension DependencyContainer {
|
||||
definitions[key] = nil
|
||||
resolvedInstances.singletons[key] = nil
|
||||
resolvedInstances.weakSingletons[key] = nil
|
||||
resolvedInstances.sharedSingletons[key] = nil
|
||||
resolvedInstances.sharedWeakSingletons[key] = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,12 +370,16 @@ extension DependencyContainer {
|
||||
definitions.removeAll()
|
||||
resolvedInstances.singletons.removeAll()
|
||||
resolvedInstances.weakSingletons.removeAll()
|
||||
resolvedInstances.sharedSingletons.removeAll()
|
||||
resolvedInstances.sharedWeakSingletons.removeAll()
|
||||
bootstrapped = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Validation
|
||||
|
||||
extension DependencyContainer {
|
||||
|
||||
/**
|
||||
@@ -665,15 +390,21 @@ 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)
|
||||
}
|
||||
|
||||
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 {
|
||||
guard type(of: argumentsSet) == key.typeOfArguments else { continue }
|
||||
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 +414,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 +425,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 +439,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.
|
||||
@@ -826,85 +501,17 @@ extension DependencyContainer.Tag: ExpressibleByIntegerLiteral {
|
||||
|
||||
}
|
||||
|
||||
public func ==(lhs: DependencyContainer.Tag, rhs: DependencyContainer.Tag) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case let (.String(lhsString), .String(rhsString)):
|
||||
return lhsString == rhsString
|
||||
case let (.Int(lhsInt), .Int(rhsInt)):
|
||||
return lhsInt == rhsInt
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
extension DependencyContainer.Tag: Equatable {
|
||||
|
||||
//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)."
|
||||
public static func ==(lhs: DependencyContainer.Tag, rhs: DependencyContainer.Tag) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case let (.String(lhsString), .String(rhsString)):
|
||||
return lhsString == rhsString
|
||||
case let (.Int(lhsInt), .Int(rhsInt)):
|
||||
return lhsInt == rhsInt
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
/**
|
||||
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,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.
|
||||
//
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
resolvedInstances.sharedSingletons[key] = nil
|
||||
resolvedInstances.sharedWeakSingletons[key] = nil
|
||||
|
||||
if .eagerSingleton == definition.scope {
|
||||
bootstrapQueue.append({ _ = try self.resolve(tag: tag) as T })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
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: () throws -> T) 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: () throws -> Any) 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)
|
||||
}
|
||||
|
||||
public func resolve<T>(tag: DependencyTagConvertible? = nil, builder: (() 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)
|
||||
}
|
||||
|
||||
public func resolve(_ type: Any.Type, tag: DependencyTagConvertible? = nil, builder: (() throws -> Any) throws -> Any) throws -> Any {
|
||||
return try _resolve(type: type, tag: tag, builder: builder)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension DependencyContainer {
|
||||
|
||||
func _resolve<T>(tag aTag: DependencyTagConvertible? = nil, builder: (() throws -> T) throws -> T) throws -> T {
|
||||
return try resolve(T.self, tag: aTag, builder: { factory in
|
||||
try withoutActuallyEscaping(factory, do: { (factory) throws -> T in
|
||||
try builder({ try factory() as! T })
|
||||
})
|
||||
}) as! T
|
||||
}
|
||||
|
||||
func _resolve(type aType: Any.Type, tag: DependencyTagConvertible? = nil, builder: (() throws -> Any) throws -> Any) throws -> Any {
|
||||
let key = DefinitionKey(type: aType, typeOfArguments: Void.self, tag: tag?.dependencyTag)
|
||||
|
||||
return try inContext(key:key, injectedInType: context?.resolvingType) {
|
||||
try self._resolve(key: key, builder: { definition in
|
||||
try builder { try definition.weakFactory(()) }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 withoutActuallyEscaping(factory, do: { (factory) throws -> T 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 {
|
||||
do {
|
||||
return try autowire(key: aKey)
|
||||
} catch {
|
||||
if let resolved = collaboratingResolve(key: aKey, builder: builder) {
|
||||
return resolved
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, context: context] = 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, context: context] 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, context: context] as? T {
|
||||
return previouslyResolved
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Searches for definition that matches provided key
|
||||
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 sharedSingletonsBox = Box<[DefinitionKey: Any]>([:])
|
||||
var sharedSingletons: [DefinitionKey: Any] {
|
||||
get { return sharedSingletonsBox.unboxed }
|
||||
set { sharedSingletonsBox.unboxed = newValue }
|
||||
}
|
||||
var singletons = [DefinitionKey: Any]()
|
||||
|
||||
var sharedWeakSingletonsBox = Box<[DefinitionKey: Any]>([:])
|
||||
var sharedWeakSingletons: [DefinitionKey: Any] {
|
||||
get { return sharedWeakSingletonsBox.unboxed }
|
||||
set { sharedWeakSingletonsBox.unboxed = newValue }
|
||||
}
|
||||
var weakSingletons = [DefinitionKey: Any]()
|
||||
|
||||
subscript(key key: DefinitionKey, inScope scope: ComponentScope, context context: DependencyContainer.Context) -> Any? {
|
||||
get {
|
||||
switch scope {
|
||||
case .singleton, .eagerSingleton:
|
||||
return context.inCollaboration ? sharedSingletons[key] : singletons[key]
|
||||
case .weakSingleton:
|
||||
let singletons = context.inCollaboration ? sharedWeakSingletons : weakSingletons
|
||||
if let boxed = singletons[key] as? WeakBoxType { return boxed.unboxed }
|
||||
else { return singletons[key] }
|
||||
case .shared:
|
||||
return resolvedInstances[key]
|
||||
case .unique:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
set {
|
||||
switch scope {
|
||||
case .singleton, .eagerSingleton:
|
||||
sharedSingletons[key] = newValue
|
||||
singletons[key] = newValue
|
||||
case .weakSingleton:
|
||||
sharedWeakSingletons[key] = newValue
|
||||
weakSingletons[key] = newValue
|
||||
case .shared:
|
||||
resolvedInstances[key] = newValue
|
||||
case .unique:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//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()
|
||||
}
|
||||
|
||||
extension Resolvable {
|
||||
func resolveDependencies(_ container: DependencyContainer) {}
|
||||
func didResolveDependencies() {}
|
||||
}
|
||||
+121
-35
@@ -22,10 +22,95 @@
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
// MARK: - Register/resolve dependencies with 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
|
||||
|
||||
/**
|
||||
@@ -33,7 +118,7 @@ extension DependencyContainer {
|
||||
|
||||
- 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.
|
||||
__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`.
|
||||
@@ -42,7 +127,7 @@ extension DependencyContainer {
|
||||
|
||||
- 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> {
|
||||
@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)) }
|
||||
}
|
||||
|
||||
@@ -68,13 +153,13 @@ extension DependencyContainer {
|
||||
|
||||
- returns: An instance of type `T`.
|
||||
|
||||
- seealso: `register(tag:_:factory:)`, `resolve(tag:builder:)`
|
||||
- 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:)`
|
||||
///- 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) }
|
||||
}
|
||||
@@ -82,84 +167,85 @@ extension DependencyContainer {
|
||||
// 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)) }
|
||||
@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:withArguments:)`
|
||||
/// - 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) }
|
||||
return try resolve(T.self, tag: tag) { factory in try factory((arg1, arg2)) } as! T
|
||||
}
|
||||
|
||||
///- seealso: `resolve(_:tag:)`, `resolve(tag:withArguments:)`
|
||||
///- 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(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: `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:withArguments:)`
|
||||
/// - 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) }
|
||||
return try resolve(T.self, tag: tag) { factory in try factory((arg1, arg2, arg3)) } as! T
|
||||
}
|
||||
|
||||
///- seealso: `resolve(_:tag:)`, `resolve(tag:withArguments:)`
|
||||
///- 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(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: `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:withArguments:)`
|
||||
/// - 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) }
|
||||
return try resolve(T.self, tag: tag) { factory in try factory((arg1, arg2, arg3, arg4)) } as! T
|
||||
}
|
||||
|
||||
/// - seealso: `resolve(_:tag:)`, `resolve(tag:withArguments:)`
|
||||
/// - 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(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: `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:withArguments:)`
|
||||
/// - 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) }
|
||||
return try resolve(T.self, tag: tag) { factory in try factory((arg1, arg2, arg3, arg4, arg5)) } as! T
|
||||
}
|
||||
|
||||
///- seealso: `resolve(_:tag:)`, `resolve(tag:withArguments:)`
|
||||
///- 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(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: `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:withArguments:)`
|
||||
/// - 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) }
|
||||
return try resolve(T.self, tag: tag) { factory in try factory((arg1, arg2, arg3, arg4, arg5, arg6)) } as! T
|
||||
}
|
||||
|
||||
/// - seealso: `resolve(_:tag:)`, `resolve(tag:withArguments:)`
|
||||
/// - 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)) }
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
protocol TypeForwardingDefinition: DefinitionType {
|
||||
var implementingTypes: [Any.Type] { get }
|
||||
func doesImplements(_ type: Any.Type) -> Bool
|
||||
func doesImplements(type aType: Any.Type) -> Bool
|
||||
}
|
||||
|
||||
extension Definition {
|
||||
@@ -82,29 +82,16 @@ extension Definition {
|
||||
}
|
||||
|
||||
///Registers definition for types passed as parameters
|
||||
@discardableResult public func implements<A, B, C, D>(_ a: A.Type, _ b: B.Type, c: C.Type, d: D.Type) -> 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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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 +106,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 +118,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 +130,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) })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-28
@@ -22,16 +22,19 @@
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public enum LogLevel {
|
||||
case Verbose
|
||||
case Errors
|
||||
public enum LogLevel: Int {
|
||||
case None
|
||||
case Errors
|
||||
case Verbose
|
||||
}
|
||||
|
||||
public var logLevel: LogLevel = .Errors
|
||||
|
||||
func log(_ logLevel: LogLevel, _ message: Any) {
|
||||
guard case logLevel = Dip.logLevel else { return }
|
||||
print(message)
|
||||
public var logger: (LogLevel, Any) -> Void = { print($1) }
|
||||
|
||||
func log(level logLevel: LogLevel, _ message: Any) {
|
||||
guard logLevel.rawValue <= Dip.logLevel.rawValue else { return }
|
||||
logger(logLevel, message)
|
||||
}
|
||||
|
||||
///Internal protocol used to unwrap optional values.
|
||||
@@ -41,19 +44,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,10 +72,10 @@ class WeakBox<T>: WeakBoxType {
|
||||
}
|
||||
|
||||
init(_ value: T) {
|
||||
#if os(Linux)
|
||||
weak var value: AnyObject? = value as? AnyObject
|
||||
#else
|
||||
#if _runtime(_ObjC)
|
||||
weak var value: AnyObject? = value as AnyObject
|
||||
#else
|
||||
weak var value: AnyObject? = value as? AnyObject
|
||||
#endif
|
||||
guard value != nil else {
|
||||
fatalError("Can not store weak reference to not a class instance (\(T.self))")
|
||||
@@ -106,17 +103,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)
|
||||
#if !_runtime(_ObjC)
|
||||
import Glibc
|
||||
class RecursiveLock {
|
||||
private var _lock = _initializeRecursiveMutex()
|
||||
|
||||
@@ -26,13 +26,13 @@ import XCTest
|
||||
@testable import Dip
|
||||
|
||||
private protocol Server: class {
|
||||
weak var client: Client? {get}
|
||||
var anotherClient: Client? {get set}
|
||||
weak var client: Client! {get}
|
||||
var anotherClient: Client! {get set}
|
||||
}
|
||||
|
||||
private protocol Client: class {
|
||||
var server: Server? {get}
|
||||
var anotherServer: Server? {get set}
|
||||
var anotherServer: Server! {get set}
|
||||
}
|
||||
|
||||
private class ServerImp: Server {
|
||||
@@ -41,11 +41,11 @@ private class ServerImp: Server {
|
||||
AutoInjectionTests.clientDidInjectCalled = true
|
||||
}
|
||||
|
||||
var client: Client? {
|
||||
var client: Client! {
|
||||
return _client.value
|
||||
}
|
||||
|
||||
weak var anotherClient: Client?
|
||||
weak var anotherClient: Client!
|
||||
|
||||
weak var _optionalProperty = InjectedWeak<AnyObject>(required: false)
|
||||
}
|
||||
@@ -60,7 +60,7 @@ private class ClientImp: Client {
|
||||
return _server.value
|
||||
}
|
||||
|
||||
var anotherServer: Server?
|
||||
var anotherServer: Server!
|
||||
|
||||
var _optionalProperty = Injected<AnyObject>(required: false)
|
||||
|
||||
|
||||
@@ -31,13 +31,13 @@ private class ServiceImp2: Service { }
|
||||
private class ServiceImp3 {}
|
||||
|
||||
private protocol AutoWiredClient: class {
|
||||
var service1: Service? { get set }
|
||||
var service2: Service? { get set }
|
||||
var service1: Service! { get set }
|
||||
var service2: Service! { get set }
|
||||
}
|
||||
|
||||
private class AutoWiredClientImp: AutoWiredClient {
|
||||
var service1: Service?
|
||||
var service2: Service?
|
||||
var service1: Service!
|
||||
var service2: Service!
|
||||
|
||||
init(service1: Service?, service2: ServiceImp2) {
|
||||
self.service1 = service1
|
||||
@@ -55,7 +55,9 @@ class AutoWiringTests: XCTestCase {
|
||||
("testThatItCanResolveWithAutoWiring", testThatItCanResolveWithAutoWiring),
|
||||
("testThatItUsesAutoWireFactoryWithMostNumberOfArguments", testThatItUsesAutoWireFactoryWithMostNumberOfArguments),
|
||||
("testThatItThrowsAmbiguityErrorWhenUsingAutoWire", testThatItThrowsAmbiguityErrorWhenUsingAutoWire),
|
||||
("testThatItFirstTriesToUseTaggedFactoriesWhenUsingAutoWire", testThatItFirstTriesToUseTaggedFactoriesWhenUsingAutoWire),
|
||||
("testThatItUsesAutoWireFactoryWithMostNumberOfArguments", testThatItUsesAutoWireFactoryWithMostNumberOfArguments),
|
||||
("testThatItPrefersTaggedFactoryWithDifferentNumberOfArgumentsWhenUsingAutoWire", testThatItPrefersTaggedFactoryWithDifferentNumberOfArgumentsWhenUsingAutoWire),
|
||||
("testThatItPrefersTaggedFactoryWithDifferentTypesOfArgumentsWhenUsingAutoWire", testThatItPrefersTaggedFactoryWithDifferentTypesOfArgumentsWhenUsingAutoWire),
|
||||
("testThatItFallbackToNotTaggedFactoryWhenUsingAutoWire", testThatItFallbackToNotTaggedFactoryWhenUsingAutoWire),
|
||||
("testThatItDoesNotTryToUseAutoWiringWhenCallingResolveWithArguments", testThatItDoesNotTryToUseAutoWiringWhenCallingResolveWithArguments),
|
||||
("testThatItDoesNotUseAutoWiringWhenFailedToResolveLowLevelDependency", testThatItDoesNotUseAutoWiringWhenFailedToResolveLowLevelDependency),
|
||||
@@ -110,7 +112,7 @@ class AutoWiringTests: XCTestCase {
|
||||
//2 args
|
||||
var factoryWithMostNumberOfArgumentsCalled = false
|
||||
container.register { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }
|
||||
.resolvingProperties { _ in
|
||||
.resolvingProperties { _,_ in
|
||||
factoryWithMostNumberOfArgumentsCalled = true
|
||||
}
|
||||
|
||||
@@ -146,7 +148,7 @@ class AutoWiringTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
func testThatItFirstTriesToUseTaggedFactoriesWhenUsingAutoWire() {
|
||||
func testThatItPrefersTaggedFactoryWithDifferentNumberOfArgumentsWhenUsingAutoWire() {
|
||||
//given
|
||||
|
||||
//1 arg
|
||||
@@ -162,7 +164,7 @@ class AutoWiringTests: XCTestCase {
|
||||
container.register(tag: "tag") { AutoWiredClientImp(service1: $0, service2: try self.container.resolve()) as AutoWiredClient }
|
||||
|
||||
//2 arg tagged
|
||||
container.register(tag: "tag") { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }.resolvingProperties { _ in
|
||||
container.register(tag: "tag") { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }.resolvingProperties { _,_ in
|
||||
taggedFactoryWithMostNumberOfArgumentsCalled = true
|
||||
}
|
||||
|
||||
@@ -176,12 +178,37 @@ class AutoWiringTests: XCTestCase {
|
||||
XCTAssertTrue(taggedFactoryWithMostNumberOfArgumentsCalled)
|
||||
}
|
||||
|
||||
func testThatItPrefersTaggedFactoryWithDifferentTypesOfArgumentsWhenUsingAutoWire() {
|
||||
//given
|
||||
|
||||
//1 arg
|
||||
container.register { AutoWiredClientImp(service1: $0, service2: try self.container.resolve()) as AutoWiredClient }
|
||||
|
||||
//2 args
|
||||
container.register { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }
|
||||
|
||||
//1 arg tagged
|
||||
var taggedFactoryCalled = false
|
||||
container.register(tag: "tag") { AutoWiredClientImp(service1: try self.container.resolve(), service2: $0) as AutoWiredClient }.resolvingProperties { _,_ in
|
||||
taggedFactoryCalled = true
|
||||
}
|
||||
|
||||
container.register() { ServiceImp1() as Service }
|
||||
container.register { ServiceImp2() }
|
||||
|
||||
//when
|
||||
let _ = try! container.resolve(tag: "tag") as AutoWiredClient
|
||||
|
||||
//then
|
||||
XCTAssertTrue(taggedFactoryCalled)
|
||||
}
|
||||
|
||||
func testThatItFallbackToNotTaggedFactoryWhenUsingAutoWire() {
|
||||
//given
|
||||
|
||||
//1 arg
|
||||
var notTaggedFactoryWithMostNumberOfArgumentsCalled = false
|
||||
container.register { AutoWiredClientImp(service1: $0, service2: try self.container.resolve()) as AutoWiredClient }.resolvingProperties { _ in
|
||||
container.register { AutoWiredClientImp(service1: $0, service2: try self.container.resolve()) as AutoWiredClient }.resolvingProperties { _,_ in
|
||||
notTaggedFactoryWithMostNumberOfArgumentsCalled = true
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -63,7 +63,7 @@ class ContextTests: XCTestCase {
|
||||
XCTAssertTrue(self.container.context.resolvingType == Service.self)
|
||||
let _ = try self.container.resolve() as ServiceImp1
|
||||
return ServiceImp1() as Service
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertTrue(self.container.context.resolvingType == Service.self)
|
||||
let _ = try self.container.resolve() as ServiceImp1
|
||||
}
|
||||
@@ -71,7 +71,7 @@ class ContextTests: XCTestCase {
|
||||
container.register { () -> ServiceImp1 in
|
||||
XCTAssertTrue(self.container.context.resolvingType == ServiceImp1.self)
|
||||
return ServiceImp1()
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertTrue(self.container.context.resolvingType == ServiceImp1.self)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ class ContextTests: XCTestCase {
|
||||
XCTAssertNil(self.container.context.injectedInType)
|
||||
let _ = try self.container.resolve() as ServiceImp1
|
||||
return ServiceImp1() as Service
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertNil(self.container.context.injectedInType)
|
||||
let _ = try self.container.resolve() as ServiceImp1
|
||||
}
|
||||
@@ -91,7 +91,7 @@ class ContextTests: XCTestCase {
|
||||
container.register { () -> ServiceImp1 in
|
||||
XCTAssertTrue(self.container.context.injectedInType == Service.self)
|
||||
return ServiceImp1()
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertTrue(self.container.context.injectedInType == Service.self)
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ class ContextTests: XCTestCase {
|
||||
XCTAssertTrue(DependencyContainer.Tag.String("tag") ~= self.container.context.tag!)
|
||||
let _ = try self.container.resolve(tag: "otherTag") as ServiceImp1
|
||||
return ServiceImp1() as Service
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertNotNil(self.container.context.tag)
|
||||
XCTAssertTrue(DependencyContainer.Tag.String("tag") ~= self.container.context.tag!)
|
||||
let _ = try self.container.resolve(tag: "otherTag") as ServiceImp1
|
||||
@@ -114,7 +114,7 @@ class ContextTests: XCTestCase {
|
||||
XCTAssertNotNil(self.container.context.tag)
|
||||
XCTAssertTrue(DependencyContainer.Tag.String("otherTag") ~= self.container.context.tag!)
|
||||
return ServiceImp1()
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertNotNil(self.container.context.tag)
|
||||
XCTAssertTrue(DependencyContainer.Tag.String("otherTag") ~= self.container.context.tag!)
|
||||
}
|
||||
@@ -135,7 +135,7 @@ class ContextTests: XCTestCase {
|
||||
XCTAssertTrue(DependencyContainer.Tag.String("injectedTag") ~= self.container.context.tag!)
|
||||
}
|
||||
return ServiceImp2()
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
if self.container.context.injectedInProperty == "injectedNilTag" {
|
||||
XCTAssertNil(self.container.context.tag)
|
||||
}
|
||||
@@ -149,7 +149,7 @@ class ContextTests: XCTestCase {
|
||||
XCTAssertNotNil(self.container.context.tag)
|
||||
XCTAssertTrue(DependencyContainer.Tag.String("tag") ~= self.container.context.tag!)
|
||||
return ServiceImp2()
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertNotNil(self.container.context.tag)
|
||||
XCTAssertTrue(DependencyContainer.Tag.String("tag") ~= self.container.context.tag!)
|
||||
}
|
||||
@@ -160,14 +160,14 @@ class ContextTests: XCTestCase {
|
||||
func testThatContextStoresTheTagPassedToResolveWhenAutoWiring() {
|
||||
container.register { (_: ServiceImp1) -> Service in
|
||||
return ServiceImp1() as Service
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
}
|
||||
|
||||
container.register { () -> ServiceImp1 in
|
||||
XCTAssertNotNil(self.container.context.tag)
|
||||
XCTAssertTrue(DependencyContainer.Tag.String("tag") ~= self.container.context.tag!)
|
||||
return ServiceImp1()
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertNotNil(self.container.context.tag)
|
||||
XCTAssertTrue(DependencyContainer.Tag.String("tag") ~= self.container.context.tag!)
|
||||
}
|
||||
@@ -181,7 +181,7 @@ class ContextTests: XCTestCase {
|
||||
XCTAssertTrue(DependencyContainer.Tag.String("tag") ~= self.container.context.tag!)
|
||||
let _ = try self.container.resolve() as ServiceImp1
|
||||
return ServiceImp1() as Service
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertNotNil(self.container.context.tag)
|
||||
XCTAssertTrue(DependencyContainer.Tag.String("tag") ~= self.container.context.tag!)
|
||||
let _ = try self.container.resolve() as ServiceImp1
|
||||
@@ -190,7 +190,7 @@ class ContextTests: XCTestCase {
|
||||
container.register { () -> ServiceImp1 in
|
||||
XCTAssertNil(self.container.context.tag)
|
||||
return ServiceImp1()
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertNil(self.container.context.tag)
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ class ContextTests: XCTestCase {
|
||||
XCTAssertNotNil(self.container.context.injectedInProperty)
|
||||
XCTAssertTrue(names.contains(self.container.context.injectedInProperty!))
|
||||
return ServiceImp2()
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertNotNil(self.container.context.injectedInProperty)
|
||||
XCTAssertTrue(names.contains(self.container.context.injectedInProperty!))
|
||||
}
|
||||
@@ -239,7 +239,7 @@ class ContextTests: XCTestCase {
|
||||
XCTAssertTrue(self.container.context.resolvingType == Service.self)
|
||||
let _ = try self.container.resolve() as ServiceImp1
|
||||
return ServiceImp1() as Service
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertTrue(self.container.context.resolvingType == Service.self)
|
||||
let _ = try self.container.resolve() as ServiceImp1
|
||||
}
|
||||
@@ -247,7 +247,7 @@ class ContextTests: XCTestCase {
|
||||
collaborator.register { () -> ServiceImp1 in
|
||||
XCTAssertTrue(collaborator.context.resolvingType == ServiceImp1.self)
|
||||
return ServiceImp1()
|
||||
}.resolvingProperties { _ in
|
||||
}.resolvingProperties { _,_ in
|
||||
XCTAssertTrue(collaborator.context.resolvingType == ServiceImp1.self)
|
||||
}
|
||||
|
||||
|
||||
+165
-30
@@ -30,10 +30,10 @@ private class ServiceImp1: Service { }
|
||||
private class ServiceImp2: Service { }
|
||||
|
||||
private protocol Server: class {
|
||||
weak var client: Client? { get }
|
||||
weak var client: Client! { get }
|
||||
}
|
||||
private protocol Client: class {
|
||||
var server: Server? { get }
|
||||
var server: Server! { get }
|
||||
}
|
||||
|
||||
class ResolvableService: Service, Resolvable {
|
||||
@@ -70,6 +70,7 @@ class DipTests: XCTestCase {
|
||||
("testThatCollaboratingWithSelfIsIgnored", testThatCollaboratingWithSelfIsIgnored),
|
||||
("testThatCollaboratingContainersAreWeakReferences", testThatCollaboratingContainersAreWeakReferences),
|
||||
("testThatCollaboratingContainersReuseInstancesResolvedByAnotherContainer", testThatCollaboratingContainersReuseInstancesResolvedByAnotherContainer),
|
||||
("testThatItCanHandleSeparateContainersAndTheirCollaboration", testThatItCanHandleSeparateContainersAndTheirCollaboration)
|
||||
]
|
||||
}()
|
||||
|
||||
@@ -483,8 +484,8 @@ class DipTests: XCTestCase {
|
||||
func testThatItResolvesCircularDependencies() {
|
||||
|
||||
class ResolvableServer: Server, Resolvable {
|
||||
weak var client: Client?
|
||||
weak var secondClient: Client?
|
||||
weak var client: Client!
|
||||
weak var secondClient: Client!
|
||||
|
||||
init(client: Client) {
|
||||
self.client = client
|
||||
@@ -505,17 +506,9 @@ class DipTests: XCTestCase {
|
||||
|
||||
}
|
||||
|
||||
//Due to a bug in Swift 3 Mirror fails if weak property is not NSObject
|
||||
//https://bugs.swift.org/browse/SR-2144
|
||||
class ResolvableClient: NSObject, Client, Resolvable {
|
||||
var server: Server?
|
||||
var secondServer: Server?
|
||||
|
||||
#if os(Linux)
|
||||
init() {}
|
||||
#else
|
||||
override init() { super.init() }
|
||||
#endif
|
||||
class ResolvableClient: Client, Resolvable {
|
||||
var server: Server!
|
||||
var secondServer: Server!
|
||||
|
||||
var didResolveDependenciesCalled = false
|
||||
|
||||
@@ -581,12 +574,12 @@ class DipTests: XCTestCase {
|
||||
}.implements(Service.self)
|
||||
|
||||
container.register(tag: "tag") { ServiceImp2() as Service }
|
||||
.resolvingProperties { _ in
|
||||
.resolvingProperties { _,_ in
|
||||
createdService2 = true
|
||||
}
|
||||
|
||||
container.register { (arg: String) in ServiceImp1() }
|
||||
.resolvingProperties { _ in
|
||||
.resolvingProperties { _,_ in
|
||||
createdService3 = true
|
||||
}
|
||||
|
||||
@@ -666,6 +659,7 @@ extension DipTests {
|
||||
//given
|
||||
let collaborator = DependencyContainer()
|
||||
collaborator.register { ResolvableService() as Service }
|
||||
container.register { "something" }
|
||||
|
||||
//when
|
||||
container.collaborate(with: collaborator)
|
||||
@@ -673,13 +667,14 @@ extension DipTests {
|
||||
//then
|
||||
AssertNoThrow(expression: try container.resolve() as Service)
|
||||
AssertNoThrow(expression: try container.resolve(Service.self))
|
||||
AssertNoThrow(expression: try collaborator.resolve() as String)
|
||||
AssertNoThrow(expression: try collaborator.resolve(String.self))
|
||||
}
|
||||
|
||||
func testThatCollaboratingWithSelfIsIgnored() {
|
||||
let collaborator = DependencyContainer()
|
||||
collaborator.collaborate(with: collaborator)
|
||||
XCTAssertTrue(collaborator._collaborators.isEmpty, "Container should not collaborate with itself")
|
||||
|
||||
}
|
||||
|
||||
func testThatCollaboratingContainersAreWeakReferences() {
|
||||
@@ -698,21 +693,13 @@ extension DipTests {
|
||||
func testThatCollaboratingContainersReuseInstancesResolvedByAnotherContainer() {
|
||||
//given
|
||||
class ServerImp: Server {
|
||||
weak var client: Client?
|
||||
weak var client: Client!
|
||||
init(client: Client) { self.client = client }
|
||||
}
|
||||
|
||||
//Due to a bug in Swift 3 Mirror fails if weak property is not NSObject
|
||||
//https://bugs.swift.org/browse/SR-2144
|
||||
class ClientImp: NSObject, Client {
|
||||
var server: Server?
|
||||
var anotherServer: Server?
|
||||
|
||||
#if os(Linux)
|
||||
init() {}
|
||||
#else
|
||||
override init() { super.init() }
|
||||
#endif
|
||||
class ClientImp: Client {
|
||||
var server: Server!
|
||||
var anotherServer: Server!
|
||||
}
|
||||
|
||||
let serverContainer = DependencyContainer()
|
||||
@@ -746,4 +733,152 @@ extension DipTests {
|
||||
XCTAssertTrue(client?.server === (client as? ClientImp)?.anotherServer)
|
||||
}
|
||||
|
||||
func testThatCollaborationReferencesAreRecursivelyUpdate() {
|
||||
let container = DependencyContainer()
|
||||
container.register(.singleton){ ResolvableService() as Service }
|
||||
|
||||
//when
|
||||
let collaborator1 = DependencyContainer()
|
||||
let collaborator2 = DependencyContainer()
|
||||
let collaborator3 = DependencyContainer()
|
||||
let collaborator4 = DependencyContainer()
|
||||
|
||||
collaborator1.collaborate(with: container)
|
||||
XCTAssertTrue(collaborator1.resolvedInstances.sharedSingletonsBox === container.resolvedInstances.sharedSingletonsBox)
|
||||
|
||||
collaborator2.collaborate(with: container)
|
||||
XCTAssertTrue(collaborator2.resolvedInstances.sharedSingletonsBox === container.resolvedInstances.sharedSingletonsBox)
|
||||
|
||||
collaborator3.collaborate(with: collaborator1)
|
||||
XCTAssertTrue(collaborator3.resolvedInstances.sharedSingletonsBox === container.resolvedInstances.sharedSingletonsBox)
|
||||
|
||||
collaborator4.collaborate(with: collaborator2)
|
||||
XCTAssertTrue(collaborator4.resolvedInstances.sharedSingletonsBox === container.resolvedInstances.sharedSingletonsBox)
|
||||
|
||||
let service1 = try! collaborator1.resolve() as Service
|
||||
let service2 = try! collaborator2.resolve() as Service
|
||||
let service3 = try! collaborator3.resolve() as Service
|
||||
let service4 = try! collaborator4.resolve() as Service
|
||||
let serviceRoot = try! container.resolve() as Service
|
||||
|
||||
XCTAssertTrue(service1 === service2)
|
||||
XCTAssertTrue(service1 === service3)
|
||||
XCTAssertTrue(service1 === service4)
|
||||
|
||||
XCTAssertTrue(service1 === serviceRoot)
|
||||
XCTAssertTrue(service2 === serviceRoot)
|
||||
XCTAssertTrue(service3 === serviceRoot)
|
||||
XCTAssertTrue(service4 === serviceRoot)
|
||||
}
|
||||
|
||||
class RootService {}
|
||||
class ServiceClient {
|
||||
let name: String
|
||||
let service: RootService
|
||||
init(name: String, service: RootService) {
|
||||
self.name = name
|
||||
self.service = service
|
||||
}
|
||||
}
|
||||
|
||||
func testThatContainersShareTheirSingletonsOnlyWithCollaborators() {
|
||||
let container = DependencyContainer()
|
||||
container.register(.singleton) { RootService() }
|
||||
|
||||
let collaborator1 = DependencyContainer()
|
||||
collaborator1.register(.singleton) {
|
||||
ServiceClient(name: "1", service: $0)
|
||||
}
|
||||
|
||||
let collaborator2 = DependencyContainer()
|
||||
collaborator2.register(.singleton) {
|
||||
ServiceClient(name: "2", service: $0)
|
||||
}
|
||||
|
||||
collaborator1.collaborate(with: container)
|
||||
collaborator2.collaborate(with: container)
|
||||
|
||||
let client2 = try! collaborator2.resolve() as ServiceClient
|
||||
let client1 = try! collaborator1.resolve() as ServiceClient
|
||||
|
||||
XCTAssertEqual(client1.name, "1")
|
||||
XCTAssertEqual(client2.name, "2")
|
||||
XCTAssertTrue(client1.service === client2.service)
|
||||
}
|
||||
|
||||
func testThatContainerAutowireBeforeCollaboration() {
|
||||
let container = DependencyContainer()
|
||||
container.register(.singleton) { RootService() }
|
||||
|
||||
let collaborator1 = DependencyContainer()
|
||||
collaborator1.register(.singleton) {
|
||||
ServiceClient(name: "1", service: $0)
|
||||
}
|
||||
|
||||
let collaborator2 = DependencyContainer()
|
||||
collaborator2.register(.singleton) {
|
||||
ServiceClient(name: "2", service: $0)
|
||||
}
|
||||
|
||||
collaborator1.collaborate(with: container, collaborator2)
|
||||
collaborator2.collaborate(with: container, collaborator1)
|
||||
|
||||
let client2 = try! collaborator2.resolve() as ServiceClient
|
||||
let client1 = try! collaborator1.resolve() as ServiceClient
|
||||
|
||||
XCTAssertEqual(client1.name, "1")
|
||||
XCTAssertEqual(client2.name, "2")
|
||||
XCTAssertTrue(client1.service === client2.service)
|
||||
}
|
||||
}
|
||||
|
||||
class Manager {}
|
||||
class AnotherManager {}
|
||||
|
||||
class Object {
|
||||
let manager: Manager?
|
||||
|
||||
init(with container: DependencyContainer) {
|
||||
self.manager = try? container.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
class Owner {
|
||||
var manager: Manager?
|
||||
}
|
||||
|
||||
extension DipTests {
|
||||
func testThatItCanHandleSeparateContainersAndTheirCollaboration() {
|
||||
let container = self.container
|
||||
|
||||
let anotherContainer = DependencyContainer()
|
||||
anotherContainer.register { Object(with: anotherContainer) }
|
||||
|
||||
container.collaborate(with: anotherContainer)
|
||||
|
||||
container
|
||||
.register { Owner() }
|
||||
.resolvingProperties { $1.manager = try $0.resolve() }
|
||||
|
||||
container.register(.singleton) { AnotherManager() }
|
||||
container.register(.singleton) { Manager() }
|
||||
|
||||
let manager: Manager? = try? container.resolve()
|
||||
let another: AnotherManager? = try? container.resolve()
|
||||
var owner: Owner? = try? container.resolve(arguments: 1, "")
|
||||
|
||||
let object: Object? = try? container.resolve()
|
||||
owner = try? container.resolve()
|
||||
|
||||
let nonNilValues: [Any?] = [another, manager, owner, object, object?.manager]
|
||||
nonNilValues.forEach { XCTAssertNotNil($0) }
|
||||
|
||||
XCTAssertTrue(
|
||||
owner?.manager
|
||||
.flatMap { value in
|
||||
manager.flatMap { $0 === value }
|
||||
}
|
||||
?? false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ class RuntimeArgumentsTests: XCTestCase {
|
||||
|
||||
// let name3 = "3"
|
||||
// container.register { (port: Int, url: String!) in ServiceImp(name: name3, baseURL: url, port: port) as Service }
|
||||
// let service3 = try! container.resolve(withArguments: 80, "http://example.com" as String!) as Service
|
||||
// let service3 = try! container.resolve(arguments: 80, "http://example.com" as String!) as Service
|
||||
// XCTAssertEqual(service3.name, name3)
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import XCTest
|
||||
@testable import Dip
|
||||
|
||||
private protocol Server: class {
|
||||
var client: Client? { get set }
|
||||
var client: Client! { get set }
|
||||
}
|
||||
|
||||
private protocol Client: class {
|
||||
@@ -45,7 +45,7 @@ private func ==<T: ClientImp>(lhs: T, rhs: T) -> Bool {
|
||||
}
|
||||
|
||||
private class ServerImp: Server, Hashable {
|
||||
weak var client: Client?
|
||||
weak var client: Client!
|
||||
init() {}
|
||||
|
||||
var hashValue: Int {
|
||||
|
||||
@@ -154,9 +154,9 @@ class TypeForwardingTests: XCTestCase {
|
||||
var originalResolvingPropertiesCalled = false
|
||||
var resolvingPropertiesCalled = false
|
||||
container.register { ServiceImp1() }
|
||||
.resolvingProperties { _ in
|
||||
.resolvingProperties { _,_ in
|
||||
originalResolvingPropertiesCalled = true
|
||||
}.implements(Service.self) { _ in
|
||||
}.implements(Service.self) { _,_ in
|
||||
resolvingPropertiesCalled = true
|
||||
}
|
||||
|
||||
@@ -184,11 +184,11 @@ class TypeForwardingTests: XCTestCase {
|
||||
var resolvingPropertiesCalled = false
|
||||
container.reset()
|
||||
let definition = container.register { ServiceImp1() }
|
||||
.implements(Service.self) { container, object in
|
||||
.implements(Service.self) { _,_ in
|
||||
resolvingPropertiesCalled = true
|
||||
}
|
||||
|
||||
definition.resolvingProperties { _ in
|
||||
definition.resolvingProperties { _,_ in
|
||||
originalResolvingPropertiesCalled = true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user