Compare commits

..

87 Commits

Author SHA1 Message Date
Ilya Puchka 7ec008a5c0 Merge pull request #237 from michalsrutek/develop
Fix couple of typos
2020-05-29 14:42:25 +01:00
Ilya Puchka 772bc13daa Merge pull request #238 from michalsrutek/use-anyobject-keyword
Use preferred AnyObject keyword
2020-05-29 14:42:03 +01:00
Michal Srutek fa4325399a Use preferred AnyObject keyword 2020-04-23 10:22:20 +02:00
Michal Srutek f7fbbda748 Fix couple of typos 2020-04-23 09:59:03 +02:00
Ilya Puchka 56b0768d60 bump version to 7.1.1 2019-12-27 11:14:55 +00:00
Ilya Puchka 8cf87efb95 Update podspec author 2019-12-27 11:07:21 +00:00
Ilya Puchka 3aa9b334f1 Merge pull request #233 from AliSoftware/spm-fix
Enabled StoryboardInstantiatable for SPM builds
2019-11-15 11:53:25 +00:00
Ilya Puchka b2a0cda242 Update StoryboardInstantiatable.swift 2019-11-13 13:16:52 +00:00
Ilya Puchka 82e3d02497 Merge branch 'master' into develop 2019-11-11 14:22:24 +00:00
Ilya Puchka 3b2f5a272f bumpe version to 7.1.0 2019-11-10 16:11:17 +00:00
Ilya Puchka eab2d424e7 allow to disable thread safety on container level 2019-11-10 16:01:20 +00:00
Ilya Puchka 0f2cda0a52 update changelog 2019-11-10 15:53:45 +00:00
Ilya Puchka 05c02c9645 Merge pull request #225 from AliSoftware/property-delegates
Property wrappers
2019-11-10 15:52:49 +00:00
Ilya Puchka fa086dad8f add xcode 11 and swift 5.1 to travis build 2019-11-10 15:35:53 +00:00
Ilya Puchka cdc09f5a03 removed unneeded initial nil values 2019-11-10 15:22:38 +00:00
Ilya Puchka b94ed3bea2 fix warning 2019-11-10 15:22:16 +00:00
Ilya Puchka 0fd70e65dd Merge branch 'develop' into property-delegates 2019-11-10 15:13:55 +00:00
Ilya Puchka 97e7f4782f Merge branch 'pr/221' into develop 2019-11-10 15:12:41 +00:00
Ilya Puchka f1fdbbc988 code review 2019-11-10 15:02:03 +00:00
Ilya Puchka e368bb3051 Merge branch 'develop' into nested_types 2019-11-10 14:50:07 +00:00
Ilya Puchka 4ce4832960 Merge pull request #229 from dchohfi/develop
Bump watchos deployment target to 3.0
2019-10-28 08:46:22 +00:00
Diego Chohfi d3bd0c27e3 Bump watchos deployment target to 3.0 2019-10-21 10:40:46 -07:00
Ilya Puchka be0c39ab6b update for beta 3 2019-07-07 23:54:58 +01:00
Ilya Puchka 0de45e7d53 fixed checking for new injected value 2019-07-07 23:53:45 +01:00
Ilya Puchka 3b421d0cff refactor property wrappers to struct 2019-07-07 16:16:50 +01:00
Ilya Puchka f1234bf2b9 introduce swift 5.1 property delegates 2019-06-30 19:12:42 +01:00
Ilya Puchka b0d7153bfe ignore swiftpm files 2019-06-30 18:27:59 +01:00
Ilya Puchka 0193aa6bf6 Merge pull request #224 from AliSoftware/swift5
Swift 5
2019-06-30 17:17:49 +01:00
Ilya Puchka de7ad3ac48 removed linux thread safety tests, regenerate test manifest 2019-06-30 16:59:10 +01:00
Ilya Puchka 40ad490c48 removed unneeded code 2019-06-30 15:53:23 +01:00
Ilya Puchka 95cc5df26f fixed Linux tests 2019-06-30 15:40:40 +01:00
Ilya Puchka 09a7115aff fix tests 2019-06-30 14:29:22 +01:00
Ilya Puchka 15c0002ea3 remove compatibility file 2019-06-12 01:18:14 +01:00
Ilya Puchka 0644d85a9c update swift version in all targets 2019-06-12 01:18:05 +01:00
Ilya Puchka e1522a0bf3 Merge branch 'develop' into swift5 2019-06-12 01:15:06 +01:00
Ilya Puchka a659415496 generate LinuxMain 2019-06-11 23:23:39 +01:00
Ilya Puchka efe25d575b update package description 2019-06-11 23:10:22 +01:00
Ilya Puchka 22c67fa30c disable spec validation 2019-06-11 21:30:55 +01:00
Ilya Puchka 7a091da44d migrate to swift 5 2019-06-11 21:16:03 +01:00
s 9580976dd8 Print nested types in errors, hash keys using full typenames 2019-04-01 17:13:06 +02:00
Ilya Puchka a4f0256313 fixed typos 2018-12-20 01:32:21 +00:00
Ilya Puchka 815e9ccd66 shorthand method for resolving properties via keypaths 2018-12-20 01:30:35 +00:00
Ilya Puchka b9bf9c4d75 Merge pull request #216 from AliSoftware/develop
Release 7.0.1
2018-12-19 22:54:14 +00:00
Ilya Puchka 530a20999a use Cocoapods 1.4.0 as latest versions fail validation 2018-12-19 15:56:32 +00:00
Ilya Puchka 4f3bc1e498 Merge branch 'master' into release/7.0.1 2018-11-19 20:35:40 +00:00
Ilya Puchka 8dcb136ff0 fixed typos 2018-11-19 20:34:56 +00:00
Ilya Puchka 88d02af706 bump version to 7.0.1 2018-11-19 20:32:40 +00:00
Ilya Puchka a8777c067e Allow to disabled/enable auto injection for container or single definition. fixes #214, resolves #212 2018-11-19 00:03:15 +00:00
Ilya Puchka db40e8580c Added test for regression SR-8878 2018-11-19 00:03:15 +00:00
Ilya Puchka 334fb384a5 Update README.md 2018-11-18 23:54:49 +00:00
Ilya Puchka 54e1db2232 Merge pull request #207 from AliSoftware/develop
Release 7.0.0
2018-09-22 23:31:41 +03:00
Ilya Puchka c5c6bd2821 Merge branch 'master' into develop 2018-09-22 23:20:46 +03:00
Ilya Puchka 593129c63e update cocoapods on travis 2018-09-22 20:47:45 +01:00
Ilya Puchka 9a8d919a13 bump xcode and swift version on travis 2018-09-22 20:16:49 +01:00
Ilya Puchka 17a2d64361 bumped version to 7.0.0 2018-09-22 20:16:49 +01:00
Ilya Puchka 27037c2dd7 fix indentation in some docs 2018-09-22 20:01:16 +01:00
Ilya Puchka bd253f4b0a bump xcode and swift version on travis 2018-09-22 19:48:10 +01:00
Ilya Puchka 882b2a2031 bumped version to 7.0 2018-09-22 19:25:18 +01:00
Ilya Puchka 6a14c1f96d updated CHANGELOG 2018-09-22 19:24:00 +01:00
Ilya Puchka f60e322445 bumped swift version, removed unneded IUO tests 2018-09-22 19:19:45 +01:00
Ilya Puchka 77f6fde2fa Merge branch 'swift42' into develop 2018-09-22 19:16:27 +01:00
Ilya Puchka a8f7a2972a silence warning 2018-09-22 19:15:43 +01:00
Ilya Puchka ce9088afc7 fix reusing optionals 2018-09-22 18:47:34 +01:00
Ilya Puchka ef563c65ba fix build error on mac with SPM 2018-09-19 16:46:31 +03:00
Ilya Puchka 90ff39c720 fix build error on mac with SPM 2018-09-19 15:13:22 +03:00
Ilya Puchka e45dbd634f Merge with DipUI (#206)
* merge with DipUI
2018-09-19 15:02:26 +03:00
Bruno Virlet f81c267c61 Disable gathering code coverage (#198)
This causes issues whenn linking as a static library using Carthage
2018-07-25 18:10:51 +01:00
Michele Gruppioni d5b07b1916 Removed extension of ImplicitlyUnwrappedOptional (#191)
* Removed extension of ImplicitlyUnwrappedOptional

It was deprecated and has been removed in swift 4.2

* Added Swift Version Check
2018-07-13 11:43:40 +01:00
Ilya Puchka 33993df68a disable test coverage 2018-06-06 21:08:44 +01:00
Ilya Puchka dadb68862a fixed getting resolved instances 2018-06-06 01:15:08 +01:00
Ilya Puchka 2e53aa8ae2 Merge pull request #190 from AliSoftware/develop
Release 6.1
2018-04-28 20:24:35 +01:00
Ilya Puchka 142a348b20 removed unneded builds from travis 2018-04-28 20:05:53 +01:00
Ilya Puchka 88f101639d updated travis config 2018-04-28 19:48:03 +01:00
Ilya Puchka 5e70633ece bumped swift version to 4.1 2018-04-28 19:43:49 +01:00
Ilya Puchka af57aa26ed bumped version to 6.1 2018-04-28 19:41:45 +01:00
Ilya Puchka 6d6d77d603 fixed swift 4.1 warnings 2018-04-28 19:39:46 +01:00
Ilya Puchka 8d2f6ab8fb Revert #171 (#189)
* Revert "fix merge conflict"

This reverts commit aff01c541b.

* Revert "Merge branch 'develop' into develop"

This reverts commit a8dd47cee5, reversing
changes made to 9e7bd51bcd.
2018-04-28 19:21:05 +01:00
Ilya Puchka 2cc5310d4b Merge pull request #182 from tapsandswipes/develop
Fix crash with colaborator containers and weak singletons
2018-02-26 13:25:17 +00:00
Antonio Cabezuelo Vivo 5cb03a11bb Fix crash with colaborator containers and weak singletons
Signed-off-by: Antonio Cabezuelo Vivo <antonio@tapsandswipes.com>
2018-02-12 14:17:31 +01:00
Ilya Puchka aff01c541b fix merge conflict 2017-12-12 16:56:23 +01:00
Ilya Puchka a8dd47cee5 Merge branch 'develop' into develop 2017-12-12 16:51:32 +01:00
Ilya Puchka 12738a665f Merge pull request #171 from creditkarma/parent_child_containers
Parent child container support for Dip
2017-12-12 16:33:32 +01:00
Oleksa 'trimm' Korin 9e7bd51bcd Restructured tests, fixed crash 2017-10-30 23:49:44 +02:00
Oleksa 'trimm' Korin ce394e7d2b Added reproduction test 2017-10-30 23:26:36 +02:00
Oleksa 'trimm' Korin cd2c66f4a8 Initial try for collaboration reproduction crash 2017-10-30 20:45:51 +02:00
Ilya Puchka 5464f00bad Merge pull request #177 from AliSoftware/release/6.0
Release 6.0
2017-10-09 18:12:39 +02:00
John Twigg 3429c6ae0b Parent child container support for Dip 2017-08-02 10:41:03 -07:00
53 changed files with 1974 additions and 734 deletions
+1
View File
@@ -36,3 +36,4 @@ Carthage
# SPM
.build/
Packages
.swiftpm
-1
View File
@@ -1 +0,0 @@
4
+20 -12
View File
@@ -3,19 +3,27 @@ matrix:
- os: linux
include:
- script:
- set -o pipefail && xcodebuild test -workspace Dip.xcworkspace -scheme Dip -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6,OS=10.1' ONLY_ACTIVE_ARCH=NO | xcpretty -c
- 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
# disable running watchos until https://github.com/travis-ci/travis-ci/issues/7580 is fixed
#- 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=10.1' ONLY_ACTIVE_ARCH=NO | xcpretty -c
- pod spec lint --allow-warnings
# - pod spec lint --allow-warnings
- carthage build --no-skip-current
- swift package clean && swift build && swift test
os: osx
osx_image: xcode9
osx_image: xcode10.2
language: objective-c
before_install:
- gem install cocoapods --version 1.1.0.rc.2 --no-document
- gem install cocoapods --version 1.8.4 --no-document
- script:
- set -o pipefail && xcodebuild test -workspace Dip.xcworkspace -scheme Dip -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 11,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
# - pod spec lint --allow-warnings
- carthage build --no-skip-current
- swift package clean && swift build && swift test
os: osx
osx_image: xcode11.2
language: objective-c
before_install:
- gem install cocoapods --version 1.8.4 --no-document
- script:
- swift package clean && swift build && swift test
os: linux
@@ -25,8 +33,8 @@ matrix:
before_install:
- wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import -
- cd ..
- export SWIFT_VERSION=swift-4.0-RELEASE
- wget https://swift.org/builds/swift-4.0-release/ubuntu1404/$SWIFT_VERSION/$SWIFT_VERSION-ubuntu14.04.tar.gz
- export SWIFT_VERSION=swift-5.1-RELEASE
- wget https://swift.org/builds/swift-5.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 Dip
@@ -39,8 +47,8 @@ matrix:
before_install:
- wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import -
- cd ..
- export SWIFT_VERSION=swift-3.1-RELEASE
- wget https://swift.org/builds/swift-3.1-release/ubuntu1404/$SWIFT_VERSION/$SWIFT_VERSION-ubuntu14.04.tar.gz
- export SWIFT_VERSION=swift-5.0-RELEASE
- wget https://swift.org/builds/swift-5.0-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 Dip
+32
View File
@@ -1,5 +1,37 @@
# CHANGELOG
## Develop
## 7.1.1
* Fixed using `StoryboardInstantiatable` with SPM ([#233](https://github.com/AliSoftware/Dip/pull/233)).
## 7.1.0
* You can now use a shorthand syntax for resolving a single property using a key path, i.e. `resolvingProperty(\.value)`.
* Swift 5.0 support ([#224](https://github.com/AliSoftware/Dip/pull/224)).
* Fixed resolving nested types with the same local names ([#221](https://github.com/AliSoftware/Dip/pull/221)).
* `@Injected` and `@IntectedWeak` property wrappers ([#225](https://github.com/AliSoftware/Dip/pull/225)).
* Thread safety can be disabled on container level.
## 7.0.1
* Added a workaround for Swift 4.2 regression related to retaining weak properties ([#214](https://github.com/AliSoftware/Dip/issues/214)).
For that auto-injection can be disabled or enabled for the whole container or individula registrations.
## 7.0.0
* Swift 4.2 support.
* Fixed some issues when reusing instances previously resolved as optionals.
* Dip-UI is now part of Dip.
## 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
+4 -2
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Dip"
s.version = "6.0"
s.version = "7.1.1"
s.summary = "Dependency Injection for Swift made easy."
s.description = <<-DESC
@@ -11,7 +11,7 @@ Pod::Spec.new do |s|
s.homepage = "https://github.com/AliSoftware/Dip"
s.license = 'MIT'
s.authors = { "Olivier Halligon" => "olivier@halligon.net", "Ilya Puchka" => "ilya@puchka.me" }
s.authors = { "Olivier Halligon" => "olivier@halligon.net", "Ilya Puchka" => "ilyapuchka@gmail.com" }
s.source = { :git => "https://github.com/AliSoftware/Dip.git", :tag => s.version.to_s }
s.social_media_url = 'https://twitter.com/aligatr'
@@ -23,4 +23,6 @@ Pod::Spec.new do |s|
s.requires_arc = true
s.source_files = 'Sources/**/*.swift'
s.swift_version = "5.0", "5.1"
end
@@ -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>
+62 -10
View File
@@ -31,6 +31,8 @@
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 */; };
63937A6A21524C0B00AEE75A /* StoryboardInstantiatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63937A6921524C0B00AEE75A /* StoryboardInstantiatable.swift */; };
63937A6F21524DA300AEE75A /* DipUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63937A6B21524DA200AEE75A /* DipUITests.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -71,6 +73,11 @@
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>"; };
63937A6921524C0B00AEE75A /* StoryboardInstantiatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StoryboardInstantiatable.swift; path = ../../Sources/StoryboardInstantiatable.swift; sourceTree = "<group>"; };
63937A6B21524DA200AEE75A /* DipUITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DipUITests.swift; path = ../../Tests/DipTests/DipUITests.swift; sourceTree = "<group>"; };
63937A6C21524DA200AEE75A /* NSStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NSStoryboard.storyboard; sourceTree = "<group>"; };
63937A6D21524DA300AEE75A /* TVStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = TVStoryboard.storyboard; sourceTree = "<group>"; };
63937A6E21524DA300AEE75A /* UIStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = UIStoryboard.storyboard; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -108,6 +115,7 @@
095F829B1D043B41008CD706 /* TypeForwarding.swift */,
0982AF0B1C5183A000B62463 /* Utils.swift */,
09871B401DAA6BF300B40B91 /* Compatibility.swift */,
63937A6921524C0B00AEE75A /* StoryboardInstantiatable.swift */,
0919F4CB1C16417000DC3B10 /* Info.plist */,
);
path = Dip;
@@ -125,6 +133,10 @@
09BD350A1D84E30D00B33E53 /* RuntimeArgumentsTests.swift */,
09BD350B1D84E30D00B33E53 /* ThreadSafetyTests.swift */,
09BD350C1D84E30D00B33E53 /* TypeForwardingTests.swift */,
63937A6B21524DA200AEE75A /* DipUITests.swift */,
63937A6C21524DA200AEE75A /* NSStoryboard.storyboard */,
63937A6D21524DA300AEE75A /* TVStoryboard.storyboard */,
63937A6E21524DA300AEE75A /* UIStoryboard.storyboard */,
09BD350D1D84E30D00B33E53 /* Utils.swift */,
0919F4D11C16417000DC3B10 /* Info.plist */,
);
@@ -189,6 +201,7 @@
buildPhases = (
0903B35D1C161543002241C1 /* Sources */,
0903B35E1C161543002241C1 /* Frameworks */,
63937A7721524E3B00AEE75A /* ShellScript */,
0903B35F1C161543002241C1 /* Resources */,
);
buildRules = (
@@ -208,25 +221,26 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0900;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = AliSoftware;
TargetAttributes = {
0903B3571C161543002241C1 = {
CreatedOnToolsVersion = 7.1.1;
LastSwiftMigration = 0800;
LastSwiftMigration = 1020;
};
0903B3601C161543002241C1 = {
CreatedOnToolsVersion = 7.1.1;
LastSwiftMigration = 0800;
LastSwiftMigration = 1020;
};
};
};
buildConfigurationList = 0945268B1BEA1CFF0034E72A /* Build configuration list for PBXProject "Dip" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 094526871BEA1CFF0034E72A;
productRefGroup = 094526921BEA1CFF0034E72A /* Products */;
@@ -256,6 +270,26 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
63937A7721524E3B00AEE75A /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "echo \"${SRCROOT}/DipTests/${STORYBOARD_NAME_PREFIX}Storyboard.storyboard\"\nibtool --compilation-directory \"${TARGET_TEMP_DIR}\" \"${SRCROOT}/DipTests/${STORYBOARD_NAME_PREFIX}Storyboard.storyboard\"\nibtool --link \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\" \"${TARGET_TEMP_DIR}/${STORYBOARD_NAME_PREFIX}Storyboard.storyboardc\"\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
0903B3531C161543002241C1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
@@ -265,6 +299,7 @@
0982AF0C1C5183A000B62463 /* Utils.swift in Sources */,
0919F4D51C16417B00DC3B10 /* Definition.swift in Sources */,
09FC481E1DAAA8F900566AA8 /* ComponentScope.swift in Sources */,
63937A6A21524C0B00AEE75A /* StoryboardInstantiatable.swift in Sources */,
09B036001C5D2B83001EA5B7 /* AutoWiring.swift in Sources */,
0919F4D41C16417B00DC3B10 /* Dip.swift in Sources */,
09FC480F1DAA9CAF00566AA8 /* Register.swift in Sources */,
@@ -282,6 +317,7 @@
files = (
09BD35141D84E30D00B33E53 /* RuntimeArgumentsTests.swift in Sources */,
09BD35151D84E30D00B33E53 /* ThreadSafetyTests.swift in Sources */,
63937A6F21524DA300AEE75A /* DipUITests.swift in Sources */,
09BD35121D84E30D00B33E53 /* DefinitionTests.swift in Sources */,
09BD350F1D84E30D00B33E53 /* AutoWiringTests.swift in Sources */,
09BD35111D84E30D00B33E53 /* ContextTests.swift in Sources */,
@@ -325,6 +361,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.alisoftware.Dip;
PRODUCT_NAME = Dip;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
@@ -344,6 +381,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.alisoftware.Dip;
PRODUCT_NAME = Dip;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
@@ -359,7 +397,13 @@
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.alisoftware.DipTests;
PRODUCT_NAME = "$(TARGET_NAME)";
STORYBOARD_NAME_PREFIX = NS;
"STORYBOARD_NAME_PREFIX[sdk=appletvos*]" = TV;
"STORYBOARD_NAME_PREFIX[sdk=appletvsimulator*]" = TV;
"STORYBOARD_NAME_PREFIX[sdk=iphoneos*]" = UI;
"STORYBOARD_NAME_PREFIX[sdk=iphonesimulator*]" = UI;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
@@ -375,6 +419,8 @@
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = com.alisoftware.DipTests;
PRODUCT_NAME = "$(TARGET_NAME)";
STORYBOARD_NAME_PREFIX = "";
SWIFT_VERSION = 5.0;
};
name = Release;
};
@@ -382,6 +428,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@@ -390,12 +437,14 @@
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;
@@ -405,7 +454,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 6.0;
CURRENT_PROJECT_VERSION = 7.0.1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@@ -430,12 +479,12 @@
ONLY_ACTIVE_ARCH = YES;
SUPPORTED_PLATFORMS = "macosx watchsimulator iphonesimulator appletvsimulator watchos appletvos iphoneos";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,3,4";
TVOS_DEPLOYMENT_TARGET = 9.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
WATCHOS_DEPLOYMENT_TARGET = 2.0;
WATCHOS_DEPLOYMENT_TARGET = 3.0;
};
name = Debug;
};
@@ -443,6 +492,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@@ -451,12 +501,14 @@
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;
@@ -466,7 +518,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 6.0;
CURRENT_PROJECT_VERSION = 7.0.1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -484,13 +536,13 @@
MTL_ENABLE_DEBUG_INFO = NO;
SUPPORTED_PLATFORMS = "macosx watchsimulator iphonesimulator appletvsimulator watchos appletvos iphoneos";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,3,4";
TVOS_DEPLOYMENT_TARGET = 9.0;
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
WATCHOS_DEPLOYMENT_TARGET = 2.0;
WATCHOS_DEPLOYMENT_TARGET = 3.0;
};
name = Release;
};
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0900"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -40,9 +40,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
@@ -71,7 +69,6 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
+1 -1
View File
@@ -9,7 +9,7 @@
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<string>7.1.1</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
+1 -1
View File
@@ -9,7 +9,7 @@
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<string>7.1.1</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
+54
View File
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14313.18"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Dip View Controller-->
<scene sceneID="adI-oe-5KL">
<objects>
<viewController storyboardIdentifier="DipViewController" id="fzZ-tH-vfC" customClass="DipViewController" customModule="DipTests" sceneMemberID="viewController">
<view key="view" id="vso-jO-9Ex">
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="dipTag" value="vc"/>
</userDefinedRuntimeAttributes>
</viewController>
<customObject id="wjM-mL-nmG" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="311" y="339"/>
</scene>
<!--Nil Tag View Controller-->
<scene sceneID="OhX-tC-zpS">
<objects>
<viewController storyboardIdentifier="NilTagViewController" id="35S-Ec-qEA" customClass="NilTagViewController" customModule="DipTests" sceneMemberID="viewController">
<view key="view" id="y58-K4-cDZ">
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="nil" keyPath="dipTag"/>
</userDefinedRuntimeAttributes>
</viewController>
<customObject id="pvf-jv-8Cj" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="874" y="339"/>
</scene>
<!--View Controller-->
<scene sceneID="Rwu-gt-fAa">
<objects>
<viewController storyboardIdentifier="ViewController" id="tne-ER-mvb" sceneMemberID="viewController">
<view key="view" id="Kwe-OO-w0D">
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</viewController>
<customObject id="0u1-hv-ZtW" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="311" y="729"/>
</scene>
</scenes>
</document>
+75
View File
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="14313.18" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="ctX-Lj-Yrr">
<device id="appleTV" orientation="landscape">
<adaptation id="light"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Dip View Controller-->
<scene sceneID="0jz-eb-APg">
<objects>
<viewController storyboardIdentifier="DipViewController" id="ctX-Lj-Yrr" customClass="DipViewController" customModule="DipTests" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="TrC-Rh-efi"/>
<viewControllerLayoutGuide type="bottom" id="arQ-XW-qWa"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="syn-UA-YGd">
<rect key="frame" x="0.0" y="0.0" width="200" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="200" height="100"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="dipTag" value="vc"/>
</userDefinedRuntimeAttributes>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="mq6-CB-g6V" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="563" y="-228"/>
</scene>
<!--View Controller-->
<scene sceneID="gDU-un-krd">
<objects>
<viewController storyboardIdentifier="ViewController" id="UwR-h2-tgS" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="qKG-R0-Nkp"/>
<viewControllerLayoutGuide type="bottom" id="vP4-Si-HAL"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="r35-FI-kgS">
<rect key="frame" x="0.0" y="0.0" width="200" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="200" height="100"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="AIY-qB-Dbe" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="563" y="-16"/>
</scene>
<!--Nil Tag View Controller-->
<scene sceneID="PiH-4i-Txa">
<objects>
<viewController storyboardIdentifier="NilTagViewController" id="ZLb-1s-1ne" customClass="NilTagViewController" customModule="DipTests" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="arU-Ca-km8"/>
<viewControllerLayoutGuide type="bottom" id="jw1-4B-Xlt"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="NMo-Oi-l7H">
<rect key="frame" x="0.0" y="0.0" width="200" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="200" height="100"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="nil" keyPath="dipTag"/>
</userDefinedRuntimeAttributes>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Cid-Yu-0N6" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="821" y="-228"/>
</scene>
</scenes>
</document>
+78
View File
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="5AO-J3-7R4">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="Ole-tM-Q3l">
<objects>
<viewController storyboardIdentifier="ViewController" id="ehZ-7Y-MeO" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="bZ0-tI-Yi9"/>
<viewControllerLayoutGuide type="bottom" id="UX3-8G-Z4L"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Jmi-i3-vAY">
<rect key="frame" x="0.0" y="0.0" width="200" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="200" height="100"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="H12-WL-igv" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="646" y="461"/>
</scene>
<!--Dip View Controller-->
<scene sceneID="Lgf-SY-hfd">
<objects>
<viewController storyboardIdentifier="DipViewController" id="5AO-J3-7R4" userLabel="Dip View Controller" customClass="DipViewController" customModule="DipTests" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="JYl-DM-U2W"/>
<viewControllerLayoutGuide type="bottom" id="Xg8-gz-BQL"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="aU0-uz-rHf">
<rect key="frame" x="0.0" y="0.0" width="200" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="200" height="100"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="dipTag" value="vc"/>
</userDefinedRuntimeAttributes>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="4iF-mX-EX6" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="646" y="200"/>
</scene>
<!--Dip View Controller-->
<scene sceneID="AUA-qF-7ky">
<objects>
<viewController storyboardIdentifier="NilTagViewController" id="fFP-hb-OdS" userLabel="Dip View Controller" customClass="NilTagViewController" customModule="DipTests" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Sio-ii-jPl"/>
<viewControllerLayoutGuide type="bottom" id="bhs-zK-dln"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="4xq-UV-htt">
<rect key="frame" x="0.0" y="0.0" width="200" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="200" height="100"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="nil" keyPath="dipTag"/>
</userDefinedRuntimeAttributes>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="xvW-k9-I9y" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="898" y="200"/>
</scene>
</scenes>
</document>
@@ -13,7 +13,7 @@ On the previous page you saw how auto-wiring helps us to get rid of boilerplate
Let's say you have following related components:
*/
protocol Service: class {
protocol Service: AnyObject {
var logger: Logger? { get }
var tracker: Tracker? { get }
}
@@ -101,11 +101,11 @@ serverWithNoClient.optionalClient.value
Another example of using auto-injection is circular dependencies. Let's say you have a `Server` and a `ServerClient` both referencing each other.
*/
protocol Server: class {
protocol Server: AnyObject {
weak var client: ServerClient? { get }
}
protocol ServerClient: class {
protocol ServerClient: AnyObject {
var server: Server? { get }
}
@@ -13,11 +13,11 @@ Very often we encounter situations when we have circular dependencies between co
Let's say you have some network client and it's delegate defined like this:
*/
protocol NetworkClientDelegate: class {
protocol NetworkClientDelegate: AnyObject {
var networkClient: NetworkClient { get }
}
protocol NetworkClient: class {
protocol NetworkClient: AnyObject {
weak var delegate: NetworkClientDelegate? { get set }
}
@@ -1,6 +1,6 @@
import Foundation
public protocol Service: class {}
public protocol Service: AnyObject {}
public class ServiceImp1: Service {
public init() {}
@@ -22,7 +22,7 @@ public class ServiceImp4: Service {
}
public protocol Client: class {
public protocol Client: AnyObject {
var service: Service {get}
init(service: Service)
}
@@ -74,9 +74,9 @@ public class DataProviderImp: DataProvider {
public init() {}
}
public protocol ListInteractorOutput: class {}
public protocol ListModuleInterface: class {}
public protocol ListInteractorInput: class {}
public protocol ListInteractorOutput: AnyObject {}
public protocol ListModuleInterface: AnyObject {}
public protocol ListInteractorInput: AnyObject {}
public class ListPresenter: NSObject {
public var listInteractor : ListInteractorInput?
public var listWireframe : ListWireframe?
@@ -96,8 +96,8 @@ public class ListWireframe : NSObject {
}
}
public protocol AddModuleDelegate: class {}
public protocol AddModuleInterface: class {}
public protocol AddModuleDelegate: AnyObject {}
public protocol AddModuleInterface: AnyObject {}
public class AddWireframe: NSObject {
let addPresenter : AddPresenter
public init(addPresenter: AddPresenter) {
+3
View File
@@ -0,0 +1,3 @@
source "https://rubygems.org"
gem "cocoapods", '=1.4.0'
+76
View File
@@ -0,0 +1,76 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.0)
activesupport (4.2.11)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
atomos (0.1.3)
claide (1.0.2)
cocoapods (1.4.0)
activesupport (>= 4.0.2, < 5)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.4.0)
cocoapods-deintegrate (>= 1.0.2, < 2.0)
cocoapods-downloader (>= 1.1.3, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-stats (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.3.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (~> 2.0.1)
gh_inspector (~> 1.0)
molinillo (~> 0.6.4)
nap (~> 1.0)
ruby-macho (~> 1.1)
xcodeproj (>= 1.5.4, < 2.0)
cocoapods-core (1.4.0)
activesupport (>= 4.0.2, < 6)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
cocoapods-deintegrate (1.0.2)
cocoapods-downloader (1.2.2)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.0)
cocoapods-stats (1.0.0)
cocoapods-trunk (1.3.1)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.1.0)
colored2 (3.1.2)
concurrent-ruby (1.1.4)
escape (0.0.4)
fourflusher (2.0.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
minitest (5.11.3)
molinillo (0.6.6)
nanaimo (0.2.6)
nap (1.1.0)
netrc (0.11.0)
ruby-macho (1.3.1)
thread_safe (0.3.6)
tzinfo (1.2.5)
thread_safe (~> 0.1)
xcodeproj (1.7.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.2.6)
PLATFORMS
ruby
DEPENDENCIES
cocoapods (= 1.4.0)
BUNDLED WITH
1.16.5
+8
View File
@@ -0,0 +1,8 @@
import XCTest
import DipTests
var tests = [XCTestCaseEntry]()
tests += DipTests.__allTests()
XCTMain(tests)
+9 -24
View File
@@ -1,30 +1,15 @@
//
// 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.
//
// swift-tools-version:5.0
import PackageDescription
let package = Package(
name: "Dip"
name: "Dip",
products: [
.library(name: "Dip", targets: ["Dip"]),
],
targets: [
.target(name: "Dip", dependencies: [], path: "Sources"),
.testTarget(name: "DipTests", dependencies: ["Dip"], path: "Tests"),
]
)
+4 -18
View File
@@ -5,8 +5,8 @@
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![License](https://img.shields.io/cocoapods/l/Dip.svg?style=flat)](http://cocoapods.org/pods/Dip)
[![Platform](https://img.shields.io/cocoapods/p/Dip.svg?style=flat)](http://cocoapods.org/pods/Dip)
[![Swift Version](https://img.shields.io/badge/Swift-3.0--3.1-F16D39.svg?style=flat)](https://developer.apple.com/swift)
[![Swift Version](https://img.shields.io/badge/Linux-3.0--3.1-4BC51D.svg?style=flat)](https://developer.apple.com/swift)
[![Swift Version](https://img.shields.io/badge/Swift-4.0--4.2-F16D39.svg?style=flat)](https://developer.apple.com/swift)
[![Swift Version](https://img.shields.io/badge/Linux-4.0--4.2-4BC51D.svg?style=flat)](https://developer.apple.com/swift)
![Animated Dipping GIF](cinnamon-pretzels-caramel-dipping.gif)
_Photo courtesy of [www.kevinandamanda.com](http://www.kevinandamanda.com/recipes/appetizer/homemade-soft-cinnamon-sugar-pretzel-bites-with-salted-caramel-dipping-sauce.html)_
@@ -19,7 +19,7 @@ It's aimed to be as simple as possible yet provide rich functionality usual for
* 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** . There is also a **[code generator](https://github.com/ilyapuchka/dipgen)** that can help to simplify registering new components.
<details>
<summary>Basic usage</summary>
@@ -148,27 +148,13 @@ File an issue if you have any question. Pull requests are warmly welcome too.
## Installation
Since version 5.0.0 Dip is built with Swift 3.0. You can install Dip using your favorite dependency manager:
You can install Dip using your favorite dependency manager:
<details>
<summary>CocoaPods</summary>
`pod "Dip"`
To build for Swift 2.3 add this code to the bottom of your Podfile
```ruby
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '2.3'
end
end
end
```
> You need at least 1.1.0.rc.2 version of CocoaPods.
</details>
<details>
@@ -279,7 +279,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0700;
LastUpgradeCheck = 0900;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = AliSoftware;
TargetAttributes = {
0990225E1BC123C000E76F43 = {
@@ -292,7 +292,7 @@
};
buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "DipSampleApp" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
@@ -423,6 +423,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@@ -431,12 +432,14 @@
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;
@@ -477,6 +480,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@@ -485,12 +489,14 @@
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;
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0900"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -40,9 +40,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
codeCoverageEnabled = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
@@ -71,7 +70,6 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "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 -1
View File
@@ -9,7 +9,7 @@
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<string>7.1.1</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
@@ -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))
}
@@ -8,7 +8,7 @@
import Foundation
///Provides Person entitis fetching them with web service
///Provides Person entities fetching them with web service
struct SWAPIPersonProvider : PersonProviderAPI {
let ws: NetworkLayer
@@ -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)
}
@@ -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)
}
@@ -8,7 +8,7 @@
import UIKit
protocol FetchableTrait: class {
protocol FetchableTrait: AnyObject {
associatedtype ObjectType
var objects: [ObjectType]? { get set }
var batchRequestID: Int { get set }
+1 -1
View File
@@ -9,7 +9,7 @@
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<string>7.1.1</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
+206 -82
View File
@@ -78,7 +78,7 @@ extension DependencyContainer {
```
*/
public protocol AutoInjectedPropertyBox: class {
public protocol AutoInjectedPropertyBox {
///The type of wrapped property.
static var wrappedType: Any.Type { get }
@@ -93,40 +93,108 @@ public protocol AutoInjectedPropertyBox: class {
func resolve(_ container: DependencyContainer) throws
}
#if swift(>=5.1)
/**
Use this wrapper to identify _strong_ properties of the instance that should be
auto-injected by `DependencyContainer`. Type T can be any type.
- warning: Do not define this property as optional or container will not be able to inject it.
Instead define it with initial value of `Injected<T>()`.
**Example**:
```swift
class ClientImp: Client {
@Injected var service: Service?
}
```
- seealso: `InjectedWeak`
*/
@propertyWrapper
public struct Injected<T>: _InjectedPropertyBox, AutoInjectedPropertyBox {
let valueBox: NullableBox<T> = NullableBox(nil)
///Wrapped value.
public var wrappedValue: T? {
get {
return valueBox.unboxed
}
set {
guard (required && newValue != nil) || !required else {
fatalError("Can not set required property to nil.")
}
valueBox.unboxed = newValue
}
}
let required: Bool
let didInject: (T) -> ()
let tag: DependencyContainer.Tag?
let overrideTag: Bool
public init(wrappedValue initialValue: T?) {
self.init()
}
}
#else
/**
Use this wrapper to identify _strong_ properties of the instance that should be
auto-injected by `DependencyContainer`. Type T can be any type.
- warning: Do not define this property as optional or container will not be able to inject it.
Instead define it with initial value of `Injected<T>()`.
**Example**:
```swift
class ClientImp: Client {
var service = Injected<Service>()
}
```
- seealso: `InjectedWeak`
*/
public final class Injected<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox {
*/
public struct Injected<T>: _InjectedPropertyBox, AutoInjectedPropertyBox {
let valueBox: NullableBox<T> = NullableBox(nil)
///Wrapped value.
public var value: T? {
return valueBox.unboxed
}
let required: Bool
let didInject: (T) -> ()
let tag: DependencyContainer.Tag?
let overrideTag: Bool
/// Returns a new wrapper with provided value.
func setValue(_ value: T?) -> Injected {
guard (required && value != nil) || !required else {
fatalError("Can not set required property to nil.")
}
return Injected(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
}
}
#endif
public extension Injected {
///The type of wrapped property.
public static var wrappedType: Any.Type {
static var wrappedType: Any.Type {
return T.self
}
///Wrapped value.
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)
self.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
self.valueBox.unboxed = value
}
init(required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> () = { _ in }) {
self.required = required
self.tag = tag?.dependencyTag
self.overrideTag = overrideTag
self.didInject = didInject
}
/**
@@ -140,30 +208,24 @@ public final class Injected<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox
- 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 }) {
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 }) {
init(required: Bool = true, tag: DependencyTagConvertible?, didInject: @escaping (T) -> () = { _ in }) {
self.init(value: nil, required: required, tag: tag, overrideTag: true, didInject: didInject)
}
public func resolve(_ container: DependencyContainer) throws {
let resolved: T? = try super.resolve(with: container)
value = resolved
}
/// Returns a new wrapper with provided value.
public func setValue(_ value: T?) -> Injected {
guard (required && value != nil) || !required else {
fatalError("Can not set required property to nil.")
func resolve(_ container: DependencyContainer) throws {
let resolved: T? = try self.resolve(with: container, tag: tag, overrideTag: overrideTag, required: required)
valueBox.unboxed = resolved
if let resolved = resolved {
didInject(resolved)
}
return Injected(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
}
}
#if swift(>=5.1)
/**
Use this wrapper to identify _weak_ properties of the instance that should be
auto-injected by `DependencyContainer`. Type T should be a **class** type.
@@ -175,7 +237,7 @@ public final class Injected<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox
For that reason if you resolve instance that has a _weak_ auto-injected property this property
will be released when `resolve` will complete.
Use `InjectedWeak<T>` to define one of two circular dependecies if another dependency is defined as `Injected<U>`.
Use `InjectedWeak<T>` to define one of two circular dependencies if another dependency is defined as `Injected<U>`.
This will prevent a retain cycle between resolved instances.
- warning: Do not define this property as optional or container will not be able to inject it.
@@ -185,42 +247,124 @@ public final class Injected<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox
```swift
class ServiceImp: Service {
var client = InjectedWeak<Client>()
@InjectedWeak var client: Client?
}
```
- seealso: `Injected`
*/
public final class InjectedWeak<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox {
@propertyWrapper
public struct InjectedWeak<T>: _InjectedPropertyBox, AutoInjectedPropertyBox {
//Only classes (means AnyObject) can be used as `weak` properties
//but we can not make <T: AnyObject> because that will prevent using protocol as generic type
//so we just rely on user reading documentation and passing AnyObject in runtime
//also we will throw fatal error if type can not be casted to AnyObject during resolution.
///The type of wrapped property.
public static var wrappedType: Any.Type {
return T.self
}
var valueBox: WeakBox<T>? = nil {
didSet {
if let value = value { didInject(value) }
let valueBox: WeakBox<T> = WeakBox(nil)
///Wrapped value.
public var wrappedValue: T? {
get {
return valueBox.value
}
set {
guard (required && newValue != nil) || !required else {
fatalError("Can not set required property to nil.")
}
valueBox.unboxed = newValue as AnyObject
}
}
let required: Bool
let didInject: (T) -> ()
let tag: DependencyContainer.Tag?
let overrideTag: Bool
public init(wrappedValue initialValue: T?) {
self.init()
}
}
#else
/**
Use this wrapper to identify _weak_ properties of the instance that should be
auto-injected by `DependencyContainer`. Type T should be a **class** type.
Otherwise it will cause runtime exception when container will try to resolve the property.
Use this wrapper to define one of two circular dependencies to avoid retain cycle.
- note: The only difference between `InjectedWeak` and `Injected` is that `InjectedWeak` uses
_weak_ reference to store underlying value, when `Injected` uses _strong_ reference.
For that reason if you resolve instance that has a _weak_ auto-injected property this property
will be released when `resolve` will complete.
Use `InjectedWeak<T>` to define one of two circular dependencies if another dependency is defined as `Injected<U>`.
This will prevent a retain cycle between resolved instances.
- warning: Do not define this property as optional or container will not be able to inject it.
Instead define it with initial value of `InjectedWeak<T>()`.
**Example**:
```swift
class ServiceImp: Service {
var client = InjectedWeak<Client>()
}
```
- seealso: `Injected`
*/
public struct InjectedWeak<T>: _InjectedPropertyBox, AutoInjectedPropertyBox {
//Only classes (means AnyObject) can be used as `weak` properties
//but we can not make <T: AnyObject> because that will prevent using protocol as generic type
//so we just rely on user reading documentation and passing AnyObject in runtime
//also we will throw fatal error if type can not be casted to AnyObject during resolution.
let valueBox: WeakBox<T> = WeakBox(nil)
///Wrapped value.
public var value: T? {
return valueBox?.value
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)
let required: Bool
let didInject: (T) -> ()
let tag: DependencyContainer.Tag?
let overrideTag: Bool
/// Returns a new wrapper with provided value.
func setValue(_ value: T?) -> InjectedWeak {
guard (required && value != nil) || !required else {
fatalError("Can not set required property to nil.")
}
return InjectedWeak(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
}
}
#endif
public extension InjectedWeak {
///The type of wrapped property.
static var wrappedType: Any.Type {
return T.self
}
init(value: T?, required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> ()) {
self.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
self.valueBox.unboxed = value as AnyObject
}
init(required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> () = { _ in }) {
self.required = required
self.tag = tag?.dependencyTag
self.overrideTag = overrideTag
self.didInject = didInject
}
/**
Creates a new wrapper for weak auto-injected property.
@@ -232,46 +376,28 @@ public final class InjectedWeak<T>: _InjectedPropertyBox<T>, AutoInjectedPropert
- 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 }) {
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 }) {
init(required: Bool = true, tag: DependencyTagConvertible?, didInject: @escaping (T) -> () = { _ in }) {
self.init(value: nil, required: required, tag: tag, overrideTag: true, didInject: didInject)
}
public func resolve(_ container: DependencyContainer) throws {
let resolved: T? = try super.resolve(with: container)
valueBox = resolved.map(WeakBox.init)
}
/// Returns a new wrapper with provided value.
public func setValue(_ value: T?) -> InjectedWeak {
guard (required && value != nil) || !required else {
fatalError("Can not set required property to nil.")
func resolve(_ container: DependencyContainer) throws {
let resolved: T? = try self.resolve(with: container, tag: tag, overrideTag: overrideTag, required: required)
valueBox.unboxed = resolved as AnyObject
if let resolved = resolved {
didInject(resolved)
}
return InjectedWeak(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
}
}
class _InjectedPropertyBox<T> {
protocol _InjectedPropertyBox {}
let required: Bool
let didInject: (T) -> ()
let tag: DependencyContainer.Tag?
let overrideTag: Bool
init(required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> () = { _ in }) {
self.required = required
self.tag = tag?.dependencyTag
self.overrideTag = overrideTag
self.didInject = didInject
}
func resolve(with container: DependencyContainer) throws -> T? {
let tag = overrideTag ? self.tag : container.context.tag
extension _InjectedPropertyBox {
func resolve<T>(with container: DependencyContainer, tag: DependencyContainer.Tag?, overrideTag: Bool, required: Bool) throws -> T? {
let tag = overrideTag ? tag : container.context.tag
do {
container.context.key = container.context.key.tagged(with: tag)
let key = DefinitionKey(type: T.self, typeOfArguments: Void.self, tag: tag?.dependencyTag)
@@ -289,12 +415,10 @@ class _InjectedPropertyBox<T> {
}
}
private func resolve<U>(with container: DependencyContainer, key: DefinitionKey, builder: ((U) throws -> Any) throws -> Any) throws -> Any {
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)
})
}
}
+4 -16
View File
@@ -1,17 +1,5 @@
#if _runtime(_ObjC)
extension String {
func has(prefix aPrefix: String) -> Bool {
return hasPrefix(aPrefix)
}
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
}
+12 -12
View File
@@ -35,10 +35,10 @@ public enum ComponentScope {
```
container.register { ServiceImp() as Service }
container.register {
ServiceConsumerImp(
service1: try container.resolve() as Service
service2: try container.resolve() as Service
) as ServiceConsumer
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
@@ -59,10 +59,10 @@ public enum ComponentScope {
```
container.register { ServiceImp() as Service }
container.register {
ServiceConsumerImp(
service1: try container.resolve() as Service
service2: try container.resolve() as Service
) as ServiceConsumer
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
@@ -89,10 +89,10 @@ public enum ComponentScope {
```
container.register(.singleton) { ServiceImp() as Service }
container.register {
ServiceConsumerImp(
service1: try container.resolve() as Service
service2: try container.resolve() as Service
) as ServiceConsumer
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
+43 -11
View File
@@ -34,12 +34,14 @@ public struct DefinitionKey: Hashable, CustomStringConvertible {
self.tag = tag
}
public var hashValue: Int {
return "\(type)-\(typeOfArguments)-\(tag.desc)".hashValue
public func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(type))
hasher.combine(ObjectIdentifier(typeOfArguments))
hasher.combine(tag.desc)
}
public var description: String {
return "type: \(type), arguments: \(typeOfArguments), tag: \(tag.desc)"
return "type: \(String(reflecting: type)), arguments: \(typeOfArguments), tag: \(tag.desc)"
}
func tagged(with tag: DependencyContainer.Tag?) -> DefinitionKey {
@@ -53,13 +55,13 @@ public struct DefinitionKey: Hashable, CustomStringConvertible {
return
lhs.type == rhs.type &&
lhs.typeOfArguments == rhs.typeOfArguments &&
lhs.tag == rhs.tag
lhs.tag.desc == rhs.tag.desc
}
}
///Dummy protocol to store definitions for different types in collection
public protocol DefinitionType: class { }
public protocol DefinitionType: AnyObject { }
/**
`Definition<T, U>` describes how instances of type `T` should be created when this type is resolved by the `DependencyContainer`.
@@ -80,6 +82,7 @@ public final class Definition<T, U>: DefinitionType {
let scope: ComponentScope
var weakFactory: ((Any) throws -> Any)!
var resolveProperties: ((DependencyContainer, Any) throws -> ())?
var autoInjectProperties: Bool?
init(scope: ComponentScope, factory: @escaping F) {
self.factory = factory
@@ -125,6 +128,34 @@ public final class Definition<T, U>: DefinitionType {
return self
}
@discardableResult public func resolvingProperty<Root, V>(_ keyPath: ReferenceWritableKeyPath<Root, V>, as type: Any.Type = V.self, tag: DependencyTagConvertible? = nil) -> Definition {
return resolvingProperties { (container, instance) in
precondition(instance is Root, "Type of resolved instance \(Swift.type(of: instance)) does not match expected type \(Root.self)")
let resolved = try container.resolve(type, tag: tag)
precondition(resolved is V, "Type of resolved property \(Swift.type(of: resolved)) does not match expected type \(type)")
(instance as! Root)[keyPath: keyPath] = resolved as! V
}
}
@discardableResult public func resolvingProperty<Root, V>(_ keyPath: ReferenceWritableKeyPath<Root, V>, factory: @escaping (DependencyContainer) throws -> V = { try $0.resolve() }) -> Definition {
return resolvingProperties { (container, instance) in
precondition(instance is Root, "Type of resolved instance \(Swift.type(of: instance)) does not match expected type \(Root.self)")
(instance as! Root)[keyPath: keyPath] = try factory(container)
}
}
/**
Whether container should perform properties auto-injection when resolving using this definition.
If called will override container configuration. Can be called together with `resolvingProperties`
to resolve properties that are not automatically injected.
- parameter shouldAutoInject: Whether container should perform properties auto-injection when resolving using this definition. Default is `true`.
*/
@discardableResult public func autoInjectingProperties(_ shouldAutoInject: Bool = true) -> Definition {
autoInjectProperties = shouldAutoInject
return self
}
/// Calls `resolveDependencies` block if it was set.
func resolveProperties(of instance: Any, container: DependencyContainer) throws {
guard let resolvedInstance = instance as? T else { return }
@@ -144,7 +175,7 @@ public final class Definition<T, U>: DefinitionType {
//MARK: - TypeForwardingDefinition
/// Types that can be resolved using this definition.
private(set) var implementingTypes: [Any.Type] = [(T?).self, (T!).self]
private(set) var implementingTypes: [Any.Type] = [(T?).self]
/// Return `true` if type can be resolved using this definition
func doesImplements(type aType: Any.Type) -> Bool {
@@ -175,7 +206,7 @@ public final class Definition<T, U>: DefinitionType {
//definitions for types that can be resolved by `forwardsTo` definition
//can also be used to resolve self type and it's implementing types
//this way container properly reuses previosly resolved instances
//this way container properly reuses previously resolved instances
//when there are several forwarded definitions
//see testThatItReusesInstanceResolvedByTypeForwarding)
for definition in forwardsTo.forwardsFrom {
@@ -204,12 +235,13 @@ protocol _Definition: AutoWiringDefinition, TypeForwardingDefinition {
var weakFactory: ((Any) throws -> Any)! { get }
func resolveProperties(of instance: Any, container: DependencyContainer) throws
var container: DependencyContainer? { get set }
var autoInjectProperties: Bool? { get }
}
//MARK: - Type Forwarding
protocol _TypeForwardingDefinition: _Definition {
weak var forwardsTo: _TypeForwardingDefinition? { get }
var forwardsTo: _TypeForwardingDefinition? { get }
var forwardsFrom: [_TypeForwardingDefinition] { get set }
func _implements(type aType: Any.Type)
func _implements(types aTypes: [Any.Type])
@@ -260,7 +292,7 @@ class DefinitionBuilder<T, U> {
typealias KeyDefinitionPair = (key: DefinitionKey, definition: _Definition)
/// Definitions are matched if they are registered for the same tag and thier factories accept the same number of runtime arguments.
/// Definitions are matched if they are registered for the same tag and their factories accept the same number of runtime arguments.
private func ~=(lhs: KeyDefinitionPair, rhs: KeyDefinitionPair) -> Bool {
guard lhs.key.type == rhs.key.type else { return false }
guard lhs.key.tag == rhs.key.tag else { return false }
@@ -268,9 +300,9 @@ private func ~=(lhs: KeyDefinitionPair, rhs: KeyDefinitionPair) -> Bool {
return true
}
/// Returns key-defintion pairs with definitions able to resolve that type (directly or via type forwarding)
/// Returns key-definition pairs with definitions able to resolve that type (directly or via type forwarding)
/// 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.
/// In the end filters definitions by type of runtime arguments.
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) })
+26 -15
View File
@@ -38,7 +38,9 @@ public final class DependencyContainer {
case String(StringLiteralType)
case Int(IntegerLiteralType)
}
var autoInjectProperties: Bool
var threadSafe: Bool
internal(set) public var context: Context!
var definitions = [DefinitionKey: _Definition]()
var resolvedInstances = ResolvedInstances()
@@ -50,7 +52,7 @@ public final class DependencyContainer {
private var _weakCollaborators: [WeakBox<DependencyContainer>] = []
var _collaborators: [DependencyContainer] {
get {
return _weakCollaborators.flatMap({ $0.value })
return _weakCollaborators.compactMap({ $0.value })
}
set {
_weakCollaborators = newValue.filter({ $0 !== self }).map(WeakBox.init)
@@ -59,8 +61,11 @@ public final class DependencyContainer {
/**
Designated initializer for a DependencyContainer
- parameter configBlock: A configuration block in which you typically put all you `register` calls.
- Parameters:
- autoInjectProperties: Whether container should perform properties auto-injection. Default is `true`.
- threadSafe: Whether container should be thread-safe. Default is `true`. You may want to disable it for better performance.
- configBlock: A configuration block in which you typically put all you `register` calls.
- note: The `configBlock` is simply called at the end of the `init` to let you configure everything.
It is only present for convenience to have a cleaner syntax when declaring and initializing
@@ -79,7 +84,9 @@ public final class DependencyContainer {
- returns: A new DependencyContainer.
*/
public init(configBlock: (DependencyContainer)->() = { _ in }) {
public init(autoInjectProperties: Bool = true, threadSafe: Bool = true, configBlock: (DependencyContainer)->() = { _ in }) {
self.autoInjectProperties = autoInjectProperties
self.threadSafe = threadSafe
configBlock(self)
}
@@ -87,7 +94,7 @@ public final class DependencyContainer {
Call this method to complete container setup. After container is bootstrapped
you can not add or remove definitions. Trying to do so will cause runtime exception.
You can completely reset container, after reset you can bootstrap it again.
During bootsrap container will instantiate components registered with `EagerSingleton` scope.
During bootstrap container will instantiate components registered with `EagerSingleton` scope.
- throws: `DipError` if failed to instantiate any component
*/
@@ -100,6 +107,10 @@ public final class DependencyContainer {
}
func threadSafe<T>(_ closure: () throws -> T) rethrows -> T {
guard threadSafe else {
return try closure()
}
lock.lock()
defer { lock.unlock() }
return try closure()
@@ -287,7 +298,7 @@ extension DependencyContainer {
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.
//so there is probably a circular reference between containers.
//To break it skip this container
if let context = collaborator.context, context.resolvingType == key.type && context.tag == key.tag { continue }
@@ -303,15 +314,15 @@ extension DependencyContainer {
collaborator.resolvedInstances = resolvedInstances
for (key, resolvedSingleton) in self.resolvedInstances.singletons {
collaborator.resolvedInstances.singletons[aKey] = resolvedSingleton
collaborator.resolvedInstances.singletons[key] = resolvedSingleton
}
for (_, resolvedSingleton) in self.resolvedInstances.weakSingletons {
guard collaborator.definition(matching: aKey) != nil else { continue }
collaborator.resolvedInstances.weakSingletons[aKey] = WeakBox(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 (_, resolved) in self.resolvedInstances.resolvedInstances {
guard collaborator.definition(matching: aKey) != nil else { continue }
collaborator.resolvedInstances.resolvedInstances[aKey] = resolved
for (key, resolved) in self.resolvedInstances.resolvedInstances {
guard collaborator.definition(matching: key) == nil else { continue }
collaborator.resolvedInstances.resolvedInstances[key] = resolved
}
}
@@ -442,7 +453,7 @@ extension DependencyContainer: CustomStringConvertible {
//MARK: - DependencyTagConvertible
/// Implement this protocol on your type if you want to use its instances as `DependencyContainer`'s tags.
/// `DependencyContainer.Tag`, `String`, `Int` and any `RawRepresentable` with `RawType` of `String` or `Int` by default confrom to this protocol.
/// `DependencyContainer.Tag`, `String`, `Int` and any `RawRepresentable` with `RawType` of `String` or `Int` by default conform to this protocol.
public protocol DependencyTagConvertible {
var dependencyTag: DependencyContainer.Tag { get }
}
+6 -8
View File
@@ -66,7 +66,7 @@ public enum DipError: Error, CustomStringConvertible {
case ambiguousDefinitions(type: Any.Type, definitions: [DefinitionType])
/**
Thrown by `resolve(tag:)` if resolved instance does not implemenet resolved type (i.e. when type-forwarding).
Thrown by `resolve(tag:)` if resolved instance does not implement resolved type (i.e. when type-forwarding).
- parameters:
- resolved: Resolved instance
@@ -77,18 +77,16 @@ public enum DipError: Error, CustomStringConvertible {
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)."
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 \(String(reflecting: key.type))."
case let .autoInjectionFailed(label, type, error):
return "Failed to auto-inject property \"\(label.desc)\" of type \(type). \(error)"
return "Failed to auto-inject property \"\(label.desc)\" of type \(String(reflecting: type)). \(error)"
case let .autoWiringFailed(type, error):
return "Failed to auto-wire type \"\(type)\". \(error)"
return "Failed to auto-wire type \"\(String(reflecting: type))\". \(error)"
case let .ambiguousDefinitions(type, definitions):
return "Ambiguous definitions for \(type):\n" +
return "Ambiguous definitions for \(String(reflecting: 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)."
return "Resolved instance \(resolved ?? "nil") does not implement expected type \(String(reflecting: key.type))."
}
}
}
+17 -12
View File
@@ -81,7 +81,7 @@ extension DependencyContainer {
- parameters:
- tag: The arbitrary tag to use to lookup definition.
- builder: Generic closure that accepts generic factory and returns inctance created by that factory.
- builder: Generic closure that accepts generic factory and returns instance created by that factory.
- throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`, `DipError.InvalidType`
@@ -183,7 +183,7 @@ extension DependencyContainer {
return previouslyResolved
}
log(level: .Verbose, context)
if let context = context { log(level: .Verbose, context) }
var resolvedInstance = try builder(definition)
/*
@@ -199,7 +199,7 @@ extension DependencyContainer {
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 {
if let box = resolvedInstance as? BoxType, let unboxedAny = box.unboxed, let unboxed = unboxedAny as? T {
resolvedInstance = unboxed
}
@@ -217,8 +217,11 @@ extension DependencyContainer {
resolvedInstances.resolvableInstances.append(resolvable)
resolvable.resolveDependencies(self)
}
try autoInjectProperties(in: resolvedInstance)
let shouldAutoInject = definition.autoInjectProperties ?? self.autoInjectProperties
if shouldAutoInject {
try autoInjectProperties(in: resolvedInstance)
}
try definition.resolveProperties(of: resolvedInstance, container: self)
log(level: .Verbose, "Resolved type \(key.type) with \(resolvedInstance)")
@@ -227,7 +230,7 @@ extension DependencyContainer {
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 {
if let previouslyResolved: T = resolvedInstances[key: key, inScope: definition.scope, context: context] {
return previouslyResolved
}
//then check if any related type was already resolved
@@ -235,7 +238,7 @@ extension DependencyContainer {
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 {
if let previouslyResolved: T = resolvedInstances[key: key, inScope: definition.scope, context: context] {
return previouslyResolved
}
}
@@ -280,20 +283,22 @@ class ResolvedInstances {
}
var weakSingletons = [DefinitionKey: Any]()
subscript(key key: DefinitionKey, inScope scope: ComponentScope, context context: DependencyContainer.Context) -> Any? {
subscript<T>(key key: DefinitionKey, inScope scope: ComponentScope, context context: DependencyContainer.Context) -> T? {
get {
let instance: Any?
switch scope {
case .singleton, .eagerSingleton:
return context.inCollaboration ? sharedSingletons[key] : singletons[key]
instance = 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] }
if let boxed = singletons[key] as? WeakBoxType { instance = boxed.unboxed }
else { instance = singletons[key] }
case .shared:
return resolvedInstances[key]
instance = resolvedInstances[key]
case .unique:
return nil
}
return instance.flatMap { $0 as? T }
}
set {
switch scope {
+2 -2
View File
@@ -98,7 +98,7 @@ extension DependencyContainer {
}
```
Though before you do so you should probably review your design and try to reduce number of depnedencies.
Though before you do so you should probably review your design and try to reduce number of dependencies.
*/
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> {
@@ -141,7 +141,7 @@ extension DependencyContainer {
which factories accept any number of runtime arguments and are tagged with the same tag,
passed to `resolve` method, or with no tag. Container will try to use these definitions
to resolve a component one by one until one of them succeeds, starting with tagged definitions
in order of decreasing their's factories number of arguments. If none of them succeds it will
in order of decreasing their's factories number of arguments. If none of them succeeds it will
throw an error. If it finds two definitions with the same number of arguments - it will throw
an error.
+245
View File
@@ -0,0 +1,245 @@
//
// DipUI
//
// Copyright (c) 2016 Ilya Puchka <ilyapuchka@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#if (canImport(UIKit) || canImport(AppKit) || canImport(WatchKit))
extension DependencyContainer {
///Containers that will be used to resolve dependencies of instances, created by stroyboards.
static public var uiContainers: [DependencyContainer] = {
#if os(watchOS)
swizzleAwakeWithContext
#endif
return []
}()
/**
Resolves dependencies of passed in instance.
Use this method to resolve dependencies of object created by storyboard.
The type of the instance should be registered in the container.
You should call this method only from implementation of `didInstantiateFromStoryboard(_:tag:)`
of `StoryboardInstantiatable` protocol if you override its default implementation.
This method will do the same as `resolve(tag:) as T`, but instead of creating
a new intance with a registered factory it will use passed in instance as a resolved instance.
- parameters:
- instance: The object which dependencies should be resolved
- tag: An optional tag used to register the type (`T`) in the container
**Example**:
```swift
class ViewController: UIViewController, ServiceDelegate, StoryboardInstantiatable {
var service: Service?
func didInstantiateFromStoryboard(_ container: DependencyContainer, tag: DependencyContainer.Tag?) throws {
try container.resolveDependencies(of: self as ServiceDelegate, tag: "vc")
}
}
class ServiceImp: Service {
weak var delegate: ServiceDelegate?
}
container.register(tag: "vc") { ViewController() }
.resolvingProperties { container, controller in
controller.service = try container.resolve() as Service
controller.service.delegate = controller
}
container.register { ServiceImp() as Service }
```
- seealso: `register(_:type:tag:factory:)`, `didInstantiateFromStoryboard(_:tag:)`
*/
public func resolveDependencies<T>(of instance: T, tag: Tag? = nil) throws {
_ = try resolve(tag: tag) { (_: () throws -> T) in instance }
}
/**
Register storyboard type `T` which has to conform to `StoryboardInstantiatable` and associate it with an optional tag.
- parameters:
- type: Storyboard type to register definition for.
- tag: The arbitrary tag to associate this factory with. Pass `nil` to associate with any tag. Default value is `nil`.
- returns: A registered definition.
- note: This method will register concrete types. If you need to register
as abstract types you should use standard `register` method from Dip.
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 MyViewController
container.register(storyboardType: MyViewController.self)
// or
container.register(tag: "myVC") { MyViewController() as MyViewControllerType }
```
*/
public func register<T: NSObject>(storyboardType type: T.Type, tag: DependencyTagConvertible? = nil) -> Dip.Definition<T, ()> where T: StoryboardInstantiatable {
return register(.unique, type: type, tag: tag, factory: { T() })
}
}
#if os(watchOS)
public protocol StoryboardInstantiatableType {}
#else
public typealias StoryboardInstantiatableType = NSObjectProtocol
#endif
public protocol StoryboardInstantiatable: StoryboardInstantiatableType {
/**
This method will be called if you set a `dipTag` attirbute on the object in a storyboard
that conforms to `StoryboardInstantiatable` protocol.
- parameters:
- tag: The tag value, that was set on the object in a storyboard
- container: The `DependencyContainer` associated with storyboards
The type that implements `StoryboardInstantiatable` protocol should be registered in `UIStoryboard.container`.
Default implementation of that method calls `resolveDependenciesOf(_:tag:)`
and pass it `self` instance and the tag.
Usually you will not need to override the default implementation of this method
if you registered the type of the instance as a concrete type in the container.
Then you only need to add conformance to `StoryboardInstantiatable`.
You may want to override it if you want to add custom logic before/after resolving dependencies
or you want to resolve the instance as implementation of some protocol which it conforms to.
- warning: This method will be called after `init?(coder:)` but before `awakeFromNib` method of `NSObject`.
On watchOS this method will be called before `awakeWithContext(_:)`.
**Example**:
```swift
extension MyViewController: SomeProtocol { ... }
extension MyViewController: StoryboardInstantiatable {
func didInstantiateFromStoryboard(_ container: DependencyContainer, tag: DependencyContainer.Tag) throws {
//resolve dependencies of the instance as SomeProtocol type
try container.resolveDependencies(of: self as SomeProtocol, tag: tag)
//do some additional setup here
}
}
```
*/
func didInstantiateFromStoryboard(_ container: DependencyContainer, tag: DependencyContainer.Tag?) throws
}
extension StoryboardInstantiatable {
public func didInstantiateFromStoryboard(_ container: DependencyContainer, tag: DependencyContainer.Tag?) throws {
try container.resolveDependencies(of: self, tag: tag)
}
}
#if os(iOS) || os(tvOS) || os(OSX)
#if os(iOS) || os(tvOS)
import UIKit
#elseif os(OSX)
import AppKit
#endif
let DipTagAssociatedObjectKey = UnsafeMutablePointer<Int8>.allocate(capacity: 1)
extension NSObject {
///A string tag that will be used to resolve dependencies of this instance
///if it implements `StoryboardInstantiatable` protocol.
@objc private(set) public var dipTag: String? {
get {
return objc_getAssociatedObject(self, DipTagAssociatedObjectKey) as? String
}
set {
objc_setAssociatedObject(self, DipTagAssociatedObjectKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
guard let instantiatable = self as? StoryboardInstantiatable else { return }
let tag = dipTag.map(DependencyContainer.Tag.String)
for (index, container) in DependencyContainer.uiContainers.enumerated() {
do {
log("Trying to resolve \(type(of: self)) with UI container at index \(index)")
try instantiatable.didInstantiateFromStoryboard(container, tag: tag)
log("Resolved \(type(of: self))")
return
} catch { }
}
}
}
}
func log(_ message: Any) {
if Dip.LogLevel.Errors.rawValue <= Dip.logLevel.rawValue {
Dip.logger(logLevel, message)
}
}
#else
import WatchKit
let swizzleAwakeWithContext: Void = {
let originalSelector = #selector(WKInterfaceController.awake(withContext:))
let swizzledSelector = #selector(WKInterfaceController.dip_awake(withContext:))
guard let originalMethod = class_getInstanceMethod(WKInterfaceController.self, originalSelector),
let swizzledMethod = class_getInstanceMethod(WKInterfaceController.self, swizzledSelector) else { return }
let didAddMethod = class_addMethod(WKInterfaceController.self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(WKInterfaceController.self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}()
extension WKInterfaceController: StoryboardInstantiatableType {
@objc func dip_awake(withContext context: AnyObject?) {
defer { self.dip_awake(withContext: context) }
guard let instantiatable = self as? StoryboardInstantiatable else { return }
for container in DependencyContainer.uiContainers {
guard let _ = try? instantiatable.didInstantiateFromStoryboard(container, tag: nil) else { continue }
break
}
}
}
#endif
#endif
+9 -14
View File
@@ -48,11 +48,6 @@ extension Optional: BoxType {
}
}
extension ImplicitlyUnwrappedOptional: BoxType {
var unboxed: Any? {
return self ?? nil
}
}
class Box<T> {
var unboxed: T
@@ -61,6 +56,13 @@ class Box<T> {
}
}
class NullableBox<T> {
var unboxed: T?
init(_ value: T?) {
self.unboxed = value
}
}
protocol WeakBoxType {
var unboxed: AnyObject? { get }
}
@@ -71,15 +73,8 @@ class WeakBox<T>: WeakBoxType {
return unboxed as? T
}
init(_ value: T) {
#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))")
}
init(_ value: T?) {
weak var value: AnyObject? = value as AnyObject
self.unboxed = value
}
}
+162 -34
View File
@@ -25,16 +25,44 @@
import XCTest
@testable import Dip
private protocol Server: class {
weak var client: Client! {get}
private protocol Server: AnyObject {
var client: Client! {get}
var anotherClient: Client! {get set}
}
private protocol Client: class {
private protocol Client: AnyObject {
var server: Server? {get}
var anotherServer: Server! {get set}
}
#if swift(>=5.1)
private class ServerImp: Server {
@InjectedWeak(didInject: { _ in
AutoInjectionTests.clientDidInjectCalled = true
}) var client: Client!
@InjectedWeak(required: false) var _optionalProperty: AnyObject?
weak var anotherClient: Client!
}
private class ClientImp: Client {
@Injected(didInject: { _ in
AutoInjectionTests.serverDidInjectCalled = true
}) var server: Server
@Injected(required: false) var _optionalProperty: AnyObject?
@Injected(tag: "tagged") var taggedServer: Server
@Injected(tag: nil) var nilTaggedServer: Server
var anotherServer: Server!
}
#else
private class ServerImp: Server {
var _client = InjectedWeak<Client>() { _ in
@@ -47,7 +75,7 @@ private class ServerImp: Server {
weak var anotherClient: Client!
weak var _optionalProperty = InjectedWeak<AnyObject>(required: false)
var _optionalProperty = InjectedWeak<AnyObject>(required: false)
}
private class ClientImp: Client {
@@ -67,7 +95,18 @@ private class ClientImp: Client {
var taggedServer = Injected<Server>(tag: "tagged")
var nilTaggedServer = Injected<Server>(tag: nil)
}
#endif
#if swift(>=5.1)
private class Obj1 {
@InjectedWeak var obj2: Obj2?
@Injected var obj3: Obj3?
}
private class Obj2 {
@Injected var obj1: Obj1?
}
#else
private class Obj1 {
let obj2 = InjectedWeak<Obj2>()
let obj3 = Injected<Obj3>()
@@ -76,6 +115,7 @@ private class Obj1 {
private class Obj2 {
let obj1 = Injected<Obj1>()
}
#endif
private class Obj3 {
@@ -92,26 +132,6 @@ class AutoInjectionTests: XCTestCase {
static var clientDidInjectCalled: Bool = false
let container = DependencyContainer()
static var allTests = {
return [
("testThatItResolvesAutoInjectedDependencies", testThatItResolvesAutoInjectedDependencies),
("testThatItResolvesInheritedDependencies", testThatItResolvesInheritedDependencies),
("testThatItCanSetInjectedProperty", testThatItCanSetInjectedProperty),
("testThatItThrowsErrorIfFailsToAutoInjectDependency", testThatItThrowsErrorIfFailsToAutoInjectDependency),
("testThatItResolvesAutoInjectedSingletons", testThatItResolvesAutoInjectedSingletons),
("testThatItCallsResolveDependencyBlockWhenAutoInjecting", testThatItCallsResolveDependencyBlockWhenAutoInjecting),
("testThatItReusesResolvedAutoInjectedInstances", testThatItReusesResolvedAutoInjectedInstances),
("testThatItReusesAutoInjectedInstancesOnNextResolveOrAutoInjection", testThatItReusesAutoInjectedInstancesOnNextResolveOrAutoInjection),
("testThatThereIsNoRetainCycleBetweenAutoInjectedCircularDependencies", testThatThereIsNoRetainCycleBetweenAutoInjectedCircularDependencies),
("testThatItCallsDidInjectOnAutoInjectedProperty", testThatItCallsDidInjectOnAutoInjectedProperty),
("testThatNoErrorThrownWhenOptionalPropertiesAreNotAutoInjected", testThatNoErrorThrownWhenOptionalPropertiesAreNotAutoInjected),
("testThatItResolvesTaggedAutoInjectedProperties", testThatItResolvesTaggedAutoInjectedProperties),
("testThatItPassesTagToAutoInjectedProperty", testThatItPassesTagToAutoInjectedProperty),
("testThatItDoesNotPassTagToAutoInjectedPropertyWithExplicitTag", testThatItDoesNotPassTagToAutoInjectedPropertyWithExplicitTag),
("testThatItAutoInjectsPropertyWithCollaboratingContainer", testThatItAutoInjectsPropertyWithCollaboratingContainer)
]
}()
override func setUp() {
container.reset()
@@ -128,10 +148,18 @@ class AutoInjectionTests: XCTestCase {
func testThatItResolvesInheritedDependencies() {
class ServerImp2: ServerImp {
#if swift(>=5.1)
@InjectedWeak(didInject: { _ in
XCTAssertTrue(AutoInjectionTests.serverDidInjectCalled, "Inherited properties should be resolved first")
}) var client2: Client?
#else
var _client2 = InjectedWeak<Client>() { _ in
XCTAssertTrue(AutoInjectionTests.serverDidInjectCalled, "Inherited properties should be resolved first")
}
var client2: Client? { return _client2.value }
var client2: Client? {
return _client2.value
}
#endif
}
container.register { ServerImp2() as Server }
@@ -153,17 +181,23 @@ class AutoInjectionTests: XCTestCase {
let newServer = ServerImp()
let newClient = ClientImp()
#if swift(>=5.1)
client.server = newServer
server.client = newClient
#else
client._server = client._server.setValue(newServer)
server._client = server._client.setValue(newClient)
#endif
XCTAssertTrue(client.server === newServer)
XCTAssertTrue(server.client === newClient)
}
func testThatItThrowsErrorIfFailsToAutoInjectDependency() {
container.register { ClientImp() as Client }
AssertThrows(expression: try container.resolve() as Client)
XCTAssertThrowsError(try self.container.resolve() as Client)
}
func testThatItResolvesAutoInjectedSingletons() {
@@ -245,11 +279,19 @@ class AutoInjectionTests: XCTestCase {
let obj2 = try! container.resolve() as Obj2
//then
XCTAssertTrue(obj2 === obj2.obj1.value!.obj2.value!,
#if swift(>=5.1)
XCTAssertTrue(obj2 === obj2.obj1!.obj2!,
"Auto-injected instance should be reused on next auto-injection")
XCTAssertTrue(obj2.obj1.value! === obj2.obj1.value!.obj3.value!.obj1,
XCTAssertTrue(obj2.obj1! === obj2.obj1!.obj3!.obj1,
"Auto-injected instance should be reused on next resolve")
#else
XCTAssertTrue(obj2 === obj2.obj1.value!.obj2.value!,
"Auto-injected instance should be reused on next auto-injection")
XCTAssertTrue(obj2.obj1.value! === obj2.obj1.value!.obj3.value!.obj1,
"Auto-injected instance should be reused on next resolve")
#endif
}
func testThatThereIsNoRetainCycleBetweenAutoInjectedCircularDependencies() {
@@ -294,7 +336,10 @@ class AutoInjectionTests: XCTestCase {
container.register { ServerImp() as Server }
container.register { ClientImp() as Client }
AssertNoThrow(expression: try container.resolve() as Client, "Container should not throw error if failed to resolve optional auto-injected properties.")
XCTAssertNoThrow(
try container.resolve() as Client,
"Container should not throw error if failed to resolve optional auto-injected properties."
)
}
func testThatItResolvesTaggedAutoInjectedProperties() {
@@ -307,7 +352,11 @@ class AutoInjectionTests: XCTestCase {
let client = try! container.resolve() as Client
//then
#if swift(>=5.1)
let taggedServer = (client as! ClientImp).taggedServer!
#else
let taggedServer = (client as! ClientImp).taggedServer.value!
#endif
let server = client.server!
//server and tagged server should be resolved as different instances
@@ -326,7 +375,11 @@ class AutoInjectionTests: XCTestCase {
let client = try! container.resolve(tag: "tagged") as Client
//then
#if swift(>=5.1)
let taggedServer = (client as! ClientImp).taggedServer!
#else
let taggedServer = (client as! ClientImp).taggedServer.value!
#endif
let server = client.server!
//server and tagged server should be resolved as the same instance
@@ -347,8 +400,13 @@ class AutoInjectionTests: XCTestCase {
let client = try! container.resolve(tag: "otherTag") as Client
//then
#if swift(>=5.1)
let taggedServer = (client as! ClientImp).taggedServer!
let nilTaggedServer = (client as! ClientImp).nilTaggedServer!
#else
let taggedServer = (client as! ClientImp).taggedServer.value!
let nilTaggedServer = (client as! ClientImp).nilTaggedServer.value!
#endif
let server = client.server!
//server and tagged server should be resolved as different instances
@@ -359,19 +417,89 @@ class AutoInjectionTests: XCTestCase {
XCTAssertNotNil(taggedServer)
XCTAssertNotNil(nilTaggedServer)
}
struct Foo
{
struct Bar
{
}
}
struct Baz
{
struct Bar
{
}
}
func testScopedTypes() {
let key1 = DefinitionKey(type: Baz.Bar.self, typeOfArguments: Void.self)
let key2 = DefinitionKey(type: Foo.Bar.self, typeOfArguments: Void.self)
XCTAssertNotEqual(key1, key2)
XCTAssertNotEqual(key1.hashValue, key2.hashValue)
container.register { Baz.Bar() }
XCTAssertNotNil(try? container.resolve() as Baz.Bar)
XCTAssertThrowsError(try container.resolve() as Foo.Bar)
container.register { Foo.Bar() }
XCTAssertNotNil(try? container.resolve() as Foo.Bar)
}
func testThatItAutoInjectsPropertyWithCollaboratingContainer() {
let collaborator = DependencyContainer()
collaborator.register { ServerImp() as Server }
container.register { ClientImp() as Client }
container.collaborate(with: collaborator)
collaborator.collaborate(with: container)
let client = try! container.resolve() as Client
let server = client.server
XCTAssertTrue(client === server?.client)
}
func testThatItDoesNotAutoInjectIfDisabledInDefinition() {
container.register { ServerImp() as Server }
container.register { ClientImp() as Client }
.autoInjectingProperties(false)
let client = try! container.resolve() as Client
let server = client.server
XCTAssertNil(server)
}
func testThatItDoesNotAutoInjectIfDisabledInContainer() {
let container = DependencyContainer(autoInjectProperties: false)
container.register { ServerImp() as Server }
container.register { ClientImp() as Client }
let client = try! container.resolve() as Client
let server = client.server
XCTAssertNil(server)
}
func testThatItAutoInjectsWhenOverriddenInDefinition() {
let container = DependencyContainer(autoInjectProperties: false)
container.register { ServerImp() as Server }
container.register { ClientImp() as Client }
.autoInjectingProperties(true)
let client = try! container.resolve() as Client
let server = client.server
XCTAssertNotNil(server)
XCTAssertNil(server?.client)
}
}
+34 -55
View File
@@ -25,12 +25,12 @@
import XCTest
@testable import Dip
private protocol Service: class { }
private protocol Service: AnyObject { }
private class ServiceImp1: Service { }
private class ServiceImp2: Service { }
private class ServiceImp3 {}
private protocol AutoWiredClient: class {
private protocol AutoWiredClient: AnyObject {
var service1: Service! { get set }
var service2: Service! { get set }
}
@@ -50,30 +50,6 @@ class AutoWiringTests: XCTestCase {
let container = DependencyContainer()
static var allTests = {
return [
("testThatItCanResolveWithAutoWiring", testThatItCanResolveWithAutoWiring),
("testThatItUsesAutoWireFactoryWithMostNumberOfArguments", testThatItUsesAutoWireFactoryWithMostNumberOfArguments),
("testThatItThrowsAmbiguityErrorWhenUsingAutoWire", testThatItThrowsAmbiguityErrorWhenUsingAutoWire),
("testThatItUsesAutoWireFactoryWithMostNumberOfArguments", testThatItUsesAutoWireFactoryWithMostNumberOfArguments),
("testThatItPrefersTaggedFactoryWithDifferentNumberOfArgumentsWhenUsingAutoWire", testThatItPrefersTaggedFactoryWithDifferentNumberOfArgumentsWhenUsingAutoWire),
("testThatItPrefersTaggedFactoryWithDifferentTypesOfArgumentsWhenUsingAutoWire", testThatItPrefersTaggedFactoryWithDifferentTypesOfArgumentsWhenUsingAutoWire),
("testThatItFallbackToNotTaggedFactoryWhenUsingAutoWire", testThatItFallbackToNotTaggedFactoryWhenUsingAutoWire),
("testThatItDoesNotTryToUseAutoWiringWhenCallingResolveWithArguments", testThatItDoesNotTryToUseAutoWiringWhenCallingResolveWithArguments),
("testThatItDoesNotUseAutoWiringWhenFailedToResolveLowLevelDependency", testThatItDoesNotUseAutoWiringWhenFailedToResolveLowLevelDependency),
("testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgain", testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgain),
("testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithTheSameTag", testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithTheSameTag),
("testThatItDoesNotReuseInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithAnotherTag", testThatItDoesNotReuseInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithAnotherTag),
("testThatItUsesTagToResolveDependenciesWithAutoWiringWith1Argument", testThatItUsesTagToResolveDependenciesWithAutoWiringWith1Argument),
("testThatItUsesTagToResolveDependenciesWithAutoWiringWith2Arguments", testThatItUsesTagToResolveDependenciesWithAutoWiringWith2Arguments),
("testThatItUsesTagToResolveDependenciesWithAutoWiringWith3Arguments", testThatItUsesTagToResolveDependenciesWithAutoWiringWith3Arguments),
("testThatItUsesTagToResolveDependenciesWithAutoWiringWith4Arguments", testThatItUsesTagToResolveDependenciesWithAutoWiringWith4Arguments),
("testThatItUsesTagToResolveDependenciesWithAutoWiringWith5Arguments", testThatItUsesTagToResolveDependenciesWithAutoWiringWith5Arguments),
("testThatItUsesTagToResolveDependenciesWithAutoWiringWith6Arguments", testThatItUsesTagToResolveDependenciesWithAutoWiringWith6Arguments),
("testThatItCanAutoWireOptional", testThatItCanAutoWireOptional)
]
}()
override func setUp() {
container.reset()
}
@@ -138,12 +114,10 @@ class AutoWiringTests: XCTestCase {
container.register { ServiceImp2() }
//when
AssertThrows(expression: try container.resolve() as AutoWiredClient) { error -> Bool in
switch error {
case let DipError.autoWiringFailed(_, error):
if case DipError.ambiguousDefinitions = error { return true }
else { return false }
default: return false
XCTAssertThrowsError(try self.container.resolve() as AutoWiredClient) { (error) in
guard case DipError.autoWiringFailed(_, DipError.ambiguousDefinitions) = error else {
XCTFail("Thrown unexpected error: \(error)")
return
}
}
}
@@ -233,8 +207,12 @@ class AutoWiringTests: XCTestCase {
//when
let service = try! container.resolve() as Service
AssertThrows(expression: try container.resolve(arguments: service) as AutoWiredClient,
"Container should not use auto-wiring when resolving with runtime arguments")
// then
XCTAssertThrowsError(
try self.container.resolve(arguments: service) as AutoWiredClient,
"Container should not use auto-wiring when resolving with runtime arguments"
)
}
func testThatItDoesNotUseAutoWiringWhenFailedToResolveLowLevelDependency() {
@@ -259,12 +237,13 @@ class AutoWiringTests: XCTestCase {
container.register { ServiceImp2() }
//then
AssertThrows(expression: try container.resolve() as AutoWiredClient,
"Container should not use auto-wiring when definition for resolved type is registered.")
XCTAssertThrowsError(
try self.container.resolve() as AutoWiredClient,
"Container should not use auto-wiring when definition for resolved type is registered."
)
}
func testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgain() {
//given
container.register { ServiceImp1() as Service }
container.register { ServiceImp2() }
@@ -282,8 +261,10 @@ class AutoWiringTests: XCTestCase {
let resolved = try! container.resolve() as AutoWiredClient
//then
//when doing another auto-wiring during resolve we should reuse instance
XCTAssertTrue((resolved as! AutoWiredClientImp) === (anotherInstance as! AutoWiredClientImp))
XCTAssertTrue(
(resolved as! AutoWiredClientImp) === (anotherInstance as! AutoWiredClientImp),
"when doing another auto-wiring during resolve we should reuse instance"
)
}
func testThatItReusesInstancesResolvedWithoutAutoWiringWhenUsingAutoWiringAgain() {
@@ -307,8 +288,10 @@ class AutoWiringTests: XCTestCase {
let resolved = try! container.resolve(arguments: service1, service2) as AutoWiredClient
//then
//when doing another auto-wiring during resolve we should reuse instance
XCTAssertTrue((resolved as! AutoWiredClientImp) === (anotherInstance as! AutoWiredClientImp))
XCTAssertTrue(
(resolved as! AutoWiredClientImp) === (anotherInstance as! AutoWiredClientImp),
"when doing another auto-wiring during resolve we should reuse instance"
)
}
func testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithTheSameTag() {
@@ -330,8 +313,10 @@ class AutoWiringTests: XCTestCase {
let resolved = try! container.resolve(tag: "tag") as AutoWiredClient
//then
//when doing another auto-wiring during resolve we should reuse instance
XCTAssertTrue((resolved as! AutoWiredClientImp) === (anotherInstance as! AutoWiredClientImp))
XCTAssertTrue(
(resolved as! AutoWiredClientImp) === (anotherInstance as! AutoWiredClientImp),
"when doing another auto-wiring during resolve we should reuse instance"
)
}
func testThatItDoesNotReuseInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithAnotherTag() {
@@ -353,8 +338,10 @@ class AutoWiringTests: XCTestCase {
let resolved = try! container.resolve(tag: "tag") as AutoWiredClient
//then
//when doing another auto-wiring during resolve we should reuse instance
XCTAssertTrue((resolved as! AutoWiredClientImp) !== (anotherInstance as! AutoWiredClientImp))
XCTAssertTrue(
(resolved as! AutoWiredClientImp) !== (anotherInstance as! AutoWiredClientImp),
"when doing another auto-wiring during resolve we should reuse instance"
)
}
func testThatItUsesTagToResolveDependenciesWithAutoWiringWith1Argument() {
@@ -464,19 +451,11 @@ class AutoWiringTests: XCTestCase {
var resolved: AutoWiredClient?
//when
AssertNoThrow(expression: resolved = try container.resolve() as AutoWiredClient?)
XCTAssertNoThrow(resolved = try self.container.resolve() as AutoWiredClient?)
XCTAssertNotNil(resolved)
//when
AssertNoThrow(expression: resolved = try container.resolve() as AutoWiredClient!)
XCTAssertNotNil(resolved)
//when
AssertNoThrow(expression: resolved = try container.resolve(tag: "tag") as AutoWiredClient?)
XCTAssertNotNil(resolved)
//when
AssertNoThrow(expression: resolved = try container.resolve(tag: "tag") as AutoWiredClient!)
XCTAssertNoThrow(resolved = try self.container.resolve(tag: "tag") as AutoWiredClient?)
XCTAssertNotNil(resolved)
}
+7 -31
View File
@@ -25,7 +25,7 @@
import XCTest
@testable import Dip
private protocol Service: class {}
private protocol Service: AnyObject {}
private class ServiceImp1: Service {}
private class ServiceImp2: Service {}
@@ -47,27 +47,6 @@ class ComponentScopeTests: XCTestCase {
let container = DependencyContainer()
static var allTests = {
return [
("testThatSharedIsDefaultScope", testThatSharedIsDefaultScope),
("testThatScopeCanBeChanged", testThatScopeCanBeChanged),
("testThatItResolvesTypeAsNewInstanceForUniqueScope", testThatItResolvesTypeAsNewInstanceForUniqueScope),
("testThatItReusesInstanceForSingletonScope", testThatItReusesInstanceForSingletonScope),
("testThatSingletonIsNotReusedAcrossContainers", testThatSingletonIsNotReusedAcrossContainers),
("testThatSingletonIsReleasedWhenDefinitionIsRemoved", testThatSingletonIsReleasedWhenDefinitionIsRemoved),
("testThatSingletonIsReleasedWhenDefinitionIsOverridden", testThatSingletonIsReleasedWhenDefinitionIsOverridden),
("testThatSingletonIsReleasedWhenContainerIsReset", testThatSingletonIsReleasedWhenContainerIsReset),
("testThatItReusesInstanceInSharedScopeDuringResolve", testThatItReusesInstanceInSharedScopeDuringResolve),
("testThatItDoesNotReuseInstanceInSharedScopeInNextResolve", testThatItDoesNotReuseInstanceInSharedScopeInNextResolve),
("testThatItDoesNotReuseInstanceInSharedScopeResolvedForNilTag", testThatItDoesNotReuseInstanceInSharedScopeResolvedForNilTagWhenResolvingForAnotherTag),
("testThatItReusesInstanceInSharedScopeResolvedForNilTag", testThatItReusesInstanceInSharedScopeResolvedForNilTag),
("testThatItReusesResolvedInstanceWhenResolvingOptional", testThatItReusesResolvedInstanceWhenResolvingOptional),
("testThatItHoldsWeakReferenceToWeakSingletonInstance", testThatItHoldsWeakReferenceToWeakSingletonInstance),
("testThatItResolvesWeakSingletonAgainAfterItWasReleased", testThatItResolvesWeakSingletonAgainAfterItWasReleased),
("testThatCollaboratingContainersReuseSingletonsResolvedByAnotherContainer", testThatCollaboratingContainersReuseSingletonsResolvedByAnotherContainer)
]
}()
override func setUp() {
container.reset()
}
@@ -301,23 +280,17 @@ class ComponentScopeTests: XCTestCase {
func testThatItReusesResolvedInstanceWhenResolvingOptional() {
var otherService: Service!
var impOtherService: Service!
var anyOtherService: Any!
var anyImpOtherService: Any!
container.register { ServiceImp1() as Service }
.resolvingProperties { container, service in
otherService = try! container.resolve() as Service?
impOtherService = try! container.resolve() as Service!
anyOtherService = try! container.resolve((Service?).self)
anyImpOtherService = try! container.resolve((Service!).self)
}
let service = try! container.resolve() as Service
XCTAssertTrue(otherService as! ServiceImp1 === service as! ServiceImp1)
XCTAssertTrue(impOtherService as! ServiceImp1 === service as! ServiceImp1)
XCTAssertTrue(anyOtherService as! ServiceImp1 === service as! ServiceImp1)
XCTAssertTrue(anyImpOtherService as! ServiceImp1 === service as! ServiceImp1)
}
func testThatItHoldsWeakReferenceToWeakSingletonInstance() {
@@ -343,11 +316,14 @@ class ComponentScopeTests: XCTestCase {
container.register(service, type: Service.self)
//when
//resolve and realease reight away
//resolve and release right away
_ = try? container.resolve() as ServiceImp1
//then
AssertNoThrow(expression: try container.resolve() as Service, "Weak singleton should be resolved again.")
XCTAssertNoThrow(
try container.resolve() as Service,
"Weak singleton should be resolved again."
)
}
func testThatCollaboratingContainersReuseSingletonsResolvedByAnotherContainer() {
-14
View File
@@ -39,20 +39,6 @@ class ContextTests: XCTestCase {
let container = DependencyContainer()
static var allTests = {
return [
("testThatContextStoresCurrentlyResolvedType", testThatContextStoresCurrentlyResolvedType),
("testThatContextStoresInjectedInType", testThatContextStoresInjectedInType),
("testThatContextStoresTheTagPassedToResolve", testThatContextStoresTheTagPassedToResolve),
("testThatContextStoresTheTagPassedToResolveWhenAutoInjecting", testThatContextStoresTheTagPassedToResolveWhenAutoInjecting),
("testThatContextStoresTheTagPassedToResolveWhenAutoWiring", testThatContextStoresTheTagPassedToResolveWhenAutoWiring),
("testThatContextDoesNotOverrideNilTagPassedToResolve", testThatContextDoesNotOverrideNilTagPassedToResolve),
("testThatContextStoresNameOfAutoInjectedProperty", testThatContextStoresNameOfAutoInjectedProperty),
("testThatItDoesNotSetInjectedInTypeWhenResolvingWithCollaboration", testThatItDoesNotSetInjectedInTypeWhenResolvingWithCollaboration),
("testThatContextIsPreservedWhenResolvingWithCollaboration", testThatContextIsPreservedWhenResolvingWithCollaboration)
]
}()
override func setUp() {
container.reset()
container.register { ServiceImp2() }
+1 -14
View File
@@ -36,18 +36,6 @@ class DefinitionTests: XCTestCase {
let tag1 = DependencyContainer.Tag.String("tag1")
let tag2 = DependencyContainer.Tag.String("tag2")
static var allTests = {
return [
("testThatDefinitionKeyIsEqualBy_Type_Factory_Tag", testThatDefinitionKeyIsEqualBy_Type_Factory_Tag),
("testThatDefinitionKeysWithDifferentTypesAreNotEqual", testThatDefinitionKeysWithDifferentTypesAreNotEqual),
("testThatDefinitionKeysWithDifferentFactoriesAreNotEqual", testThatDefinitionKeysWithDifferentFactoriesAreNotEqual),
("testThatDefinitionKeysWithDifferentTagsAreNotEqual", testThatDefinitionKeysWithDifferentTagsAreNotEqual),
("testThatResolveDependenciesCallsResolveDependenciesBlock", testThatResolveDependenciesCallsResolveDependenciesBlock),
("testThatResolveDependenciesBlockIsNotCalledWhenPassedWrongInstance", testThatResolveDependenciesBlockIsNotCalledWhenPassedWrongInstance),
("testThatItRegisteresOptionalTypesAsForwardedTypes", testThatItRegisteresOptionalTypesAsForwardedTypes)
]
}()
func testThatDefinitionKeyIsEqualBy_Type_Factory_Tag() {
let equalKey1 = DefinitionKey(type: Service.self, typeOfArguments: F1.self, tag: tag1)
let equalKey2 = DefinitionKey(type: Service.self, typeOfArguments: F1.self, tag: tag1)
@@ -112,11 +100,10 @@ class DefinitionTests: XCTestCase {
XCTAssertFalse(blockCalled)
}
func testThatItRegisteresOptionalTypesAsForwardedTypes() {
func testThatItRegistersOptionalTypesAsForwardedTypes() {
let def = Definition<Service, ()>(scope: .unique) { ServiceImp() as Service }
XCTAssertTrue(def.implementingTypes.contains(where: { $0 == Service?.self }))
XCTAssertTrue(def.implementingTypes.contains(where: { $0 == Service!.self }))
}
}
+166 -119
View File
@@ -25,14 +25,14 @@
import XCTest
@testable import Dip
private protocol Service: class { }
private protocol Service: AnyObject { }
private class ServiceImp1: Service { }
private class ServiceImp2: Service { }
private protocol Server: class {
weak var client: Client! { get }
private protocol Server: AnyObject {
var client: Client! { get }
}
private protocol Client: class {
private protocol Client: AnyObject {
var server: Server! { get }
}
@@ -49,30 +49,6 @@ class DipTests: XCTestCase {
let container = DependencyContainer()
static var allTests = {
return [
("testThatCreatingContainerWithConfigBlockDoesNotCreateRetainCycle", testThatCreatingContainerWithConfigBlockDoesNotCreateRetainCycle),
("testThatItResolvesInstanceRegisteredWithoutTag", testThatItResolvesInstanceRegisteredWithoutTag),
("testThatItResolvesInstanceRegisteredWithTag", testThatItResolvesInstanceRegisteredWithTag),
("testThatItResolvesDifferentInstancesRegisteredForDifferentTags", testThatItResolvesDifferentInstancesRegisteredForDifferentTags),
("testThatNewRegistrationOverridesPreviousRegistration", testThatNewRegistrationOverridesPreviousRegistration),
("testThatItCallsResolveDependenciesOnDefinition", testThatItCallsResolveDependenciesOnDefinition),
("testThatItThrowsErrorIfCanNotFindDefinitionForType", testThatItThrowsErrorIfCanNotFindDefinitionForType),
("testThatItThrowsErrorIfCanNotFindDefinitionForTag", testThatItThrowsErrorIfCanNotFindDefinitionForTag),
("testThatItThrowsErrorIfCanNotFindDefinitionForFactoryWithArguments", testThatItThrowsErrorIfCanNotFindDefinitionForFactoryWithArguments),
("testThatItThrowsErrorIfConstructorThrows", testThatItThrowsErrorIfConstructorThrows),
("testThatItThrowsErrorIfFailsToResolveDependency", testThatItThrowsErrorIfFailsToResolveDependency),
("testThatItCallsDidResolveDependenciesOnResolvableIntance", testThatItCallsDidResolveDependenciesOnResolvableIntance),
("testThatItCallsDidResolveDependenciesInReverseOrder", testThatItCallsDidResolveDependenciesInReverseOrder),
("testItCallsResolveDependenciesOnResolableInstance", testItCallsResolveDependenciesOnResolableInstance),
("testThatItResolvesCircularDependencies", testThatItResolvesCircularDependencies),
("testThatItCanResolveUsingContainersCollaboration", testThatItCanResolveUsingContainersCollaboration),
("testThatCollaboratingWithSelfIsIgnored", testThatCollaboratingWithSelfIsIgnored),
("testThatCollaboratingContainersAreWeakReferences", testThatCollaboratingContainersAreWeakReferences),
("testThatCollaboratingContainersReuseInstancesResolvedByAnotherContainer", testThatCollaboratingContainersReuseInstancesResolvedByAnotherContainer),
]
}()
override func setUp() {
container.reset()
}
@@ -123,12 +99,6 @@ class DipTests: XCTestCase {
//then
XCTAssertTrue(optService is ServiceImp1)
//and when
let impService = try! container.resolve((Service!).self)
//then
XCTAssertTrue(impService is ServiceImp1)
}
func testThatItResolvesInstanceRegisteredWithTag() {
@@ -152,12 +122,6 @@ class DipTests: XCTestCase {
//then
XCTAssertTrue(optService is ServiceImp1)
//and when
let impService = try! container.resolve((Service!).self, tag: "service")
//then
XCTAssertTrue(impService is ServiceImp1)
}
func testThatItResolvesDifferentInstancesRegisteredForDifferentTags() {
@@ -188,14 +152,6 @@ class DipTests: XCTestCase {
//then
XCTAssertTrue(optService1 is ServiceImp1)
XCTAssertTrue(optService2 is ServiceImp2)
//and when
let impService1 = try! container.resolve((Service!).self, tag: "service1")
let impService2 = try! container.resolve((Service!).self, tag: "service2")
//then
XCTAssertTrue(impService1 is ServiceImp1)
XCTAssertTrue(impService2 is ServiceImp2)
}
func testThatNewRegistrationOverridesPreviousRegistration() {
@@ -241,38 +197,34 @@ class DipTests: XCTestCase {
XCTAssertTrue(resolveDependenciesCalled)
resolveDependenciesCalled = false
//and when
let _ = try! container.resolve((Service!).self)
//then
XCTAssertTrue(resolveDependenciesCalled)
}
func testThatItThrowsErrorIfCanNotFindDefinitionForType() {
//given
container.register { ServiceImp1() as ServiceImp1 }
//when
AssertThrows(expression: try container.resolve() as Service) { error in
guard case let DipError.definitionNotFound(key) = error else { return false }
XCTAssertThrowsError(try self.container.resolve() as Service) { error in
guard case let DipError.definitionNotFound(key) = error else {
XCTFail("Thrown unexpected error: \(error)")
return
}
//then
let expectedKey = DefinitionKey(type: Service.self, typeOfArguments: Void.self, tag: nil)
XCTAssertEqual(key, expectedKey)
return true
}
//and when
AssertThrows(expression: try container.resolve(Service.self)) { error in
guard case let DipError.definitionNotFound(key) = error else { return false }
XCTAssertThrowsError(try self.container.resolve(Service.self)) { error in
guard case let DipError.definitionNotFound(key) = error else {
XCTFail("Thrown unexpected error: \(error)")
return
}
//then
let expectedKey = DefinitionKey(type: Service.self, typeOfArguments: Void.self, tag: nil)
XCTAssertEqual(key, expectedKey)
return true
}
}
@@ -281,25 +233,27 @@ class DipTests: XCTestCase {
container.register(tag: "some tag") { ServiceImp1() as Service }
//when
AssertThrows(expression: try container.resolve(tag: "other tag") as Service) { error in
guard case let DipError.definitionNotFound(key) = error else { return false }
XCTAssertThrowsError(try self.container.resolve(tag: "other tag") as Service) { error in
guard case let DipError.definitionNotFound(key) = error else {
XCTFail("Thrown unexpected error: \(error)")
return
}
//then
let expectedKey = DefinitionKey(type: Service.self, typeOfArguments: Void.self, tag: "other tag")
XCTAssertEqual(key, expectedKey)
return true
}
//and when
AssertThrows(expression: try container.resolve(Service.self, tag: "other tag")) { error in
guard case let DipError.definitionNotFound(key) = error else { return false }
XCTAssertThrowsError(try self.container.resolve(Service.self, tag: "other tag")) { error in
guard case let DipError.definitionNotFound(key) = error else {
XCTFail("Thrown unexpected error: \(error)")
return
}
//then
let expectedKey = DefinitionKey(type: Service.self, typeOfArguments: Void.self, tag: "other tag")
XCTAssertEqual(key, expectedKey)
return true
}
}
@@ -308,25 +262,27 @@ class DipTests: XCTestCase {
container.register { ServiceImp1() as Service }
//when
AssertThrows(expression: try container.resolve(arguments: "some string") as Service) { error in
guard case let DipError.definitionNotFound(key) = error else { return false }
XCTAssertThrowsError(try self.container.resolve(arguments: "some string") as Service) { error in
guard case let DipError.definitionNotFound(key) = error else {
XCTFail("Thrown unexpected error: \(error)")
return
}
//then
let expectedKey = DefinitionKey(type: Service.self, typeOfArguments: String.self, tag: nil)
XCTAssertEqual(key, expectedKey)
return true
}
//and when
AssertThrows(expression: try container.resolve(Service.self, arguments: "some string")) { error in
guard case let DipError.definitionNotFound(key) = error else { return false }
XCTAssertThrowsError(try self.container.resolve(Service.self, arguments: "some string")) { error in
guard case let DipError.definitionNotFound(key) = error else {
XCTFail("Thrown unexpected error: \(error)")
return
}
//then
let expectedKey = DefinitionKey(type: Service.self, typeOfArguments: String.self, tag: nil)
XCTAssertEqual(key, expectedKey)
return true
}
}
@@ -337,18 +293,18 @@ class DipTests: XCTestCase {
container.register { () throws -> Service in throw expectedError }
//when
AssertThrows(expression: try container.resolve() as Service) { error in
switch error {
case let DipError.definitionNotFound(key) where key == failedKey: return true
default: return false
XCTAssertThrowsError(try self.container.resolve() as Service) { error in
guard case let DipError.definitionNotFound(key) = error, key == failedKey else {
XCTFail("Thrown unexpected error: \(error)")
return
}
}
//and when
AssertThrows(expression: try container.resolve(Service.self)) { error in
switch error {
case let DipError.definitionNotFound(key) where key == failedKey: return true
default: return false
XCTAssertThrowsError(try self.container.resolve(Service.self)) { error in
guard case let DipError.definitionNotFound(key) = error, key == failedKey else {
XCTFail("Thrown unexpected error: \(error)")
return
}
}
}
@@ -364,18 +320,18 @@ class DipTests: XCTestCase {
}
//when
AssertThrows(expression: try container.resolve() as Service) { error in
switch error {
case let DipError.definitionNotFound(key) where key == failedKey: return true
default: return false
XCTAssertThrowsError(try self.container.resolve() as Service) { error in
guard case let DipError.definitionNotFound(key) = error, key == failedKey else {
XCTFail("Thrown unexpected error: \(error)")
return
}
}
//and when
AssertThrows(expression: try container.resolve(Service.self)) { error in
switch error {
case let DipError.definitionNotFound(key) where key == failedKey: return true
default: return false
XCTAssertThrowsError(try self.container.resolve(Service.self)) { error in
guard case let DipError.definitionNotFound(key) = error, key == failedKey else {
XCTFail("Thrown unexpected error: \(error)")
return
}
}
}
@@ -520,22 +476,15 @@ class DipTests: XCTestCase {
XCTAssertNotNil(self.server?.client)
XCTAssertNotNil(self.secondServer?.client)
}
}
//given
container.register { try ResolvableServer(client: self.container.resolve()) as Server }
.resolvingProperties { (container: DependencyContainer, server: Server) in
let server = server as! ResolvableServer
server.secondClient = try container.resolve() as Client
}
.resolvingProperty(\ResolvableServer.secondClient, as: Client.self)
container.register { ResolvableClient() as Client }
.resolvingProperties { (container: DependencyContainer, client: Client) in
let client = client as! ResolvableClient
client.server = try container.resolve() as Server
client.secondServer = try container.resolve() as Server
}
.resolvingProperty(\ResolvableClient.server, factory: { try $0.resolve() })
.resolvingProperty(\ResolvableClient.secondServer)
//when
let client = (try! container.resolve() as Client) as! ResolvableClient
@@ -583,7 +532,7 @@ class DipTests: XCTestCase {
}
//then
AssertNoThrow(expression: try container.validate("arg"))
XCTAssertNoThrow(try self.container.validate("arg"))
XCTAssertTrue(createdService1)
XCTAssertTrue(createdService2)
XCTAssertTrue(createdService3)
@@ -606,8 +555,8 @@ class DipTests: XCTestCase {
}
//then
AssertNoThrow(expression:
try container.validate(
XCTAssertNoThrow(
try self.container.validate(
"1",
expectedIntArgument,
"x",
@@ -622,8 +571,18 @@ class DipTests: XCTestCase {
container.register { (a: Int) -> Service in ServiceImp1() as Service }
//then
AssertThrows(expression: try container.validate()) { error in error is DipError }
AssertThrows(expression: try container.validate("1")) { error in error is DipError }
XCTAssertThrowsError(try self.container.validate()) { error in
guard error is DipError else {
XCTFail("Thrown unexpected error: \(error)")
return
}
}
XCTAssertThrowsError(try self.container.validate("1")) { error in
guard error is DipError else {
XCTFail("Thrown unexpected error: \(error)")
return
}
}
}
func testThatItFailsValidationOnlyForDipErrors() {
@@ -635,7 +594,7 @@ class DipTests: XCTestCase {
}
//then
AssertNoThrow(expression: try container.validate())
XCTAssertNoThrow(try self.container.validate())
//given
let key = DefinitionKey(type: Service.self, typeOfArguments: Void.self, tag: nil)
@@ -644,9 +603,11 @@ class DipTests: XCTestCase {
}
//then
AssertThrows(expression: try container.validate()) { error in
if case let DipError.definitionNotFound(_key) = error, _key == key { return true }
else { return false }
XCTAssertThrowsError(try self.container.validate()) { error in
guard case let DipError.definitionNotFound(_key) = error, _key == key else {
XCTFail("Thrown unexpected error: \(error)")
return
}
}
}
@@ -664,10 +625,10 @@ extension DipTests {
container.collaborate(with: collaborator)
//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))
XCTAssertNoThrow(try self.container.resolve() as Service)
XCTAssertNoThrow(try self.container.resolve(Service.self))
XCTAssertNoThrow(try collaborator.resolve() as String)
XCTAssertNoThrow(try collaborator.resolve(String.self))
}
func testThatCollaboratingWithSelfIsIgnored() {
@@ -829,5 +790,91 @@ extension DipTests {
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
)
}
}
extension DipTests {
// https://bugs.swift.org/browse/SR-8878
func test_weak_mirror_regression() {
class A {
static var released = false
deinit {
A.released = true
}
}
class B {
static var released = false
weak var a: A?
init(a: A) {
self.a = a
}
deinit {
B.released = true
}
}
let container = DependencyContainer()
let tag = "my_tag"
container.register(.unique, tag: tag, factory: B.init(a:))
do {
let a0 = A()
let _: B = try container.resolve(tag: tag, arguments: a0)
XCTAssertTrue(B.released)
// Due to regression in swift 4.2 Mirror retains weak children
// https://bugs.swift.org/browse/SR-8878
XCTAssertFalse(A.released)
} catch {
}
}
}
+252
View File
@@ -0,0 +1,252 @@
//
// DipUI
//
// Copyright (c) 2016 Ilya Puchka <ilyapuchka@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#if (canImport(UIKit) || canImport(AppKit)) && !SWIFT_PACKAGE
import XCTest
@testable import Dip
#if canImport(UIKit)
import UIKit
typealias Storyboard = UIStoryboard
typealias ViewController = UIViewController
typealias StoryboardName = String
extension UIStoryboard {
@nonobjc
@discardableResult func instantiateViewControllerWithIdentifier(_ identifier: String) -> UIViewController {
return instantiateViewController(withIdentifier: identifier)
}
}
#else
import AppKit
typealias Storyboard = NSStoryboard
typealias ViewController = NSViewController
typealias StoryboardName = NSStoryboard.Name
extension NSStoryboard {
@discardableResult func instantiateViewControllerWithIdentifier(_ identifier: String) -> NSViewController {
return instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(identifier)) as! NSViewController
}
}
#endif
#if os(iOS)
let storyboardName: StoryboardName = "UIStoryboard"
#elseif os(tvOS)
let storyboardName: StoryboardName = "TVStoryboard"
#else
let storyboardName: StoryboardName = StoryboardName("NSStoryboard")
#endif
class DipViewController: ViewController, StoryboardInstantiatable {}
class NilTagViewController: ViewController, StoryboardInstantiatable {}
class DipUITests: XCTestCase {
let storyboard: Storyboard = {
let bundle = Bundle(for: DipUITests.self)
return Storyboard(name: storyboardName, bundle: bundle)
}()
func testThatViewControllerHasDipTagProperty() {
let viewController = storyboard.instantiateViewControllerWithIdentifier("DipViewController")
XCTAssertEqual(viewController.dipTag, "vc")
}
func testThatItDoesNotResolveIfContainerIsNotSet() {
let container = DependencyContainer()
container.register(tag: "vc") { ViewController() }
.resolvingProperties { _, _ in
XCTFail("Should not resolve when container is not set.")
}
storyboard.instantiateViewControllerWithIdentifier("DipViewController")
}
func testThatItDoesNotResolveIfTagIsNotSet() {
let container = DependencyContainer()
container.register(tag: "vc") { ViewController() }
.resolvingProperties { _, _ in
XCTFail("Should not resolve when container is not set.")
}
DependencyContainer.uiContainers = [container]
storyboard.instantiateViewControllerWithIdentifier("ViewController")
}
func testThatItResolvesIfContainerAndStringTagAreSet() {
var resolved = false
let container = DependencyContainer()
container.register(storyboardType: DipViewController.self, tag: "vc")
.resolvingProperties { _, _ in
resolved = true
}
DependencyContainer.uiContainers = [container]
storyboard.instantiateViewControllerWithIdentifier("DipViewController")
XCTAssertTrue(resolved, "Should resolve when container and tag are set.")
}
func testThatItResolvesIfContainerAndNilTagAreSet() {
var resolved = false
let container = DependencyContainer()
container.register(storyboardType: NilTagViewController.self)
.resolvingProperties { _, _ in
resolved = true
}
DependencyContainer.uiContainers = [container]
storyboard.instantiateViewControllerWithIdentifier("NilTagViewController")
XCTAssertTrue(resolved, "Should resolve when container and nil tag are set.")
}
func testThatItDoesNotResolveIfTagDoesNotMatch() {
let container = DependencyContainer()
container.register(storyboardType: DipViewController.self, tag: "wrong tag")
.resolvingProperties { _, _ in
XCTFail("Should not resolve when container is not set.")
}
DependencyContainer.uiContainers = [container]
storyboard.instantiateViewControllerWithIdentifier("DipViewController")
}
func testThatItResolvesWithDefinitionWithNoTag() {
var resolved = false
let container = DependencyContainer()
container.register(storyboardType: DipViewController.self)
.resolvingProperties { _, _ in
resolved = true
}
DependencyContainer.uiContainers = [container]
storyboard.instantiateViewControllerWithIdentifier("DipViewController")
XCTAssertTrue(resolved, "Should fallback to definition with no tag.")
}
func testThatItIteratesUIContainers() {
var resolved = false
let container1 = DependencyContainer()
let container2 = DependencyContainer()
container2.register(storyboardType: DipViewController.self, tag: "vc")
.resolvingProperties { container, _ in
XCTAssertTrue(container === container2)
resolved = true
}
DependencyContainer.uiContainers = [container1, container2]
storyboard.instantiateViewControllerWithIdentifier("DipViewController")
XCTAssertTrue(resolved, "Should resolve using second container")
}
}
protocol SomeService: AnyObject {
var delegate: SomeServiceDelegate? { get set }
}
protocol SomeServiceDelegate: AnyObject { }
class SomeServiceImp: SomeService {
weak var delegate: SomeServiceDelegate?
init(delegate: SomeServiceDelegate) {
self.delegate = delegate
}
init(){}
}
protocol OtherService: AnyObject {
var delegate: OtherServiceDelegate? { get set }
}
protocol OtherServiceDelegate: AnyObject {}
class OtherServiceImp: OtherService {
weak var delegate: OtherServiceDelegate?
init(delegate: OtherServiceDelegate){
self.delegate = delegate
}
init(){}
}
protocol SomeScreen: AnyObject {
var someService: SomeService? { get set }
var otherService: OtherService? { get set }
}
class ViewControllerImp: SomeScreen, SomeServiceDelegate, OtherServiceDelegate {
var someService: SomeService?
var otherService: OtherService?
init(){}
}
extension DipUITests {
func testThatItDoesNotCreateNewInstanceWhenResolvingDependenciesOfExternalInstance() {
let container = DependencyContainer()
//given
var factoryCalled = false
container.register(.shared) { () -> SomeScreen in
factoryCalled = true
return ViewControllerImp() as SomeScreen
}
//when
let screen = ViewControllerImp()
try! container.resolveDependencies(of: screen as SomeScreen)
//then
XCTAssertFalse(factoryCalled, "Container should not create new instance when resolving dependencies of external instance.")
}
func testThatItResolvesInstanceThatImplementsSeveralProtocols() {
let container = DependencyContainer()
//given
container.register(.shared) { ViewControllerImp() as SomeScreen }
.resolvingProperties { container, resolved in
//manually provide resolved instance for the delegate properties
resolved.someService = try container.resolve() as SomeService
resolved.someService?.delegate = resolved as? SomeServiceDelegate
resolved.otherService = try container.resolve(arguments: resolved as! OtherServiceDelegate) as OtherService
}
container.register(.shared) { SomeServiceImp() as SomeService }
container.register(.shared) { OtherServiceImp(delegate: $0) as OtherService }
//when
let screen = try! container.resolve() as SomeScreen
//then
XCTAssertNotNil(screen.someService)
XCTAssertNotNil(screen.otherService)
XCTAssertTrue(screen.someService?.delegate === screen)
XCTAssertTrue(screen.otherService?.delegate === screen)
}
}
#endif
+1 -17
View File
@@ -51,22 +51,6 @@ class RuntimeArgumentsTests: XCTestCase {
let container = DependencyContainer()
static var allTests = {
return [
("testThatItResolvesInstanceWithOneArgument", testThatItResolvesInstanceWithOneArgument),
("testThatItResolvesInstanceWithTwoArguments", testThatItResolvesInstanceWithTwoArguments),
("testThatItResolvesInstanceWithThreeArguments", testThatItResolvesInstanceWithThreeArguments),
("testThatItResolvesInstanceWithFourArguments", testThatItResolvesInstanceWithFourArguments),
("testThatItResolvesInstanceWithFiveArguments", testThatItResolvesInstanceWithFiveArguments),
("testThatItResolvesInstanceWithSixArguments", testThatItResolvesInstanceWithSixArguments),
("testThatItRegistersDifferentFactoriesForDifferentNumberOfArguments", testThatItRegistersDifferentFactoriesForDifferentNumberOfArguments),
("testThatItRegistersDifferentFactoriesForDifferentTypesOfArguments", testThatItRegistersDifferentFactoriesForDifferentTypesOfArguments),
("testThatItRegistersDifferentFactoriesForDifferentOrderOfArguments", testThatItRegistersDifferentFactoriesForDifferentOrderOfArguments),
("testThatNewRegistrationWithSameArgumentsOverridesPreviousRegistration", testThatNewRegistrationWithSameArgumentsOverridesPreviousRegistration),
("testThatDifferentFactoriesRegisteredIfArgumentIsOptional", testThatDifferentFactoriesRegisteredIfArgumentIsOptional)
]
}()
override func setUp() {
container.reset()
}
@@ -284,7 +268,7 @@ class RuntimeArgumentsTests: XCTestCase {
//Due to incomplete implementation of SE-0054 (bug: https://bugs.swift.org/browse/SR-2143)
//registering definition with T? and T! arguments types will produce two different definitions
//but when argement of T! will be passed to `resolve` method it will be transformed to T?
//but when argument of T! will be passed to `resolve` method it will be transformed to T?
//and wrong definition will be used
//When fixed using T? and T! should not register two different definitions
+6 -94
View File
@@ -22,14 +22,15 @@
// THE SOFTWARE.
//
#if canImport(ObjectiveC)
import XCTest
@testable import Dip
private protocol Server: class {
private protocol Server: AnyObject {
var client: Client! { get set }
}
private protocol Client: class {
private protocol Client: AnyObject {
var server: Server { get }
}
@@ -48,8 +49,8 @@ private class ServerImp: Server, Hashable {
weak var client: Client!
init() {}
var hashValue: Int {
return Unmanaged.passUnretained(self).toOpaque().hashValue
func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}
@@ -62,22 +63,6 @@ private var resolvedClients = Array<ClientImp>()
private var container: DependencyContainer!
#if os(Linux)
import Glibc
private var runningThreads: Int = 0
private var lock: pthread_spinlock_t = 0
private let resolveClientSync: () -> Client? = {
let pointer = dispatch_sync { _ in
let resolved = try! container.resolve() as Client
return UnsafeMutableRawPointer(Unmanaged.passRetained(resolved as! ClientImp).toOpaque())
}
guard let clientPointer = pointer else { return nil }
return Unmanaged<ClientImp>.fromOpaque(clientPointer).takeRetainedValue()
}
#else
let queue = OperationQueue()
let lock = RecursiveLock()
@@ -88,18 +73,11 @@ private let resolveClientSync: () -> Client? = {
}
return client
}
#endif
let resolveServerAsync = {
let server = try! container.resolve() as Server
lock.lock()
resolvedServers.insert(server as! ServerImp)
#if os(Linux)
runningThreads -= 1
#endif
lock.unlock()
}
@@ -107,31 +85,11 @@ let resolveClientAsync = {
let client = try! container.resolve() as Client
lock.lock()
resolvedClients.append(client as! ClientImp)
#if os(Linux)
runningThreads -= 1
#endif
lock.unlock()
}
class ThreadSafetyTests: XCTestCase {
#if os(Linux)
required init(name: String, testClosure: @escaping (XCTestCase) throws -> Void) {
pthread_spin_init(&lock, 0)
super.init(name: name, testClosure: testClosure)
}
#endif
static var allTests = {
return [
("testSingletonThreadSafety", testSingletonThreadSafety),
("testFactoryThreadSafety", testFactoryThreadSafety),
("testCircularReferenceThreadSafety", testCircularReferenceThreadSafety)
]
}()
override func setUp() {
Dip.logLevel = .Verbose
container = DependencyContainer()
@@ -146,25 +104,10 @@ class ThreadSafetyTests: XCTestCase {
container.register(.singleton) { ServerImp() as Server }
for _ in 0..<100 {
#if os(Linux)
lock.lock()
runningThreads += 1
lock.unlock()
dispatch_async { _ in
resolveServerAsync()
return nil
}
#else
queue.addOperation(resolveServerAsync)
#endif
}
#if os(Linux)
while runningThreads > 0 { sleep(1) }
#else
queue.waitUntilAllOperationsAreFinished()
#endif
XCTAssertEqual(resolvedServers.count, 1, "Should create only one instance")
}
@@ -174,25 +117,10 @@ class ThreadSafetyTests: XCTestCase {
container.register { ServerImp() as Server }
for _ in 0..<100 {
#if os(Linux)
lock.lock()
runningThreads += 1
lock.unlock()
dispatch_async { _ in
resolveServerAsync()
return nil
}
#else
queue.addOperation(resolveServerAsync)
#endif
}
#if os(Linux)
while runningThreads > 0 { sleep(1) }
#else
queue.waitUntilAllOperationsAreFinished()
#endif
XCTAssertEqual(resolvedServers.count, 100, "All instances should be different")
}
@@ -209,25 +137,10 @@ class ThreadSafetyTests: XCTestCase {
}
for _ in 0..<100 {
#if os(Linux)
lock.lock()
runningThreads += 1
lock.unlock()
dispatch_async { _ in
resolveClientAsync()
return nil
}
#else
queue.addOperation(resolveClientAsync)
#endif
}
#if os(Linux)
while runningThreads > 0 { sleep(1) }
#else
queue.waitUntilAllOperationsAreFinished()
#endif
XCTAssertEqual(resolvedClients.count, 100, "Instances should be not reused in different object graphs")
for client in resolvedClients {
@@ -238,5 +151,4 @@ class ThreadSafetyTests: XCTestCase {
}
}
#endif
+70 -30
View File
@@ -25,31 +25,38 @@
import XCTest
@testable import Dip
private protocol Service: class { }
private protocol ForwardedType: class { }
private protocol Service: AnyObject { }
private protocol ForwardedType: AnyObject { }
#if os(Linux)
private class ServiceImp1: Service, ForwardedType { }
private class ServiceImp2: Service, ForwardedType { }
#else
private class ServiceImp1: NSObject, Service, ForwardedType { }
private class ServiceImp2: NSObject, Service, ForwardedType { }
#endif
private protocol Dependency {}
private struct DependencyImpl: Dependency {}
private class DependencyRefImpl: Dependency {}
private struct DependencyClient {
let dep: Dependency
init(dependency: Dependency) {
self.dep = dependency
}
}
private struct OptionalDependencyClient {
let dep: Dependency?
init(dependency: Dependency?) {
self.dep = dependency
}
}
class TypeForwardingTests: XCTestCase {
let container = DependencyContainer()
static var allTests = {
return [
("testThatItResolvesInstanceByTypeForwarding", testThatItResolvesInstanceByTypeForwarding),
("testThatItReusesInstanceResolvedByTypeForwarding", testThatItReusesInstanceResolvedByTypeForwarding),
("testThatItDoesNotResolveByTypeForwardingIfRegisteredForAnotherTag", testThatItDoesNotResolveByTypeForwardingIfRegisteredForAnotherTag),
("testThatItDoesNotReuseInstanceResolvedByTypeForwardingRegisteredForAnotherTag", testThatItDoesNotReuseInstanceResolvedByTypeForwardingRegisteredForAnotherTag),
("testThatItCallsResolvedDependenciesBlockWhenResolvingByTypeForwarding", testThatItCallsResolvedDependenciesBlockWhenResolvingByTypeForwarding),
("testThatItCallsResolvedDependenciesBlockProvidedAfterRegistrationWhenResolvingByTypeForwarding",testThatItCallsResolvedDependenciesBlockProvidedAfterRegistrationWhenResolvingByTypeForwarding),
("testThatItFallbackToDefinitionWithNoTagWhenResolvingInstanceByTypeForwarding", testThatItFallbackToDefinitionWithNoTagWhenResolvingInstanceByTypeForwarding),
("testThatItCanResolveOptional", testThatItCanResolveOptional),
("testThatItFirstUsesTaggedDefinitionWhenResolvingOptional", testThatItFirstUsesTaggedDefinitionWhenResolvingOptional),
("testThatItThrowsErrorWhenResolvingNotImplementedTypeWithTypeForwarding", testThatItThrowsErrorWhenResolvingNotImplementedTypeWithTypeForwarding),
("testThatItOverridesIfSeveralDefinitionsWithTheSameTagForwardTheSameType", testThatItOverridesIfSeveralDefinitionsWithTheSameTagForwardTheSameType),
("testThatItDoesNotOverrideIfDefinitionForwardsTheSameTypeWithDifferentTag", testThatItDoesNotOverrideIfDefinitionForwardsTheSameTypeWithDifferentTag)
]
}()
override func setUp() {
container.reset()
@@ -109,15 +116,15 @@ class TypeForwardingTests: XCTestCase {
def.implements(ForwardedType.self, tag: "otherTag")
//then
AssertThrows(expression: try container.resolve(tag: "tag") as ForwardedType)
AssertThrows(expression: try container.resolve(ForwardedType.self, tag: "tag"))
XCTAssertThrowsError(try self.container.resolve(tag: "tag") as ForwardedType)
XCTAssertThrowsError(try self.container.resolve(ForwardedType.self, tag: "tag"))
//and given
def.implements(ForwardedType.self, tag: "tag")
//then
AssertNoThrow(expression: try container.resolve(tag: "tag") as ForwardedType)
AssertNoThrow(expression: try container.resolve(ForwardedType.self, tag: "tag"))
XCTAssertNoThrow(try self.container.resolve(tag: "tag") as ForwardedType)
XCTAssertNoThrow(try self.container.resolve(ForwardedType.self, tag: "tag"))
}
func testThatItDoesNotReuseInstanceResolvedByTypeForwardingRegisteredForAnotherTag() {
@@ -129,7 +136,7 @@ class TypeForwardingTests: XCTestCase {
resolveDependenciesCalled = true
//when
//resolving via type-forawrding for tag different then tag for original definition
//resolving via type-forwarding for tag different then tag for original definition
let forwardType = try container.resolve(tag: "tag") as ForwardedType
let anyForwardType = try container.resolve(ForwardedType.self, tag: "tag") as! ForwardedType
@@ -252,6 +259,35 @@ class TypeForwardingTests: XCTestCase {
XCTAssertTrue(object is ServiceImp1)
XCTAssertTrue(anyObject is ServiceImp1)
}
func testThatItReusesInstancesResolvedForOptionalType() {
var alreadyResolved = false
container.register(.singleton) { () -> Dependency in
XCTAssertFalse(alreadyResolved)
return DependencyImpl() as Dependency
}
container.register() { DependencyClient(dependency: try! self.container.resolve()) }
container.register() { OptionalDependencyClient(dependency: try! self.container.resolve()) }
let _ = try! container.resolve() as OptionalDependencyClient
let _ = try! container.resolve() as DependencyClient
alreadyResolved = false
let _ = try! container.resolve() as DependencyClient
let _ = try! container.resolve() as OptionalDependencyClient
container.register(.singleton) { () -> Dependency in
XCTAssertFalse(alreadyResolved)
return DependencyRefImpl() as Dependency
}
let client1 = try! container.resolve() as DependencyClient
let client2 = try! container.resolve() as OptionalDependencyClient
XCTAssertTrue(client1.dep as! DependencyRefImpl === client2.dep as! DependencyRefImpl)
}
func testThatItFirstUsesTaggedDefinitionWhenResolvingOptional() {
let expectedTag: DependencyContainer.Tag = .String("tag")
@@ -276,19 +312,23 @@ class TypeForwardingTests: XCTestCase {
.implements(ServiceImp2.self)
//then
AssertThrows(expression: try container.resolve() as ServiceImp2) { error in
guard case let DipError.invalidType(_, key) = error else { return false }
XCTAssertThrowsError(try self.container.resolve() as ServiceImp2) { error in
guard case let DipError.invalidType(_, key) = error else {
XCTFail("Thrown unexpected error: \(error)")
return
}
let expectedKey = DefinitionKey(type: ServiceImp2.self, typeOfArguments: Void.self, tag: nil)
XCTAssertEqual(key, expectedKey)
return true
}
AssertThrows(expression: try container.resolve(ServiceImp2.self)) { error in
guard case let DipError.invalidType(_, key) = error else { return false }
XCTAssertThrowsError(try self.container.resolve(ServiceImp2.self)) { error in
guard case let DipError.invalidType(_, key) = error else {
XCTFail("Thrown unexpected error: \(error)")
return
}
let expectedKey = DefinitionKey(type: ServiceImp2.self, typeOfArguments: Void.self, tag: nil)
XCTAssertEqual(key, expectedKey)
return true
}
}
+1 -36
View File
@@ -28,41 +28,6 @@ import XCTest
typealias NSObject = AnyObject
#endif
func AssertThrows<T>(_ file: StaticString = #file, line: UInt = #line, expression: @autoclosure () throws -> T) {
AssertThrows(file, line: line, expression: expression, "")
}
func AssertThrows<T>(_ file: StaticString = #file, line: UInt = #line, expression: @autoclosure () throws -> T, _ message: String) {
AssertThrows(expression: expression, checkError: { _ in true }, message)
}
func AssertThrows<T>(_ file: StaticString = #file, line: UInt = #line, expression: @autoclosure () throws -> T, checkError: (Error) -> Bool) {
AssertThrows(file, line: line, expression: expression, checkError: checkError, "")
}
func AssertThrows<T>(_ file: StaticString = #file, line: UInt = #line, expression: @autoclosure () throws -> T, checkError: (Error) -> Bool, _ message: String) {
do {
let _ = try expression()
XCTFail(message, file: file, line: line)
}
catch {
XCTAssertTrue(checkError(error), "Thrown unexpected error: \(error)", file: file, line: line)
}
}
func AssertNoThrow<T>(_ file: StaticString = #file, line: UInt = #line, expression: @autoclosure () throws -> T) {
AssertNoThrow(file, line: line, expression: expression, "")
}
func AssertNoThrow<T>(_ file: StaticString = #file, line: UInt = #line, expression: @autoclosure () throws -> T, _ message: String) {
do {
let _ = try expression()
}
catch {
XCTFail(message, file: file, line: line)
}
}
#if os(Linux)
import Glibc
typealias TMain = @convention(c) (UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer?
@@ -78,7 +43,7 @@ func dispatch_async(block: @escaping TMain) -> pthread_t {
}
func dispatch_sync(block: @escaping TMain) -> UnsafeMutableRawPointer? {
var result: UnsafeMutableRawPointer? = UnsafeMutableRawPointer.allocate(bytes: 1, alignedTo: 0)
var result: UnsafeMutableRawPointer? = UnsafeMutableRawPointer.allocate(byteCount: 1, alignment: 0)
let pid = startThread(block)
pthread_join(pid, &result)
return result
-14
View File
@@ -1,14 +0,0 @@
import XCTest
@testable import DipTests
XCTMain([
testCase(DipTests.allTests),
testCase(DefinitionTests.allTests),
testCase(RuntimeArgumentsTests.allTests),
testCase(ComponentScopeTests.allTests),
testCase(AutoInjectionTests.allTests),
// testCase(ThreadSafetyTests.allTests),
testCase(AutoWiringTests.allTests),
testCase(ContextTests.allTests),
testCase(TypeForwardingTests.allTests)
])
+202
View File
@@ -0,0 +1,202 @@
#if !canImport(ObjectiveC)
import XCTest
extension AutoInjectionTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__AutoInjectionTests = [
("testThatItAutoInjectsPropertyWithCollaboratingContainer", testThatItAutoInjectsPropertyWithCollaboratingContainer),
("testThatItAutoInjectsWhenOverridenInDefinition", testThatItAutoInjectsWhenOverridenInDefinition),
("testThatItCallsDidInjectOnAutoInjectedProperty", testThatItCallsDidInjectOnAutoInjectedProperty),
("testThatItCallsResolveDependencyBlockWhenAutoInjecting", testThatItCallsResolveDependencyBlockWhenAutoInjecting),
("testThatItDoesNotAutoInjectIfDisabledInContainer", testThatItDoesNotAutoInjectIfDisabledInContainer),
("testThatItDoesNotAutoInjectIfDisabledInDefinition", testThatItDoesNotAutoInjectIfDisabledInDefinition),
("testThatItDoesNotPassTagToAutoInjectedPropertyWithExplicitTag", testThatItDoesNotPassTagToAutoInjectedPropertyWithExplicitTag),
("testThatItPassesTagToAutoInjectedProperty", testThatItPassesTagToAutoInjectedProperty),
("testThatItResolvesAutoInjectedDependencies", testThatItResolvesAutoInjectedDependencies),
("testThatItResolvesAutoInjectedSingletons", testThatItResolvesAutoInjectedSingletons),
("testThatItResolvesInheritedDependencies", testThatItResolvesInheritedDependencies),
("testThatItResolvesTaggedAutoInjectedProperties", testThatItResolvesTaggedAutoInjectedProperties),
("testThatItReusesAutoInjectedInstancesOnNextResolveOrAutoInjection", testThatItReusesAutoInjectedInstancesOnNextResolveOrAutoInjection),
("testThatItReusesResolvedAutoInjectedInstances", testThatItReusesResolvedAutoInjectedInstances),
("testThatItThrowsErrorIfFailsToAutoInjectDependency", testThatItThrowsErrorIfFailsToAutoInjectDependency),
("testThatNoErrorThrownWhenOptionalPropertiesAreNotAutoInjected", testThatNoErrorThrownWhenOptionalPropertiesAreNotAutoInjected),
("testThatThereIsNoRetainCycleBetweenAutoInjectedCircularDependencies", testThatThereIsNoRetainCycleBetweenAutoInjectedCircularDependencies),
]
}
extension AutoWiringTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__AutoWiringTests = [
("testThatItCanAutoWireOptional", testThatItCanAutoWireOptional),
("testThatItCanResolveWithAutoWiring", testThatItCanResolveWithAutoWiring),
("testThatItDoesNotReuseInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithAnotherTag", testThatItDoesNotReuseInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithAnotherTag),
("testThatItDoesNotTryToUseAutoWiringWhenCallingResolveWithArguments", testThatItDoesNotTryToUseAutoWiringWhenCallingResolveWithArguments),
("testThatItDoesNotUseAutoWiringWhenFailedToResolveLowLevelDependency", testThatItDoesNotUseAutoWiringWhenFailedToResolveLowLevelDependency),
("testThatItFallbackToNotTaggedFactoryWhenUsingAutoWire", testThatItFallbackToNotTaggedFactoryWhenUsingAutoWire),
("testThatItPrefersTaggedFactoryWithDifferentNumberOfArgumentsWhenUsingAutoWire", testThatItPrefersTaggedFactoryWithDifferentNumberOfArgumentsWhenUsingAutoWire),
("testThatItPrefersTaggedFactoryWithDifferentTypesOfArgumentsWhenUsingAutoWire", testThatItPrefersTaggedFactoryWithDifferentTypesOfArgumentsWhenUsingAutoWire),
("testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgain", testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgain),
("testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithTheSameTag", testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithTheSameTag),
("testThatItReusesInstancesResolvedWithoutAutoWiringWhenUsingAutoWiringAgain", testThatItReusesInstancesResolvedWithoutAutoWiringWhenUsingAutoWiringAgain),
("testThatItThrowsAmbiguityErrorWhenUsingAutoWire", testThatItThrowsAmbiguityErrorWhenUsingAutoWire),
("testThatItUsesAutoWireFactoryWithMostNumberOfArguments", testThatItUsesAutoWireFactoryWithMostNumberOfArguments),
("testThatItUsesTagToResolveDependenciesWithAutoWiringWith1Argument", testThatItUsesTagToResolveDependenciesWithAutoWiringWith1Argument),
("testThatItUsesTagToResolveDependenciesWithAutoWiringWith2Arguments", testThatItUsesTagToResolveDependenciesWithAutoWiringWith2Arguments),
("testThatItUsesTagToResolveDependenciesWithAutoWiringWith3Arguments", testThatItUsesTagToResolveDependenciesWithAutoWiringWith3Arguments),
("testThatItUsesTagToResolveDependenciesWithAutoWiringWith4Arguments", testThatItUsesTagToResolveDependenciesWithAutoWiringWith4Arguments),
("testThatItUsesTagToResolveDependenciesWithAutoWiringWith5Arguments", testThatItUsesTagToResolveDependenciesWithAutoWiringWith5Arguments),
("testThatItUsesTagToResolveDependenciesWithAutoWiringWith6Arguments", testThatItUsesTagToResolveDependenciesWithAutoWiringWith6Arguments),
]
}
extension ComponentScopeTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__ComponentScopeTests = [
("testThatCollaboratingContainersReuseSingletonsResolvedByAnotherContainer", testThatCollaboratingContainersReuseSingletonsResolvedByAnotherContainer),
("testThatContainerCanBeBootstrappedAgainAfterReset", testThatContainerCanBeBootstrappedAgainAfterReset),
("testThatItDoesNotReuseInstanceInSharedScopeInNextResolve", testThatItDoesNotReuseInstanceInSharedScopeInNextResolve),
("testThatItDoesNotReuseInstanceInSharedScopeResolvedForNilTagWhenResolvingForAnotherTag", testThatItDoesNotReuseInstanceInSharedScopeResolvedForNilTagWhenResolvingForAnotherTag),
("testThatItHoldsWeakReferenceToWeakSingletonInstance", testThatItHoldsWeakReferenceToWeakSingletonInstance),
("testThatItResolvesTypeAsNewInstanceForUniqueScope", testThatItResolvesTypeAsNewInstanceForUniqueScope),
("testThatItResolvesWeakSingletonAgainAfterItWasReleased", testThatItResolvesWeakSingletonAgainAfterItWasReleased),
("testThatItReusesInstanceForSingletonScope", testThatItReusesInstanceForSingletonScope),
("testThatItReusesInstanceInSharedScopeDuringResolve", testThatItReusesInstanceInSharedScopeDuringResolve),
("testThatItReusesInstanceInSharedScopeResolvedForNilTag", testThatItReusesInstanceInSharedScopeResolvedForNilTag),
("testThatItReusesResolvedInstanceWhenResolvingOptional", testThatItReusesResolvedInstanceWhenResolvingOptional),
("testThatOnlyEagerSingletonIsCreatedWhenContainerIsBootsrapped", testThatOnlyEagerSingletonIsCreatedWhenContainerIsBootsrapped),
("testThatScopeCanBeChanged", testThatScopeCanBeChanged),
("testThatSharedIsDefaultScope", testThatSharedIsDefaultScope),
("testThatSingletonIsNotReusedAcrossContainers", testThatSingletonIsNotReusedAcrossContainers),
("testThatSingletonIsReleasedWhenContainerIsReset", testThatSingletonIsReleasedWhenContainerIsReset),
("testThatSingletonIsReleasedWhenDefinitionIsOverridden", testThatSingletonIsReleasedWhenDefinitionIsOverridden),
("testThatSingletonIsReleasedWhenDefinitionIsRemoved", testThatSingletonIsReleasedWhenDefinitionIsRemoved),
]
}
extension ContextTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__ContextTests = [
("testThatContextDoesNotOverrideNilTagPassedToResolve", testThatContextDoesNotOverrideNilTagPassedToResolve),
("testThatContextIsPreservedWhenResolvingWithCollaboration", testThatContextIsPreservedWhenResolvingWithCollaboration),
("testThatContextStoresCurrentlyResolvedType", testThatContextStoresCurrentlyResolvedType),
("testThatContextStoresInjectedInType", testThatContextStoresInjectedInType),
("testThatContextStoresNameOfAutoInjectedProperty", testThatContextStoresNameOfAutoInjectedProperty),
("testThatContextStoresTheTagPassedToResolve", testThatContextStoresTheTagPassedToResolve),
("testThatContextStoresTheTagPassedToResolveWhenAutoInjecting", testThatContextStoresTheTagPassedToResolveWhenAutoInjecting),
("testThatContextStoresTheTagPassedToResolveWhenAutoWiring", testThatContextStoresTheTagPassedToResolveWhenAutoWiring),
("testThatItDoesNotSetInjectedInTypeWhenResolvingWithCollaboration", testThatItDoesNotSetInjectedInTypeWhenResolvingWithCollaboration),
]
}
extension DefinitionTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__DefinitionTests = [
("testThatDefinitionKeyIsEqualBy_Type_Factory_Tag", testThatDefinitionKeyIsEqualBy_Type_Factory_Tag),
("testThatDefinitionKeysWithDifferentFactoriesAreNotEqual", testThatDefinitionKeysWithDifferentFactoriesAreNotEqual),
("testThatDefinitionKeysWithDifferentTagsAreNotEqual", testThatDefinitionKeysWithDifferentTagsAreNotEqual),
("testThatDefinitionKeysWithDifferentTypesAreNotEqual", testThatDefinitionKeysWithDifferentTypesAreNotEqual),
("testThatItRegisteresOptionalTypesAsForwardedTypes", testThatItRegisteresOptionalTypesAsForwardedTypes),
("testThatResolveDependenciesBlockIsNotCalledWhenPassedWrongInstance", testThatResolveDependenciesBlockIsNotCalledWhenPassedWrongInstance),
("testThatResolveDependenciesCallsResolveDependenciesBlock", testThatResolveDependenciesCallsResolveDependenciesBlock),
]
}
extension DipTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__DipTests = [
("test_weak_mirror_regression", test_weak_mirror_regression),
("testItCallsResolveDependenciesOnResolableInstance", testItCallsResolveDependenciesOnResolableInstance),
("testThatCollaboratingContainersAreWeakReferences", testThatCollaboratingContainersAreWeakReferences),
("testThatCollaboratingContainersReuseInstancesResolvedByAnotherContainer", testThatCollaboratingContainersReuseInstancesResolvedByAnotherContainer),
("testThatCollaboratingWithSelfIsIgnored", testThatCollaboratingWithSelfIsIgnored),
("testThatCollaborationReferencesAreRecursivelyUpdate", testThatCollaborationReferencesAreRecursivelyUpdate),
("testThatContainerAutowireBeforeCollaboration", testThatContainerAutowireBeforeCollaboration),
("testThatContainersShareTheirSingletonsOnlyWithCollaborators", testThatContainersShareTheirSingletonsOnlyWithCollaborators),
("testThatCreatingContainerWithConfigBlockDoesNotCreateRetainCycle", testThatCreatingContainerWithConfigBlockDoesNotCreateRetainCycle),
("testThatItCallsDidResolveDependenciesInReverseOrder", testThatItCallsDidResolveDependenciesInReverseOrder),
("testThatItCallsDidResolveDependenciesOnResolvableIntance", testThatItCallsDidResolveDependenciesOnResolvableIntance),
("testThatItCallsResolveDependenciesOnDefinition", testThatItCallsResolveDependenciesOnDefinition),
("testThatItCanHandleSeparateContainersAndTheirCollaboration", testThatItCanHandleSeparateContainersAndTheirCollaboration),
("testThatItCanResolveUsingContainersCollaboration", testThatItCanResolveUsingContainersCollaboration),
("testThatItFailsValidationIfNoMatchingArgumentsFound", testThatItFailsValidationIfNoMatchingArgumentsFound),
("testThatItFailsValidationOnlyForDipErrors", testThatItFailsValidationOnlyForDipErrors),
("testThatItPicksRuntimeArgumentsWhenValidatingConfiguration", testThatItPicksRuntimeArgumentsWhenValidatingConfiguration),
("testThatItResolvesCircularDependencies", testThatItResolvesCircularDependencies),
("testThatItResolvesDifferentInstancesRegisteredForDifferentTags", testThatItResolvesDifferentInstancesRegisteredForDifferentTags),
("testThatItResolvesInstanceRegisteredWithoutTag", testThatItResolvesInstanceRegisteredWithoutTag),
("testThatItResolvesInstanceRegisteredWithTag", testThatItResolvesInstanceRegisteredWithTag),
("testThatItThrowsErrorIfCanNotFindDefinitionForFactoryWithArguments", testThatItThrowsErrorIfCanNotFindDefinitionForFactoryWithArguments),
("testThatItThrowsErrorIfCanNotFindDefinitionForTag", testThatItThrowsErrorIfCanNotFindDefinitionForTag),
("testThatItThrowsErrorIfCanNotFindDefinitionForType", testThatItThrowsErrorIfCanNotFindDefinitionForType),
("testThatItThrowsErrorIfConstructorThrows", testThatItThrowsErrorIfConstructorThrows),
("testThatItThrowsErrorIfFailsToResolveDependency", testThatItThrowsErrorIfFailsToResolveDependency),
("testThatItValidatesConfiguration", testThatItValidatesConfiguration),
("testThatNewRegistrationOverridesPreviousRegistration", testThatNewRegistrationOverridesPreviousRegistration),
]
}
extension RuntimeArgumentsTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__RuntimeArgumentsTests = [
("testThatDifferentFactoriesRegisteredIfArgumentIsOptional", testThatDifferentFactoriesRegisteredIfArgumentIsOptional),
("testThatItRegistersDifferentFactoriesForDifferentNumberOfArguments", testThatItRegistersDifferentFactoriesForDifferentNumberOfArguments),
("testThatItRegistersDifferentFactoriesForDifferentOrderOfArguments", testThatItRegistersDifferentFactoriesForDifferentOrderOfArguments),
("testThatItRegistersDifferentFactoriesForDifferentTypesOfArguments", testThatItRegistersDifferentFactoriesForDifferentTypesOfArguments),
("testThatItResolvesInstanceWithFiveArguments", testThatItResolvesInstanceWithFiveArguments),
("testThatItResolvesInstanceWithFourArguments", testThatItResolvesInstanceWithFourArguments),
("testThatItResolvesInstanceWithOneArgument", testThatItResolvesInstanceWithOneArgument),
("testThatItResolvesInstanceWithSixArguments", testThatItResolvesInstanceWithSixArguments),
("testThatItResolvesInstanceWithThreeArguments", testThatItResolvesInstanceWithThreeArguments),
("testThatItResolvesInstanceWithTwoArguments", testThatItResolvesInstanceWithTwoArguments),
("testThatNewRegistrationWithSameArgumentsOverridesPreviousRegistration", testThatNewRegistrationWithSameArgumentsOverridesPreviousRegistration),
]
}
extension TypeForwardingTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__TypeForwardingTests = [
("testThatItCallsResolvedDependenciesBlockProvidedAfterRegistrationWhenResolvingByTypeForwarding", testThatItCallsResolvedDependenciesBlockProvidedAfterRegistrationWhenResolvingByTypeForwarding),
("testThatItCallsResolvedDependenciesBlockWhenResolvingByTypeForwarding", testThatItCallsResolvedDependenciesBlockWhenResolvingByTypeForwarding),
("testThatItCanResolveOptional", testThatItCanResolveOptional),
("testThatItDoesNotOverrideIfDefinitionForwardsTheSameTypeWithDifferentTag", testThatItDoesNotOverrideIfDefinitionForwardsTheSameTypeWithDifferentTag),
("testThatItDoesNotResolveByTypeForwardingIfRegisteredForAnotherTag", testThatItDoesNotResolveByTypeForwardingIfRegisteredForAnotherTag),
("testThatItDoesNotReuseInstanceResolvedByTypeForwardingRegisteredForAnotherTag", testThatItDoesNotReuseInstanceResolvedByTypeForwardingRegisteredForAnotherTag),
("testThatItFallbackToDefinitionWithNoTagWhenResolvingInstanceByTypeForwarding", testThatItFallbackToDefinitionWithNoTagWhenResolvingInstanceByTypeForwarding),
("testThatItFirstUsesTaggedDefinitionWhenResolvingOptional", testThatItFirstUsesTaggedDefinitionWhenResolvingOptional),
("testThatItOverridesIfSeveralDefinitionsWithTheSameTagForwardTheSameType", testThatItOverridesIfSeveralDefinitionsWithTheSameTagForwardTheSameType),
("testThatItResolvesInstanceByTypeForwarding", testThatItResolvesInstanceByTypeForwarding),
("testThatItReusesInstanceResolvedByTypeForwarding", testThatItReusesInstanceResolvedByTypeForwarding),
("testThatItReusesInstancesResolvedForOptionalType", testThatItReusesInstancesResolvedForOptionalType),
("testThatItThrowsErrorWhenResolvingNotImplementedTypeWithTypeForwarding", testThatItThrowsErrorWhenResolvingNotImplementedTypeWithTypeForwarding),
]
}
public func __allTests() -> [XCTestCaseEntry] {
return [
testCase(AutoInjectionTests.__allTests__AutoInjectionTests),
testCase(AutoWiringTests.__allTests__AutoWiringTests),
testCase(ComponentScopeTests.__allTests__ComponentScopeTests),
testCase(ContextTests.__allTests__ContextTests),
testCase(DefinitionTests.__allTests__DefinitionTests),
testCase(DipTests.__allTests__DipTests),
testCase(RuntimeArgumentsTests.__allTests__RuntimeArgumentsTests),
testCase(TypeForwardingTests.__allTests__TypeForwardingTests),
]
}
#endif