Compare commits

...

41 Commits

Author SHA1 Message Date
Ilya Puchka 1356a8056f Merge pull request #70 from ilyapuchka/release/4.4.0
Release 4.4.0
2016-03-31 22:55:38 +02:00
Ilya Puchka 0ddb37bea3 Bumped version to 4.4.0 2016-03-31 22:39:00 +02:00
Ilya Puchka 537cad5923 updated docs 2016-03-31 22:37:53 +02:00
Ilya Puchka 0c4ce2213b bootstrap method can throw 2016-03-31 22:19:29 +02:00
Ilya Puchka a91dacb29c Merge pull request #65 from ilyapuchka/feature/eager-singleton-scope
EagerSingleton scope and bootstrap method
2016-03-31 10:03:59 +02:00
Ilya Puchka 895a6f2583 Merge pull request #67 from ilyapuchka/feature/resolvable-calls-order
Reversed order of Resolvable callback calls
2016-03-31 10:03:52 +02:00
Ilya Puchka 73f71a99b2 Reversed order of Resolvable callback calls 2016-03-30 21:56:45 +02:00
Ilya Puchka 41664914f4 EagerSingleton scope and bootstrap method 2016-03-25 10:24:49 +01:00
Ilya Puchka 53bc97ba63 Update README.md
Added some fancy badges
2016-03-24 14:34:38 +01:00
Ilya Puchka c321189b66 Merge branch 'release/4.3.1' into develop 2016-03-24 12:24:05 +01:00
Ilya Puchka 317d67ca90 Merge pull request #64 from AliSoftware/release/4.3.1
Release 4.3.1
2016-03-24 12:23:17 +01:00
Ilya Puchka cbcef835f7 Bumped version to 4.3.1 2016-03-24 12:10:21 +01:00
Ilya Puchka 47a1870de1 Fixed sample app warnings 2016-03-24 12:02:50 +01:00
Ilya Puchka da2197c909 updated travis script to use Xcode 7.3 2016-03-24 11:48:41 +01:00
Ilya Puchka 3ee8b04118 Merge pull request #62 from mwoollard/develop
Fix build warnings / issues from Swift 2.2
2016-03-24 10:50:45 +01:00
Mark Woollard 29c1a3805f Fix build warnings / issues from Swift 2.2 2016-03-22 13:32:37 +00:00
Ilya Puchka 4dc11a6e2d Merge branch 'hotfix/pods' into develop 2016-03-19 17:53:09 +01:00
Ilya Puchka 7a4d9c554c Merge pull request #60 from AliSoftware/hotfix/pods
Pods hotfix
2016-03-19 17:52:46 +01:00
Ilya Puchka cf4bb0352a pods hotfix 2016-03-19 17:40:59 +01:00
Ilya Puchka 8c6a822eca Merge pull request #59 from AliSoftware/release/4.3.0
Release 4.3.0
2016-03-19 17:09:18 +01:00
Ilya Puchka 4c6718efb3 Merge branch 'release/4.3.0' into develop 2016-03-19 17:06:41 +01:00
Ilya Puchka 1306f12804 Merge branch 'master' into release/4.3.0 2016-03-19 17:02:06 +01:00
Ilya Puchka 764cb98a59 bumped version to 4.3.0 2016-03-19 16:54:42 +01:00
Ilya Puchka 130d71ce3c Added missing tests for Linux 2016-03-19 16:46:17 +01:00
Ilya Puchka 490c2a4d56 Updated CHANGELOG 2016-03-19 16:00:03 +01:00
Ilya Puchka 6e98f270bf Merge pull request #58 from AliSoftware/remove-resolution-failed-error
Removed ResolutionFailed error
2016-03-19 15:44:34 +01:00
Ilya Puchka b3d090c3fd Call didResolveDependencies when graph is complete 2016-03-19 15:33:10 +01:00
Ilya Puchka 4e5cf238c9 removed ResolutionFailed error 2016-03-19 15:33:10 +01:00
Ilya Puchka f4f660d81d Minor DependencyTagConvertible refactoring 2016-03-19 15:33:10 +01:00
Ilya Puchka 211b2fce97 Merge pull request #50 from gavrix/develop
DependencyTagConvertible for better typed Tags
2016-03-19 11:50:19 +01:00
Ilya Puchka 3aa79d1c69 Merge branch 'gavrix-develop' into develop
# Conflicts:
#	Sources/Dip.swift
#	Sources/RuntimeArguments.swift
2016-03-19 11:48:57 +01:00
Sergey Gavrilyuk 4d085d1329 DependencyTag reverted back to DependencyCotnainer.Tag 2016-03-19 11:39:13 +01:00
Ilya Puchka 84845c9b17 Merge pull request #57 from AliSoftware/feature/resolve-dependencies-callback
Added Resolvable protocol
2016-03-19 00:06:34 +01:00
Ilya Puchka f4c38f281e Merge pull request #55 from AliSoftware/feature/auto-wiring
Auto-wiring
2016-03-19 00:05:55 +01:00
Ilya Puchka 99315e32eb Added Resolvable protocol 2016-03-18 23:49:04 +01:00
Ilya Puchka b48fe9840a Moved numberOfArguments property to Definition 2016-03-18 23:40:04 +01:00
Ilya Puchka 6a6fbf906b added playground page, changelog and readmy entry, improved docs 2016-03-18 23:39:33 +01:00
Sergey Gavrilyuk 7e5a0c8ada DependencyTag reverted back to DependencyCotnainer.Tag 2016-03-16 14:43:32 -04:00
Ilya Puchka 1f7ce50035 auto-wiring 2016-03-07 23:59:34 +01:00
Ilya Puchka 3700f687a2 Merge branch 'release/4.2.0' into develop 2016-02-27 15:00:34 +01:00
Sergey Gavrilyuk c817980dd8 DependencyTagConvertible introduced 2016-02-23 11:39:55 -05:00
26 changed files with 1203 additions and 218 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
language: objective-c
osx_image: xcode7.2
osx_image: xcode7.3
# cache: cocoapods
# before_install:
+24
View File
@@ -1,5 +1,29 @@
# CHANGELOG
## 4.4.0
* Added `.EagerSingleton` scope for objectes requiring early instantiation and `bootstrap()` method on `DepenencyContainer`.
[#65](https://github.com/AliSoftware/Dip/pull/65), [@ilyapuchka](https://github.com/ilyapuchka)
* Reverted order of `Resolvable` callbacks.
[#67](https://github.com/AliSoftware/Dip/pull/67), [@ilyapuchka](https://github.com/ilyapuchka)
## 4.3.1
* Fix Swift 2.2 compile errors in tests.
[#62](https://github.com/AliSoftware/Dip/pull/62), [@mwoollard](https://github.com/mwoollard)
## 4.3.0
* Added `DependencyTagConvertible` protocol for better typed tags.
[#50](https://github.com/AliSoftware/Dip/pull/50), [@gavrix](https://github.com/gavrix)
* Auto-wiring. `DependencyContainer` resolves constructor arguments automatically.
[#55](https://github.com/AliSoftware/Dip/pull/55), [@ilyapuchka](https://github.com/ilyapuchka)
* Added `Resolvable` protocol to get a callback when dependencies graph is complete.
[#57](https://github.com/AliSoftware/Dip/pull/57), [@ilyapuchka](https://github.com/ilyapuchka)
* Removed `DipError.ResolutionFailed` error for better consistency.
[#58](https://github.com/AliSoftware/Dip/pull/58), [@ilyapuchka](https://github.com/ilyapuchka)
## 4.2.0
* Added support for Swift Package Manager.
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Dip"
s.version = "4.2.0"
s.version = "4.4.0"
s.summary = "A simple Dependency Resolver: Dependency Injection using Protocol resolution."
s.description = <<-DESC
+28 -10
View File
@@ -49,12 +49,19 @@
09873F7A1C1E0252000C02F6 /* AutoInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09873F551C1E0237000C02F6 /* AutoInjection.swift */; };
09873F7B1C1E0253000C02F6 /* AutoInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09873F551C1E0237000C02F6 /* AutoInjection.swift */; };
09873F7C1C1E0254000C02F6 /* AutoInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09873F551C1E0237000C02F6 /* AutoInjection.swift */; };
09B035FC1C5D2AD6001EA5B7 /* AutoWiringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B035FB1C5D2AD6001EA5B7 /* AutoWiringTests.swift */; };
09B035FD1C5D2AD6001EA5B7 /* AutoWiringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B035FB1C5D2AD6001EA5B7 /* AutoWiringTests.swift */; };
09B035FE1C5D2AD6001EA5B7 /* AutoWiringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B035FB1C5D2AD6001EA5B7 /* AutoWiringTests.swift */; };
09B036001C5D2B83001EA5B7 /* AutoWiring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B035FF1C5D2B83001EA5B7 /* AutoWiring.swift */; };
09B036011C5D2B83001EA5B7 /* AutoWiring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B035FF1C5D2B83001EA5B7 /* AutoWiring.swift */; };
09B036021C5D2B83001EA5B7 /* AutoWiring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B035FF1C5D2B83001EA5B7 /* AutoWiring.swift */; };
09B036031C5D2B83001EA5B7 /* AutoWiring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B035FF1C5D2B83001EA5B7 /* AutoWiring.swift */; };
09C20EC11C8B3BFD009A082B /* ThreadSafetyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C20EBF1C8B3BC3009A082B /* ThreadSafetyTests.swift */; };
09C20EC21C8B3BFE009A082B /* ThreadSafetyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C20EBF1C8B3BC3009A082B /* ThreadSafetyTests.swift */; };
09C20EC31C8B3BFF009A082B /* ThreadSafetyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C20EBF1C8B3BC3009A082B /* ThreadSafetyTests.swift */; };
09D598331C6F9EC100F24D49 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D598321C6F9EC100F24D49 /* Utils.swift */; };
09D598341C6F9EC100F24D49 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D598321C6F9EC100F24D49 /* Utils.swift */; };
09D598351C6F9EC100F24D49 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D598321C6F9EC100F24D49 /* Utils.swift */; };
2C15B9511C25F01200EA3486 /* ThreadSafetyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C15B94F1C25EF7800EA3486 /* ThreadSafetyTests.swift */; };
2C15B9521C25F01300EA3486 /* ThreadSafetyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C15B94F1C25EF7800EA3486 /* ThreadSafetyTests.swift */; };
2C15B9531C25F01500EA3486 /* ThreadSafetyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C15B94F1C25EF7800EA3486 /* ThreadSafetyTests.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -102,8 +109,10 @@
0982AF0B1C5183A000B62463 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Utils.swift; path = ../../Sources/Utils.swift; sourceTree = "<group>"; };
09873F551C1E0237000C02F6 /* AutoInjection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutoInjection.swift; path = ../../Sources/AutoInjection.swift; sourceTree = "<group>"; };
09873F751C1E0249000C02F6 /* AutoInjectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutoInjectionTests.swift; path = Sources/AutoInjectionTests.swift; sourceTree = "<group>"; };
09B035FB1C5D2AD6001EA5B7 /* AutoWiringTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutoWiringTests.swift; path = Sources/AutoWiringTests.swift; sourceTree = "<group>"; };
09B035FF1C5D2B83001EA5B7 /* AutoWiring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutoWiring.swift; path = ../../Sources/AutoWiring.swift; sourceTree = "<group>"; };
09C20EBF1C8B3BC3009A082B /* ThreadSafetyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ThreadSafetyTests.swift; path = Sources/ThreadSafetyTests.swift; sourceTree = "<group>"; };
09D598321C6F9EC100F24D49 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Utils.swift; path = Sources/Utils.swift; sourceTree = "<group>"; };
2C15B94F1C25EF7800EA3486 /* ThreadSafetyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ThreadSafetyTests.swift; path = Sources/ThreadSafetyTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -170,6 +179,7 @@
0919F4C81C16417000DC3B10 /* Definition.swift */,
0919F4CC1C16417000DC3B10 /* RuntimeArguments.swift */,
09873F551C1E0237000C02F6 /* AutoInjection.swift */,
09B035FF1C5D2B83001EA5B7 /* AutoWiring.swift */,
0982AF0B1C5183A000B62463 /* Utils.swift */,
0919F4CB1C16417000DC3B10 /* Info.plist */,
);
@@ -184,7 +194,8 @@
0919F4D21C16417000DC3B10 /* RuntimeArgumentsTests.swift */,
0919F4CE1C16417000DC3B10 /* ComponentScopeTests.swift */,
09873F751C1E0249000C02F6 /* AutoInjectionTests.swift */,
2C15B94F1C25EF7800EA3486 /* ThreadSafetyTests.swift */,
09C20EBF1C8B3BC3009A082B /* ThreadSafetyTests.swift */,
09B035FB1C5D2AD6001EA5B7 /* AutoWiringTests.swift */,
09D598321C6F9EC100F24D49 /* Utils.swift */,
0919F4D11C16417000DC3B10 /* Info.plist */,
);
@@ -495,6 +506,7 @@
files = (
0982AF0C1C5183A000B62463 /* Utils.swift in Sources */,
0919F4D51C16417B00DC3B10 /* Definition.swift in Sources */,
09B036001C5D2B83001EA5B7 /* AutoWiring.swift in Sources */,
0919F4D41C16417B00DC3B10 /* Dip.swift in Sources */,
09873F561C1E0237000C02F6 /* AutoInjection.swift in Sources */,
0919F4D61C16417B00DC3B10 /* RuntimeArguments.swift in Sources */,
@@ -506,9 +518,10 @@
buildActionMask = 2147483647;
files = (
0919F4E61C16419300DC3B10 /* ComponentScopeTests.swift in Sources */,
2C15B9511C25F01200EA3486 /* ThreadSafetyTests.swift in Sources */,
09C20EC11C8B3BFD009A082B /* ThreadSafetyTests.swift in Sources */,
0919F4E41C16419300DC3B10 /* DefinitionTests.swift in Sources */,
09D598331C6F9EC100F24D49 /* Utils.swift in Sources */,
09B035FC1C5D2AD6001EA5B7 /* AutoWiringTests.swift in Sources */,
0919F4E31C16419300DC3B10 /* DipTests.swift in Sources */,
0919F4E51C16419300DC3B10 /* RuntimeArgumentsTests.swift in Sources */,
09873F771C1E024E000C02F6 /* AutoInjectionTests.swift in Sources */,
@@ -521,6 +534,7 @@
files = (
0982AF0D1C5183A000B62463 /* Utils.swift in Sources */,
0919F4D91C16417C00DC3B10 /* Definition.swift in Sources */,
09B036011C5D2B83001EA5B7 /* AutoWiring.swift in Sources */,
0919F4D81C16417C00DC3B10 /* Dip.swift in Sources */,
09873F7A1C1E0252000C02F6 /* AutoInjection.swift in Sources */,
0919F4DA1C16417C00DC3B10 /* RuntimeArguments.swift in Sources */,
@@ -532,9 +546,10 @@
buildActionMask = 2147483647;
files = (
0919F4EA1C16419400DC3B10 /* ComponentScopeTests.swift in Sources */,
2C15B9521C25F01300EA3486 /* ThreadSafetyTests.swift in Sources */,
09C20EC21C8B3BFE009A082B /* ThreadSafetyTests.swift in Sources */,
0919F4E81C16419400DC3B10 /* DefinitionTests.swift in Sources */,
09D598341C6F9EC100F24D49 /* Utils.swift in Sources */,
09B035FD1C5D2AD6001EA5B7 /* AutoWiringTests.swift in Sources */,
0919F4E71C16419400DC3B10 /* DipTests.swift in Sources */,
0919F4E91C16419400DC3B10 /* RuntimeArgumentsTests.swift in Sources */,
09873F781C1E024E000C02F6 /* AutoInjectionTests.swift in Sources */,
@@ -547,6 +562,7 @@
files = (
0982AF0E1C5183A000B62463 /* Utils.swift in Sources */,
0919F4DD1C16417D00DC3B10 /* Definition.swift in Sources */,
09B036021C5D2B83001EA5B7 /* AutoWiring.swift in Sources */,
0919F4DC1C16417D00DC3B10 /* Dip.swift in Sources */,
09873F7B1C1E0253000C02F6 /* AutoInjection.swift in Sources */,
0919F4DE1C16417D00DC3B10 /* RuntimeArguments.swift in Sources */,
@@ -558,9 +574,10 @@
buildActionMask = 2147483647;
files = (
0919F4EE1C16419500DC3B10 /* ComponentScopeTests.swift in Sources */,
2C15B9531C25F01500EA3486 /* ThreadSafetyTests.swift in Sources */,
09C20EC31C8B3BFF009A082B /* ThreadSafetyTests.swift in Sources */,
0919F4EC1C16419500DC3B10 /* DefinitionTests.swift in Sources */,
09D598351C6F9EC100F24D49 /* Utils.swift in Sources */,
09B035FE1C5D2AD6001EA5B7 /* AutoWiringTests.swift in Sources */,
0919F4EB1C16419500DC3B10 /* DipTests.swift in Sources */,
0919F4ED1C16419500DC3B10 /* RuntimeArgumentsTests.swift in Sources */,
09873F791C1E024F000C02F6 /* AutoInjectionTests.swift in Sources */,
@@ -573,6 +590,7 @@
files = (
0982AF0F1C5183A000B62463 /* Utils.swift in Sources */,
0919F4E11C16417E00DC3B10 /* Definition.swift in Sources */,
09B036031C5D2B83001EA5B7 /* AutoWiring.swift in Sources */,
0919F4E01C16417E00DC3B10 /* Dip.swift in Sources */,
09873F7C1C1E0254000C02F6 /* AutoInjection.swift in Sources */,
0919F4E21C16417E00DC3B10 /* RuntimeArguments.swift in Sources */,
@@ -848,7 +866,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 4.2.0;
CURRENT_PROJECT_VERSION = 4.4.0;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@@ -897,7 +915,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 4.2.0;
CURRENT_PROJECT_VERSION = 4.4.0;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -111,7 +111,7 @@ class AutoInjectionTests: XCTestCase {
}
func setUp() {
container.reset()
container.reset()
}
#else
override func setUp() {
+299
View File
@@ -0,0 +1,299 @@
//
// 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.
//
import XCTest
@testable import Dip
private protocol Service: class { }
private class ServiceImp1: Service { }
private class ServiceImp2: Service { }
private protocol AutoWiredClient: class {
var service1: Service! { get set }
var service2: Service! { get set }
}
private class AutoWiredClientImp: AutoWiredClient {
var service1: Service!
var service2: Service!
init(service1: Service, service2: ServiceImp2) {
self.service1 = service1
self.service2 = service2
}
init() {}
}
class AutoWiringTests: XCTestCase {
let container = DependencyContainer()
#if os(Linux)
var allTests: [(String, () throws -> Void)] {
return [
("testThatItCanResolveWithAutoWiring", testThatItCanResolveWithAutoWiring),
("testThatItUsesAutoWireFactoryWithMostNumberOfArguments", testThatItUsesAutoWireFactoryWithMostNumberOfArguments),
("testThatItThrowsAmbiguityErrorWhenUsingAutoWire", testThatItThrowsAmbiguityErrorWhenUsingAutoWire),
("testThatItFirstTriesToUseTaggedFactoriesWhenUsingAutoWire", testThatItFirstTriesToUseTaggedFactoriesWhenUsingAutoWire),
("testThatItFallbackToNotTaggedFactoryWhenUsingAutoWire", testThatItFallbackToNotTaggedFactoryWhenUsingAutoWire),
("testThatItDoesNotTryToUseAutoWiringWhenCallingResolveWithArguments", testThatItDoesNotTryToUseAutoWiringWhenCallingResolveWithArguments),
("testThatItDoesNotUseAutoWiringWhenFailedToResolveLowLevelDependency", testThatItDoesNotUseAutoWiringWhenFailedToResolveLowLevelDependency),
("testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgain", testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgain),
("testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithTheSameTagged", testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithTheSameTagged),
("testThatItDoesNotReuseInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithNoTag", testThatItDoesNotReuseInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithNoTag)
]
}
func setUp() {
container.reset()
}
#else
override func setUp() {
container.reset()
}
#endif
func testThatItCanResolveWithAutoWiring() {
//given
container.register(.ObjectGraph) { ServiceImp1() as Service }
container.register(.ObjectGraph) { ServiceImp2() }
container.register(.ObjectGraph) { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }
//when
let client = try! container.resolve() as AutoWiredClient
//then
let service1 = client.service1
XCTAssertTrue(service1 is ServiceImp1)
let service2 = client.service2
XCTAssertTrue(service2 is ServiceImp2)
}
func testThatItUsesAutoWireFactoryWithMostNumberOfArguments() {
//given
//1 arg
container.register(.ObjectGraph) { AutoWiredClientImp(service1: $0, service2: try self.container.resolve()) as AutoWiredClient }
//1 arg
container.register(.ObjectGraph) { AutoWiredClientImp(service1: try self.container.resolve(), service2: $0) as AutoWiredClient }
//2 args
var factoryWithMostNumberOfArgumentsCalled = false
container.register(.ObjectGraph) { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }
.resolveDependencies { _ in
factoryWithMostNumberOfArgumentsCalled = true
}
container.register(.ObjectGraph) { ServiceImp1() as Service }
container.register(.ObjectGraph) { ServiceImp2() }
//when
let _ = try! container.resolve() as AutoWiredClient
//then
XCTAssertTrue(factoryWithMostNumberOfArgumentsCalled)
}
func testThatItThrowsAmbiguityErrorWhenUsingAutoWire() {
//given
//1 arg
container.register(.ObjectGraph) { AutoWiredClientImp(service1: $0, service2: try self.container.resolve()) as AutoWiredClient }
//1 arg
container.register(.ObjectGraph) { AutoWiredClientImp(service1: try self.container.resolve(), service2: $0) as AutoWiredClient }
container.register(.ObjectGraph) { ServiceImp1() as Service }
container.register(.ObjectGraph) { ServiceImp2() }
//when
AssertThrows(expression: try container.resolve() as AutoWiredClient) { error -> Bool in
switch error {
case DipError.AmbiguousDefinitions: return true
default: return false
}
}
}
func testThatItFirstTriesToUseTaggedFactoriesWhenUsingAutoWire() {
//given
//1 arg
container.register(.ObjectGraph) { AutoWiredClientImp(service1: $0, service2: try self.container.resolve()) as AutoWiredClient }
//1 arg
container.register(.ObjectGraph) { AutoWiredClientImp(service1: try self.container.resolve(), service2: $0) as AutoWiredClient }
//2 args
container.register(.ObjectGraph) { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }
//1 arg tagged
var taggedFactoryWithMostNumberOfArgumentsCalled = false
container.register(tag: "tag", .ObjectGraph) { AutoWiredClientImp(service1: $0, service2: try self.container.resolve()) as AutoWiredClient }
//2 arg tagged
container.register(tag: "tag", .ObjectGraph) { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }.resolveDependencies { _ in
taggedFactoryWithMostNumberOfArgumentsCalled = true
}
container.register(.ObjectGraph) { ServiceImp1() as Service }
container.register(.ObjectGraph) { ServiceImp2() }
//when
let _ = try! container.resolve(tag: "tag") as AutoWiredClient
//then
XCTAssertTrue(taggedFactoryWithMostNumberOfArgumentsCalled)
}
func testThatItFallbackToNotTaggedFactoryWhenUsingAutoWire() {
//given
//1 arg
var notTaggedFactoryWithMostNumberOfArgumentsCalled = false
container.register(.ObjectGraph) { AutoWiredClientImp(service1: $0, service2: try self.container.resolve()) as AutoWiredClient }.resolveDependencies {_ in
notTaggedFactoryWithMostNumberOfArgumentsCalled = true
}
//1 arg tagged
container.register(tag: "tag", .ObjectGraph) { AutoWiredClientImp(service1: $0, service2: try self.container.resolve()) as AutoWiredClient }
container.register(.ObjectGraph) { ServiceImp1() as Service }
container.register(.ObjectGraph) { ServiceImp2() }
//when
let _ = try! container.resolve(tag: "other tag") as AutoWiredClient
//then
XCTAssertTrue(notTaggedFactoryWithMostNumberOfArgumentsCalled)
}
func testThatItDoesNotTryToUseAutoWiringWhenCallingResolveWithArguments() {
//given
container.register(.ObjectGraph) { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }
container.register(.ObjectGraph) { ServiceImp1() as Service }
container.register(.ObjectGraph) { ServiceImp2() }
//when
let service = try! container.resolve() as Service
AssertThrows(expression: try container.resolve(withArguments: service) as AutoWiredClient,
"Container should not use auto-wiring when resolving with runtime arguments")
}
func testThatItDoesNotUseAutoWiringWhenFailedToResolveLowLevelDependency() {
//given
container.register(.ObjectGraph) { AutoWiredClientImp() as AutoWiredClient }
.resolveDependencies { container, resolved in
resolved.service1 = try container.resolve() as Service
resolved.service2 = try container.resolve() as ServiceImp2
//simulate that something goes wrong on the way
throw DipError.DefinitionNotFound(key: DefinitionKey(protocolType: ServiceImp1.self, factoryType: Any.self))
}
container.register(.ObjectGraph) { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }
.resolveDependencies { container, resolved in
//auto-wiring should be performed only when definition for type to resolve is not found
//but not for any other type along the way in the graph
XCTFail("Auto-wiring should not be performed if instance was actually resolved.")
}
container.register(.ObjectGraph) { ServiceImp1() as Service }
container.register(.ObjectGraph) { ServiceImp2() }
//then
AssertThrows(expression: try container.resolve() as AutoWiredClient,
"Container should not use auto-wiring when definition for resolved type is registered.")
}
func testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgain() {
//given
container.register(.ObjectGraph) { ServiceImp1() as Service }
container.register(.ObjectGraph) { ServiceImp2() }
var anotherInstance: AutoWiredClient?
container.register(.ObjectGraph) { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }
.resolveDependencies { container, _ in
if anotherInstance == nil {
anotherInstance = try! container.resolve() as AutoWiredClient
}
}
//when
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))
}
func testThatItReusesInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithTheSameTagged() {
//given
container.register(.ObjectGraph) { ServiceImp1() as Service }
container.register(.ObjectGraph) { ServiceImp2() }
var anotherInstance: AutoWiredClient?
container.register(tag: "tag", .ObjectGraph) { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }
.resolveDependencies { container, _ in
if anotherInstance == nil {
anotherInstance = try! container.resolve(tag: "tag") as AutoWiredClient
}
}
//when
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))
}
func testThatItDoesNotReuseInstancesResolvedWithAutoWiringWhenUsingAutoWiringAgainWithNoTag() {
//given
container.register(.ObjectGraph) { ServiceImp1() as Service }
container.register(.ObjectGraph) { ServiceImp2() }
var anotherInstance: AutoWiredClient?
container.register(.ObjectGraph) { AutoWiredClientImp(service1: $0, service2: $1) as AutoWiredClient }
.resolveDependencies { container, _ in
if anotherInstance == nil {
anotherInstance = try! container.resolve() as AutoWiredClient
}
}
//when
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))
}
}
+116 -59
View File
@@ -96,78 +96,104 @@ class ComponentScopeTests: XCTestCase {
}
func testThatItReusesInstanceForSingletonScope() {
//given
container.register(.Singleton) { ServiceImp1() as Service }
func test(scope: ComponentScope) {
//given
container.register(scope) { ServiceImp1() as Service }
//when
let service1 = try! container.resolve() as Service
let service2 = try! container.resolve() as Service
//then
XCTAssertTrue(service1 === service2)
}
//when
let service1 = try! container.resolve() as Service
let service2 = try! container.resolve() as Service
//then
XCTAssertTrue(service1 === service2)
test(.Singleton)
test(.EagerSingleton)
}
func testThatSingletonIsNotReusedAcrossContainers() {
//given
let def = container.register(.Singleton) { ServiceImp1() as Service }
let secondContainer = DependencyContainer()
secondContainer.register(def, forTag: nil)
func test(scope: ComponentScope) {
//given
let def = container.register(.Singleton) { ServiceImp1() as Service }
let secondContainer = DependencyContainer()
secondContainer.register(def, forTag: nil)
//when
let service1 = try! container.resolve() as Service
let service2 = try! secondContainer.resolve() as Service
//then
XCTAssertTrue(service1 !== service2, "Singleton instances should not be reused across containers")
}
//when
let service1 = try! container.resolve() as Service
let service2 = try! secondContainer.resolve() as Service
//then
XCTAssertTrue(service1 !== service2, "Singleton instances should not be reused across containers")
test(.Singleton)
test(.EagerSingleton)
}
func testThatSingletonIsReleasedWhenDefinitionIsRemoved() {
//given
let def = container.register(.Singleton) { ServiceImp1() as Service }
let service1 = try! container.resolve() as Service
func test(scope: ComponentScope) {
//given
let def = container.register(.Singleton) { ServiceImp1() as Service }
let service1 = try! container.resolve() as Service
//when
container.remove(def, forTag: nil)
container.register(def, forTag: nil)
//then
let service2 = try! container.resolve() as Service
XCTAssertTrue(service1 !== service2, "Singleton instances should be released when definition is removed from the container")
}
//when
container.remove(def, forTag: nil)
container.register(def, forTag: nil)
//then
let service2 = try! container.resolve() as Service
XCTAssertTrue(service1 !== service2, "Singleton instances should be released when definition is removed from the container")
test(.Singleton)
test(.EagerSingleton)
}
func testThatSingletonIsReleasedWhenDefinitionIsOverridden() {
//given
let def = container.register(.Singleton) { ServiceImp1() as Service }
let service1 = try! container.resolve() as Service
func test(scope: ComponentScope) {
//given
let def = container.register(.Singleton) { ServiceImp1() as Service }
let service1 = try! container.resolve() as Service
//when
container.register(def, forTag: nil)
//then
let service2 = try! container.resolve() as Service
XCTAssertTrue(service1 !== service2, "Singleton instances should be released when definition is overridden")
}
//when
container.register(def, forTag: nil)
//then
let service2 = try! container.resolve() as Service
XCTAssertTrue(service1 !== service2, "Singleton instances should be released when definition is overridden")
test(.Singleton)
test(.EagerSingleton)
}
func testThatSingletonIsReleasedWhenContainerIsReset() {
//given
let def = container.register(.Singleton) { ServiceImp1() as Service }
let service1 = try! container.resolve() as Service
func test(scope: ComponentScope) {
//given
let def = container.register(.Singleton) { ServiceImp1() as Service }
let service1 = try! container.resolve() as Service
//when
container.reset()
container.register(def, forTag: nil)
//then
let service2 = try! container.resolve() as Service
XCTAssertTrue(service1 !== service2, "Singleton instances should be released when container is reset")
}
//when
container.reset()
container.register(def, forTag: nil)
//then
let service2 = try! container.resolve() as Service
XCTAssertTrue(service1 !== service2, "Singleton instances should be released when container is reset")
test(.Singleton)
test(.EagerSingleton)
}
func testThatItReusesInstanceInObjectGraphScopeDuringResolve() {
//given
container.register(.ObjectGraph) { Client(server: try self.container.resolve()) as Client }
container.register(.ObjectGraph) { Server() as Server }.resolveDependencies { container, server in
server.client = try container.resolve() as Client
container.register(.ObjectGraph) { Server() as Server }
.resolveDependencies { container, server in
server.client = try container.resolve() as Client
}
//when
@@ -181,8 +207,9 @@ class ComponentScopeTests: XCTestCase {
func testThatItDoesNotReuseInstanceInObjectGraphScopeInNextResolve() {
//given
container.register(.ObjectGraph) { Client(server: try self.container.resolve()) as Client }
container.register(.ObjectGraph) { Server() as Server }.resolveDependencies { container, server in
server.client = try container.resolve() as Client
container.register(.ObjectGraph) { Server() as Server }
.resolveDependencies { container, server in
server.client = try container.resolve() as Client
}
//when
@@ -200,14 +227,15 @@ class ComponentScopeTests: XCTestCase {
func testThatItDoesNotReuseInstanceInObjectGraphScopeResolvedForNilTag() {
//given
var service2: Service?
container.register(.ObjectGraph) { ServiceImp1() as Service }.resolveDependencies { (c, _) in
service2 = try c.resolve(tag: "service") as Service
//then
//when service1 is resolved using this definition due to fallback to nil tag
//we don't want every next resolve of service reuse it
XCTAssertTrue(service2 is ServiceImp2)
container.register(.ObjectGraph) { ServiceImp1() as Service }
.resolveDependencies { (c, _) in
service2 = try c.resolve(tag: "service") as Service
//then
//when service1 is resolved using this definition due to fallback to nil tag
//we don't want every next resolve of service reuse it
XCTAssertTrue(service2 is ServiceImp2)
}
container.register(tag: "service", .ObjectGraph) { ServiceImp2() as Service}
@@ -217,6 +245,35 @@ class ComponentScopeTests: XCTestCase {
//then
XCTAssertTrue(service1 is ServiceImp1)
}
func testThatOnlyEagerSingletonIsCreatedWhenContainerIsBootsrapped() {
//given
var eagerSingletonResolved = false
container.register(tag: "eager", .EagerSingleton) { ServiceImp1() as Service }
.resolveDependencies { container, service in eagerSingletonResolved = true }
container.register(tag: "singleton", .Singleton) { ServiceImp1() as Service }
.resolveDependencies { container, service in XCTFail() }
container.register(tag: "prototype", .Prototype) { ServiceImp1() as Service }
.resolveDependencies { container, service in XCTFail() }
container.register(tag: "graph", .ObjectGraph) { ServiceImp1() as Service }
.resolveDependencies { container, service in XCTFail() }
//when
try! container.bootstrap()
XCTAssertTrue(eagerSingletonResolved)
}
func testThatContainerCanBeBootstrappedAgainAfterReset() {
try! container.bootstrap()
XCTAssertTrue(container.bootstrapped)
container.reset()
XCTAssertFalse(container.bootstrapped)
}
}
+160 -21
View File
@@ -29,6 +29,13 @@ private protocol Service: class { }
private class ServiceImp1: Service { }
private class ServiceImp2: Service { }
private protocol Server: class {
weak var client: Client? { get }
}
private protocol Client: class {
var server: Server? { get }
}
class DipTests: XCTestCase {
let container = DependencyContainer()
@@ -45,7 +52,10 @@ class DipTests: XCTestCase {
("testThatItThrowsErrorIfCanNotFindDefinitionForTag", testThatItThrowsErrorIfCanNotFindDefinitionForTag),
("testThatItThrowsErrorIfCanNotFindDefinitionForFactoryWithArguments", testThatItThrowsErrorIfCanNotFindDefinitionForFactoryWithArguments),
("testThatItThrowsErrorIfConstructorThrows", testThatItThrowsErrorIfConstructorThrows),
("testThatItThrowsErrorIfFailsToResolveDependency", testThatItThrowsErrorIfFailsToResolveDependency)
("testThatItThrowsErrorIfFailsToResolveDependency", testThatItThrowsErrorIfFailsToResolveDependency),
("testThatItCallsDidResolveDependenciesOnResolvableIntance", testThatItCallsDidResolveDependenciesOnResolvableIntance),
("testThatItCallsDidResolveDependenciesInReverseOrder", testThatItCallsDidResolveDependenciesInReverseOrder),
("testThatItResolvesCircularDependencies", testThatItResolvesCircularDependencies)
]
}
@@ -181,15 +191,10 @@ class DipTests: XCTestCase {
//when
AssertThrows(expression: try container.resolve() as Service) { error in
guard case let DipError.ResolutionFailed(key, error) = error else { return false }
guard case let DipError.DefinitionNotFound(subKey) = error where subKey == failedKey else { return false }
//then
typealias F = () throws -> Service
let expectedKey = DefinitionKey(protocolType: Service.self, factoryType: F.self)
XCTAssertEqual(key, expectedKey)
return true
switch error {
case let DipError.DefinitionNotFound(key) where key == failedKey: return true
default: return false
}
}
}
@@ -205,18 +210,152 @@ class DipTests: XCTestCase {
//when
AssertThrows(expression: try container.resolve() as Service) { error in
guard case let DipError.ResolutionFailed(key, error) = error else { return false }
guard case let DipError.DefinitionNotFound(subKey) = error where subKey == failedKey else { return false }
//then
typealias F = () throws -> Service
let expectedKey = DefinitionKey(protocolType: Service.self, factoryType: F.self)
XCTAssertEqual(key, expectedKey)
return true
switch error {
case let DipError.DefinitionNotFound(key) where key == failedKey: return true
default: return false
}
}
}
func testThatItCallsDidResolveDependenciesOnResolvableIntance() {
class ResolvableService: Service, Resolvable {
var didResolveDependenciesCalled = false
func didResolveDependencies() {
XCTAssertFalse(didResolveDependenciesCalled, "didResolveDependencies should be called only once per instance")
didResolveDependenciesCalled = true
}
}
container.register { ResolvableService() as Service }
.resolveDependencies { _, service in
XCTAssertFalse((service as! ResolvableService).didResolveDependenciesCalled, "didResolveDependencies should not be called yet")
return
}
container.register(tag: "graph", .ObjectGraph) { ResolvableService() as Service }
.resolveDependencies { _, service in
XCTAssertFalse((service as! ResolvableService).didResolveDependenciesCalled)
return
}
container.register(tag: "singleton", .Singleton) { ResolvableService() as Service }
.resolveDependencies { _, service in
XCTAssertFalse((service as! ResolvableService).didResolveDependenciesCalled)
return
}
let service = try! container.resolve() as Service
XCTAssertTrue((service as! ResolvableService).didResolveDependenciesCalled)
let graphService = try! container.resolve(tag: "graph") as Service
XCTAssertTrue((graphService as! ResolvableService).didResolveDependenciesCalled)
let singletonService = try! container.resolve(tag: "singleton") as Service
let _ = try! container.resolve(tag: "singleton") as Service
XCTAssertTrue((singletonService as! ResolvableService).didResolveDependenciesCalled)
}
func testThatItCallsDidResolveDependenciesInReverseOrder() {
class ResolvableService: Service, Resolvable {
static var resolved: [Service] = []
func didResolveDependencies() {
ResolvableService.resolved.append(self)
}
}
var resolveDependenciesCalled = false
var service2: Service!
container.register { ResolvableService() as Service }
.resolveDependencies { _, service in
if !resolveDependenciesCalled {
resolveDependenciesCalled = true
service2 = try! self.container.resolve() as Service
}
return
}
let service1 = try! container.resolve() as Service
XCTAssertTrue(ResolvableService.resolved.first === service2)
XCTAssertTrue(ResolvableService.resolved.last === service1)
}
func testThatItResolvesCircularDependencies() {
class ResolvableServer: Server, Resolvable {
weak var client: Client?
weak var secondClient: Client?
init(client: Client) {
self.client = client
}
var didResolveDependenciesCalled = false
func didResolveDependencies() {
XCTAssertFalse(didResolveDependenciesCalled, "didResolveDependencies should be called only once per instance")
didResolveDependenciesCalled = true
XCTAssertNotNil(self.client)
XCTAssertNotNil(self.secondClient)
XCTAssertNotNil(self.client?.server)
XCTAssertNotNil(self.secondClient)
XCTAssertNotNil(self.secondClient?.server)
}
}
class ResolvableClient: Client, Resolvable {
var server: Server?
var secondServer: Server?
init() {}
var didResolveDependenciesCalled = false
func didResolveDependencies() {
XCTAssertFalse(didResolveDependenciesCalled, "didResolveDependencies should be called only once per instance")
didResolveDependenciesCalled = true
XCTAssertNotNil(self.server)
XCTAssertNotNil(self.secondServer)
XCTAssertNotNil(self.server?.client)
XCTAssertNotNil(self.secondServer?.client)
}
}
container.register(.ObjectGraph) { try ResolvableServer(client: self.container.resolve()) as Server }
.resolveDependencies { (container: DependencyContainer, server: Server) in
let server = server as! ResolvableServer
server.secondClient = try container.resolve() as Client
}
container.register(.ObjectGraph) { ResolvableClient() as Client }
.resolveDependencies { (container: DependencyContainer, client: Client) in
let client = client as! ResolvableClient
client.server = try container.resolve() as Server
client.secondServer = try container.resolve() as Server
}
let client = (try! container.resolve() as Client) as! ResolvableClient
let server = client.server as! ResolvableServer
let secondServer = client.secondServer as! ResolvableServer
let secondClient = server.secondClient as! ResolvableClient
XCTAssertTrue(client === server.client)
XCTAssertTrue(client === server.secondClient)
XCTAssertTrue(client === secondServer.client)
XCTAssertTrue(client === secondServer.secondClient)
XCTAssertTrue(client === secondClient)
XCTAssertTrue(server === secondServer)
XCTAssertTrue(client.didResolveDependenciesCalled)
XCTAssertTrue(server.didResolveDependenciesCalled)
}
}
+7 -13
View File
@@ -24,40 +24,34 @@
import XCTest
#if os(Linux)
typealias FileString = StaticString
#else
typealias FileString = String
#endif
func AssertThrows<T>(file: FileString = __FILE__, line: UInt = __LINE__, @autoclosure expression: () throws -> T) {
func AssertThrows<T>(file: StaticString = #file, line: UInt = #line, @autoclosure expression: () throws -> T) {
AssertThrows(file, line: line, expression: expression, "")
}
func AssertThrows<T>(file: FileString = __FILE__, line: UInt = __LINE__, @autoclosure expression: () throws -> T, _ message: String) {
func AssertThrows<T>(file: StaticString = #file, line: UInt = #line, @autoclosure expression: () throws -> T, _ message: String) {
AssertThrows(expression: expression, checkError: { _ in true }, message)
}
func AssertThrows<T>(file: FileString = __FILE__, line: UInt = __LINE__, @autoclosure expression: () throws -> T, checkError: ErrorType -> Bool) {
func AssertThrows<T>(file: StaticString = #file, line: UInt = #line, @autoclosure expression: () throws -> T, checkError: ErrorType -> Bool) {
AssertThrows(file, line: line, expression: expression, checkError: checkError, "")
}
func AssertThrows<T>(file: FileString = __FILE__, line: UInt = __LINE__, @autoclosure expression: () throws -> T, checkError: ErrorType -> Bool, _ message: String) {
func AssertThrows<T>(file: StaticString = #file, line: UInt = #line, @autoclosure expression: () throws -> T, checkError: ErrorType -> Bool, _ message: String) {
do {
try expression()
XCTFail(message, file: file, line: line)
}
catch {
XCTAssertTrue(checkError(error), "Thrown unexpected error: \(error)")
XCTAssertTrue(checkError(error), "Thrown unexpected error: \(error)", file: file, line: line)
}
}
func AssertNoThrow<T>(file: FileString = __FILE__, line: UInt = __LINE__, @autoclosure expression: () throws -> T) {
func AssertNoThrow<T>(file: StaticString = #file, line: UInt = #line, @autoclosure expression: () throws -> T) {
AssertNoThrow(file, line: line, expression: expression, "")
}
func AssertNoThrow<T>(file: FileString = __FILE__, line: UInt = __LINE__, @autoclosure expression: () throws -> T, _ message: String) {
func AssertNoThrow<T>(file: StaticString = #file, line: UInt = #line, @autoclosure expression: () throws -> T, _ message: String) {
do {
try expression()
}
+2 -1
View File
@@ -6,5 +6,6 @@ XCTMain([
RuntimeArgumentsTests(),
ComponentScopeTests(),
AutoInjectionTests(),
ThreadSafetyTests()
ThreadSafetyTests(),
AutoWiringTests()
])
@@ -8,7 +8,9 @@ let container = DependencyContainer()
### Auto-Injection
If you follow Single Responsibility Principle chances are very high that you will end up with more than two collaborating components in your system. Let's say you have a component that depends on few others. Using _Dip_ you can register all of the dependencies in a container as well as that component itself and register a factory that will create that component and feed it with the dependencies resolving them with a container:
On the previous page you saw how auto-wiring helps us get rid of boilerplate code when registering and resolving components with consturctor injection. Auto-injection solves the same problem for property injection.
Let's say you have following related components:
*/
protocol Service: class {
@@ -21,13 +23,18 @@ class ServiceImp: Service {
var tracker: Tracker?
}
/*:
When you register them in a container you will end up with something like this:
*/
container.register() { TrackerImp() as Tracker }
container.register() { LoggerImp() as Logger }
container.register() { ServiceImp() as Service }
.resolveDependencies { container, service in
(service as! ServiceImp).logger = try container.resolve() as Logger
(service as! ServiceImp).tracker = try container.resolve() as Tracker
let service = service as! ServiceImp
service.logger = try container.resolve() as Logger
service.tracker = try container.resolve() as Tracker
}
let service = try! container.resolve() as Service
@@ -35,10 +42,8 @@ service.logger
service.tracker
/*:
Not bad so far. Though that `resolveDependencies` block looks heavy. It would be cool if we can get rid of it. Alternatively you can use _constructor injection_ here, which is actually more prefereable by default but not always possible (see [circular dependencies](Circular%20dependencies)).
Now let's say that you have a bunch of components in your app that require `Logger` or `Tracker` too. You will need to resolve them in a factory for each component again and again. That can be a lot of boilerplate code, simple but still duplicated.
That is one of the scenarios when auto-injection can be useful. It works with property injection and with it the previous code will transform to this:
Notice that the same boilerplate code that we saw in constructor injection now moved to `resolveDepedencies` block.
With auto-injection your code transforms to this:
*/
class AutoInjectedServiceImp: Service {
@@ -60,10 +65,12 @@ As you can see we added two private properties to our implementation of `Service
What is happening under the hood is that after concrete instance of resolved type is created (`Service` in that case), container will iterate through its properties using `Mirror`. For each of the properties wrapped with `Injected<T>` or `InjectedWeak<T>` it will search a definition that can be used to create an instance of wrapped type and use it to create and inject a concrete instance in a `value` property of a wrapper. The fact that wrappers are _classes_ or _reference types_ makes it possible at runtime to inject dependency in instance of resolved type.
You can provide closure that will be called when the dependency will be injected in the property. It is similar to `didSet` property observer.
The requirement for auto-injection is that types injected types should be registered in a container and should use factories with no runtime arguments.
Auto-injected properties can be marked with tag. Then container will search for definition tagged by the same tag to resolve this property.
You can provide closure that will be called when the dependency will be injected in the property. It is similar to `didSet` property observer.
Auto-injected properties are required by default. That means that if container fails to resolve any of auto-injected properties of the instance (or any of its dependencies) it will fail resolution of the object graph in whole.
*/
@@ -198,11 +205,9 @@ autoViewController.router.value
/*:
In such scenario when view controller is created by storyboard you will need to use property injection anyway, so the overhead of adding additional properties for auto-injection is smaller. Also all the boilerplate code of unwrapping injected properties (if you need that) can be moved to extension, cleaning implementation a bit.
> **Note**: For such cases concider using [DipUI](https://github.com/AliSoftware/Dip-UI). It is a small extension for Dip that allows you to do exactly what we need in this example - inject dependencies in instances created by storyboards. It does not require to use auto-injection feature.
> **Note**: For such cases concider using [DipUI](https://github.com/AliSoftware/Dip-UI). It is a small extension for Dip that allows you to do exactly what we need in this example - inject dependencies in instances created by storyboards. It does not require to use auto-injection feature but plays nice with it.
So as you can see there are certain advantages and disadvatages of using auto-injection. It makes your definitions simpler, especially if there are circular dependencies involved or the number of dependencies is high. But it requires additional properties and some boilerplate code in your implementations, makes your implementatios tightly coupled with Dip. It has also some limitations like that it requires factories for auto-injected types that accept no runtime arguments to be registered in a container.
So you should decide for yourself whether you prefer to use auto-injection or "the standard" way. At the end they let you achieve the same result.
So as you can see there are certain advantages and disadvatages of using auto-injection. It makes your definitions simpler, especially if there are circular dependencies involved or the number of dependencies is high, removing boilerplate calls to `resolve` method in `resolveDependencies` block of your definitions. But it requires additional properties and some boilerplate code in your implementations, makes your implementatios tightly coupled with Dip. You can avoid tight coupoling by using your own boxing classes instead of `Injected<T>` and `InjectedWeak<T>` (see `AutoInjectedPropertyBox`).
*/
//: [Next: Testing](@next)
@@ -0,0 +1,117 @@
//: [Previous: Shared Instances](@previous)
import Dip
import UIKit
/*:
### Auto-wiring
Among three main DI patterns - _constructor_, _property_ and _method_ injection - construction injection should be your choise by default. Dip makes use of this pattern very simple.
Let's say you have some VIPER module with following components:
*/
protocol Service {}
protocol Interactor {
var service: Service { get }
}
protocol Router {}
protocol ViewOutput {}
protocol Presenter {
var router: Router { get }
var interactor: Interactor { get }
var view: ViewOutput { get }
}
class RouterImp: Router {}
class View: UIView, ViewOutput {}
class ServiceImp: Service {}
/*:
VIPER module by its nature consists of a lot of components, wired up using protocols. Using construction injection you can end up with following constructors for presenter and interactor:
*/
class InteractorImp: Interactor {
var service: Service
init(service: Service) {
self.service = service
}
}
class PresenterImp: Presenter {
let router: Router
let interactor: Interactor
let view: ViewOutput
init(view: ViewOutput, interactor: Interactor, router: Router) {
self.view = view
self.interactor = interactor
self.router = router
}
}
/*:
If you register these components in a container you will end up with rather boilerplate code:
*/
let container = DependencyContainer()
container.register { ServiceImp() as Service }
container.register { RouterImp() as Router }
container.register { View() as ViewOutput }
container.register { try InteractorImp(service: container.resolve()) as Interactor }
container.register {
try PresenterImp(
view: container.resolve(),
interactor: container.resolve(),
router: container.resolve()) as Presenter
}
var presenter = try! container.resolve() as Presenter
presenter.interactor.service
/*:
While definition for `Interactor` looks fine, `Presenter`'s definition is overloaded with the same `resolve` calls to container.
The other option you have is to register factory with runtime arguments:
*/
container.register { InteractorImp(service: $0) as Interactor }
container.register { PresenterImp(view: $0, interactor: $1, router: $2) as Presenter }
/*:
But then to resolve presenter or interactor you will first need to resolve their dependencies and pass them as arguments to `resolve` method:
*/
let service = try! container.resolve() as Service
let interactor = try! container.resolve(withArguments: service) as Interactor
let view = try! container.resolve() as ViewOutput
let router = try! container.resolve() as Router
presenter = try! container.resolve(withArguments: view, interactor, router) as Presenter
presenter.interactor.service
/*:
Again to much of boilerplate code. Also it's easy to make a mistake in the order of arguments.
Auto-wiring solves this problem by combining these two approaches - you register factories with runtime arguments, but resolve components with just a call to `resolve()`. Container will resolve all consturctor arguments for you.
*/
container.register { InteractorImp(service: $0) as Interactor }
container.register { PresenterImp(view: $0, interactor: $1, router: $2) as Presenter }
presenter = try! container.resolve() as Presenter
presenter.interactor.service
/*:
You don't need to call `resolve` in a factory and care about order of arguments any more.
The only requirement is that all constructor arguments should be registered in the container and there should be no several factories with the same _number_ of arguments registered for the same components.
In very rare case when you have several different factories with different set of runtime arguments registered for the same component, when you try to resolve it container will try to use these factories one by one until one of them succeeds starting with a factory with most numbers of arguments. If it finds two factories with the same number of arguments it will throw an error.
You can use auto-wiring with tags. The tag that you pass to `resolve` method will be used to resolve each of the constructor arguments.
*/
//: [Next: Auto-injection](@next)
@@ -31,11 +31,17 @@ container.register(factory: factory.someService)
/*:
Optionally you can associate definitions with Integer or String tags. This way you can register different implementations for the same protocol.
You can use String or Integer literals, or the `DependencyContainer.Tag` enum.
You can use `DependencyContainer.Tag` enum, String or Integer literals, or instances of types that conform to `DependencyTagConvertible` protocol.
*/
container.register(tag: "tag") { ServiceImp1() as Service }
container.register(tag: DependencyContainer.Tag.Int(0)) { ServiceImp1() as Service }
container.register(tag: .Int(0)) { ServiceImp1() as Service }
enum MyCustomTag: String, DependencyTagConvertible {
case SomeTag
}
container.register(tag: MyCustomTag.SomeTag) { ServiceImp1() as Service }
/*:
We recommand you to use constants for the tags, to make the intent clear and avoid magic numbers and typos.
@@ -44,6 +50,7 @@ You can remove all registered definitions or register and remove them one by one
*/
let serviceDefinition = container.register { ServiceImp1() as Service }
container
container.remove(serviceDefinition)
container.reset()
@@ -13,6 +13,7 @@ Dip supports three different scopes of objects: _Prototype_, _ObjectGraph_ and _
* The `.Prototype` scope will make the `DependencyContainer` resolve your type as __a new instance every time__ you call `resolve`. This is the default scope.
* The `.ObjectGraph` scope is like `.Prototype` scope, but it will make the `DependencyContainer` to reuse resolved instances during one (recursive) call to `resolve` method. When this call returns, all resolved instances will be discarded and next call to `resolve` will produce new instances. This scope should be used to resolve [circular dependencies](Circular%20dependencies).
* The `.Singleton` scope will make the `DependencyContainer` retain the instance once resolved the first time, and reuse it in the next calls to `resolve` during the container lifetime.
* The `.EagerSingleton` scope is the same as `.Singleton` scope but instances with this cope will be created when you call `bootstrap()` method on the container.
The `.Prototype` scope is the default. To set a scope you pass it as an argument to `register` method.
*/
@@ -43,5 +44,23 @@ let sameSharedService = try! container.resolve(tag: "shared instance") as Servic
// same instances, the singleton scope keep and reuse instances during the lifetime of the container
sharedService as! ServiceImp3 === sameSharedService as! ServiceImp3
/*:
### Bootstrapping
You can use `bootstrap()` method to fix your container setup and initialise components registered with `EagerSingleton` scope.
After bootstrapping if you try to add or remove any definition it will cause runtime exception. Call `boostrap` when you registered all the components, for example at the end of initialization block if you use `init(configBlock:)`.
*/
var resolvedEagerSingleton = false
let definition = container.register(tag: "eager shared instance", .EagerSingleton) { ServiceImp1() as Service }
.resolveDependencies { _ in resolvedEagerSingleton = true }
try! container.bootstrap()
resolvedEagerSingleton
let eagerSharedService = try! container.resolve(tag: "eager shared instance") as Service
container.remove(definition)
//: [Next: Circular Dependencies](@next)
@@ -158,6 +158,6 @@ If you want to know more about Dependency Injection in general we recomend you t
*/
//: [Next: Auto-Injection](@next)
//: [Next: Auto-wiring](@next)
@@ -9,6 +9,7 @@
<page name='Scopes'/>
<page name='Circular dependencies'/>
<page name='Shared Instances'/>
<page name='Auto-wiring'/>
<page name='Auto-injection'/>
<page name='Testing'/>
</pages>
+27 -5
View File
@@ -5,6 +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/Linux-compatible-4BC51D.svg?style=flat)](https://developer.apple.com/swift)
[![Swift Version](https://img.shields.io/badge/Swift-2.2-F16D39.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)_
@@ -35,6 +37,8 @@ If you want to know more about Dependency Injection in general we recomend you t
## Installation
Since version 4.3.1 Dip is built with Swift 2.2. The lates version built with Swift 2.1 is 4.3.0.
Dip is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:
@@ -54,7 +58,7 @@ If you use [_Swift Package Manager_](https://swift.org/package-manager/) add Dip
let package = Package(
name: "MyPackage",
dependencies: [
.Package(url: "https://github.com/AliSoftware/Dip.git", "4.2.0")
.Package(url: "https://github.com/AliSoftware/Dip.git", "4.4.0")
]
)
```
@@ -111,11 +115,11 @@ let service: ServiceImp = try! container.resolve()
### Scopes
Dip provides three _scopes_ that you can use to register dependencies:
Dip provides four _scopes_ that you can use to register dependencies:
* The `.Prototype` scope will make the `DependencyContainer` resolve your type as __a new instance every time__ you call `resolve`. It's a default scope.
* The `.ObjectGraph` scope is like `.Prototype` scope but it will make the `DependencyContainer` to reuse resolved instances during one call to `resolve` method. When this call returns all resolved insances will be discarded and next call to `resolve` will produce new instances. This scope _must_ be used to properly resolve circular dependencies.
* The `.Singleton` scope will make the `DependencyContainer` retain the instance once resolved the first time, and reuse it in the next calls to `resolve` during the container lifetime.
* The `.Singleton` and `.EagerSingleton` scopes will make the `DependencyContainer` retain the instance once resolved the first time, and reuse it in the next calls to `resolve` during the container lifetime.
You specify scope when you register dependency like that:
@@ -205,9 +209,27 @@ container.register(.ObjectGraph) { ServerImp() as Server }
```
More information about circular dependencies you can find in the Playground.
### Auto-Injection
### Auto-wiring
Auto-injection lets your resolve all the dependencies of the instance resolved by container with just one call, also allowing a simpler syntax to register circular dependencies.
When you use constructor injection to inject dependencies in your component auto-wiring enables you to resolve it just with one call to `resolve` method without carying about how to resolve all constructor arguments - container will resolve them for you.
```swift
class PresenterImp: Presenter {
init(view: ViewOutput, interactor: Interactor, router: Router) { ... }
...
}
container.register { RouterImp() as Router }
container.register { View() as ViewOutput }
container.register { InteractorImp() as Interactor }
container.register { PresenterImp(view: $0, interactor: $1, router: $2) as Presenter }
let presenter = try! container.resolve() as Presenter
```
### Auto-injection
Auto-injection lets your resolve all property dependencies of the instance resolved by container with just one call, also allowing a simpler syntax to register circular dependencies.
```swift
protocol Server {
+1 -1
View File
@@ -36,6 +36,6 @@ extension BaseCell where Self : UITableViewCell {
}
protocol FillableCell: BaseCell {
typealias ObjectType
associatedtype ObjectType
func fillWithObject(object: ObjectType)
}
@@ -17,6 +17,11 @@ private let FAKE_STARSHIPS = false
/* ---- */
enum DependencyTags: Int, DependencyTagConvertible {
case Hardcoded
case Dummy
}
// MARK: Dependency Container for Providers
func configureContainer(dip: DependencyContainer) {
@@ -47,16 +52,16 @@ func configureContainer(dip: DependencyContainer) {
// 2) Register fake starships provider
//Here we register different implementations for the same protocol using tags
dip.register(tag: "hardcoded") { HardCodedStarshipProvider() as StarshipProviderAPI }
dip.register(tag: DependencyTags.Hardcoded) { HardCodedStarshipProvider() as StarshipProviderAPI }
//Here we register factory that will require a runtime argument
dip.register(tag: "dummy") { DummyStarshipProvider(pilotName: $0) as StarshipProviderAPI }
dip.register(tag: DependencyTags.Dummy) { DummyStarshipProvider(pilotName: $0) as StarshipProviderAPI }
//Here we use constructor injection, but instead of providing dependencies manually container resolves them for us
dip.register() {
FakeStarshipProvider(
dummyProvider: try dip.resolve(tag: "dummy", withArguments: "Main Pilot"),
hardCodedProvider: try dip.resolve(tag: "hardcoded")) as StarshipProviderAPI
dummyProvider: try dip.resolve(tag: DependencyTags.Dummy, withArguments: "Main Pilot"),
hardCodedProvider: try dip.resolve(tag: DependencyTags.Hardcoded)) as StarshipProviderAPI
}
} else {
@@ -9,7 +9,7 @@
import UIKit
protocol FetchableTrait: class {
typealias ObjectType
associatedtype ObjectType
var objects: [ObjectType]? { get set }
var batchRequestID: Int { get set }
var tableView: UITableView! { get }
+6 -6
View File
@@ -49,6 +49,7 @@ extension DependencyContainer {
instead of using `Injected<T>` or `InjectedWeak<T>` types.
**Example**:
```swift
class MyCustomBox<T> {
private(set) var value: T?
@@ -75,7 +76,7 @@ public protocol AutoInjectedPropertyBox: class {
- parameter container: A container to be used to resolve an instance
-note: This method is not intended to be called manually, `DependencyContainer` will call it by itself.
- note: This method is not intended to be called manually, `DependencyContainer` will call it by itself.
*/
func resolve(container: DependencyContainer) throws
}
@@ -93,7 +94,6 @@ public protocol AutoInjectedPropertyBox: class {
class ClientImp: Client {
var service = Injected<Service>()
}
```
- seealso: `InjectedWeak`
@@ -122,7 +122,7 @@ 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 override init(required: Bool = true, tag: DependencyContainer.Tag? = nil, didInject: T -> () = { _ in }) {
public override init(required: Bool = true, tag: DependencyTagConvertible? = nil, didInject: T -> () = { _ in }) {
super.init(required: required, tag: tag, didInject: didInject)
}
@@ -195,7 +195,7 @@ 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 override init(required: Bool = true, tag: DependencyContainer.Tag? = nil, didInject: T -> () = { _ in }) {
public override init(required: Bool = true, tag: DependencyTagConvertible? = nil, didInject: T -> () = { _ in }) {
super.init(required: required, tag: tag, didInject: didInject)
}
@@ -215,9 +215,9 @@ private class _InjectedPropertyBox<T> {
let didInject: T -> ()
let tag: DependencyContainer.Tag?
init(required: Bool = true, tag: DependencyContainer.Tag?, didInject: T -> () = { _ in }) {
init(required: Bool = true, tag: DependencyTagConvertible?, didInject: T -> () = { _ in }) {
self.required = required
self.tag = tag
self.tag = tag?.dependencyTag
self.didInject = didInject
}
+106
View File
@@ -0,0 +1,106 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <olivier@halligon.net>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
extension DependencyContainer {
/// Tries to resolve instance using auto-wire factories
func _resolveByAutoWiring<T>(key: DefinitionKey) throws -> T? {
typealias NoArgumentsFactory = () throws -> T
guard key.factoryType == NoArgumentsFactory.self else { return nil }
let autoWiringDefinitions = self.autoWiringDefinitionsFor(T.self, tag: key.associatedTag)
return try _resolveEnumeratingKeys(autoWiringDefinitions, tag: key.associatedTag)
}
private func autoWiringDefinitionsFor(type: Any.Type, tag: DependencyContainer.Tag?) -> [(DefinitionKey, _Definition)] {
var definitions = self.definitions
.map({ ($0.0, $0.1 as! _Definition) })
//filter definitions
definitions = definitions
.filter({ $0.1.supportsAutoWiring() })
.filter({ $0.0.protocolType == type })
.filter({ $0.0.associatedTag == tag || $0.0.associatedTag == nil })
//order definitions
definitions = definitions
.sort({ $0.1.numberOfArguments > $1.1.numberOfArguments })
definitions =
//first will try to use tagged definitions
definitions.filter({ $0.0.associatedTag == tag }) +
//then will use not tagged definitions
definitions.filter({ $0.0.associatedTag != tag })
return definitions
}
/// Tries definitions one by one until one of them succeeds, otherwise returns nil
private func _resolveEnumeratingKeys<T>(definitions: [(DefinitionKey, _Definition)], tag: DependencyContainer.Tag?) throws -> T? {
for (index, definition) in definitions.enumerate() {
//If the next definition matches current definition then they are ambigous
if case definition? = definitions[next: index] {
throw DipError.AmbiguousDefinitions(
type: definition.0.protocolType,
definitions: [definition.1, definitions[next: index]!.1]
)
}
if let resolved: T = _resolveKey(definition.0, tag: tag) {
return resolved
}
}
return nil
}
private func _resolveKey<T>(key: DefinitionKey, tag: DependencyContainer.Tag?) -> T? {
let key = DefinitionKey(protocolType: key.protocolType, factoryType: key.factoryType, associatedTag: tag)
return try? _resolveKey(key, builder: { definition throws -> T in
guard let resolved = try definition._autoWiringFactory!(self, tag) as? T else {
fatalError("Internal inconsistency exception! Expected type: \(T.self); Definition: \(definition)")
}
return resolved
})
}
}
extension CollectionType {
subscript(safe index: Index) -> Generator.Element? {
guard indices.contains(index) else { return nil }
return self[index]
}
subscript(next index: Index) -> Generator.Element? {
return self[safe: index.advancedBy(1)]
}
}
/// Definitions are matched if they are registered for the same tag and thier factoris accept the same number of runtime arguments.
private func ~=(lhs: (DefinitionKey, _Definition), rhs: (DefinitionKey, _Definition)) -> Bool {
return
lhs.0.protocolType == rhs.0.protocolType &&
lhs.0.associatedTag == rhs.0.associatedTag &&
lhs.1.numberOfArguments == rhs.1.numberOfArguments
}
+44 -1
View File
@@ -41,6 +41,7 @@ public struct DefinitionKey : Hashable, CustomStringConvertible {
public var description: String {
return "type: \(protocolType), factory: \(factoryType), tag: \(associatedTag.desc)"
}
}
/// Check two definition keys on equality by comparing their `protocolType`, `factoryType` and `associatedTag` properties.
@@ -130,6 +131,11 @@ public enum ComponentScope {
```
*/
case Singleton
/**
The same scope as `Singleton`, but instance will be created when container is bootstrapped.
*/
case EagerSingleton
}
/**
@@ -191,6 +197,17 @@ public final class DefinitionOf<T, F>: Definition {
private var _resolvedInstance: T?
//Auto-wiring helpers
private(set) var autoWiringFactory: ((DependencyContainer, DependencyContainer.Tag?) throws -> T)?
private(set) var numberOfArguments: Int = 0
convenience init(scope: ComponentScope, factory: F, autoWiringFactory: (DependencyContainer, DependencyContainer.Tag?) throws -> T, numberOfArguments: Int) {
self.init(scope: scope, factory: factory)
self.autoWiringFactory = autoWiringFactory
self.numberOfArguments = numberOfArguments
}
}
///Dummy protocol to store definitions for different types in collection
@@ -198,9 +215,35 @@ public protocol Definition: class { }
protocol _Definition: Definition {
var scope: ComponentScope { get }
var _autoWiringFactory: ((DependencyContainer, DependencyContainer.Tag?) throws -> Any)? { get }
var _factory: Any { get }
var numberOfArguments: Int { get }
func resolveDependenciesOf(resolvedInstance: Any, withContainer container: DependencyContainer) throws
}
extension DefinitionOf: _Definition { }
extension _Definition {
func supportsAutoWiring() -> Bool {
return _autoWiringFactory != nil && numberOfArguments > 0
}
}
extension DefinitionOf: _Definition {
var _resolveDependenciesBlock: ((DependencyContainer, Any) throws -> ())? {
return resolveDependenciesBlock.map({ block in { try block($0, $1 as! T) } })
}
var _autoWiringFactory: ((DependencyContainer, DependencyContainer.Tag?) throws -> Any)? {
return autoWiringFactory.map({ factory in { try factory($0.0, $0.1)} })
}
var _factory: Any {
return factory
}
}
extension DefinitionOf: CustomStringConvertible {
public var description: String {
+184 -57
View File
@@ -22,8 +22,6 @@
// THE SOFTWARE.
//
// MARK: - DependencyContainer
/**
`DependencyContainer` allows you to do _Dependency Injection_
by associating abstractions to concrete implementations.
@@ -33,6 +31,8 @@ public final class DependencyContainer {
/**
Use a tag in case you need to register multiple factories fo the same type,
to differentiate them. Tags can be either String or Int, to your convenience.
- seealso: `DependencyTagConvertible`
*/
public enum Tag: Equatable {
case String(StringLiteralType)
@@ -43,6 +43,9 @@ public final class DependencyContainer {
let resolvedInstances = ResolvedInstances()
let lock = RecursiveLock()
private(set) var bootstrapped = false
private var bootstrapQueue: [() throws -> ()] = []
/**
Designated initializer for a DependencyContainer
@@ -58,6 +61,22 @@ public final class DependencyContainer {
configBlock(self)
}
/**
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.
- throws: `DipError` if failed to instantiate any component
*/
public func bootstrap() throws {
try threadSafe {
bootstrapped = true
try bootstrapQueue.forEach({ try $0() })
bootstrapQueue.removeAll()
}
}
private func threadSafe<T>(@noescape closure: () throws -> T) rethrows -> T {
lock.lock()
defer {
@@ -94,7 +113,9 @@ extension DependencyContainer {
```
*/
public func register<T>(tag tag: Tag? = nil, _ scope: ComponentScope = .Prototype, factory: () throws -> T) -> DefinitionOf<T, () throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory)
let definition = DefinitionOf<T, () throws -> T>(scope: scope, factory: factory)
register(definition, forTag: tag)
return definition
}
/**
@@ -106,25 +127,58 @@ extension DependencyContainer {
- factory: The factory to register.
- returns: A registered definition.
- note: You _should not_ call this method directly, instead call any of other `register` methods.
You _should_ use this method only to register dependency with more runtime arguments
than _Dip_ supports (currently it's up to six) like in the following example:
You _should_ use this method only to register dependency with more runtime arguments
than _Dip_ supports (currently it's up to six) like in the following example:
```swift
public func register<T, Arg1, Arg2, Arg3, ...>(tag: Tag? = nil, scope: ComponentScope = .Prototype, factory: (Arg1, Arg2, Arg3, ...) throws -> T) -> DefinitionOf<T, (Arg1, Arg2, Arg3, ...) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory) as DefinitionOf<T, (Arg1, Arg2, Arg3, ...) throws -> T>
return registerFactory(tag: tag, scope: scope, factory: factory)
}
```
Though before you do so you should probably review your design and try to reduce number of depnedencies.
*/
public func registerFactory<T, F>(tag tag: Tag? = nil, scope: ComponentScope, factory: F) -> DefinitionOf<T, F> {
@available(*, deprecated=4.3.0, message="Use registerFactory(tag:scope:factory:numberOfArguments:autoWiringFactory:) instead.")
public func registerFactory<T, F>(tag tag: DependencyTagConvertible? = nil, scope: ComponentScope, factory: F) -> DefinitionOf<T, F> {
let definition = DefinitionOf<T, F>(scope: scope, factory: factory)
register(definition, forTag: tag)
return definition
}
/**
Register generic factory and auto-wiring factory and associate it with an optional tag.
- parameters:
- tag: The arbitrary tag to associate this factory with. Pass `nil` to associate with any tag. Default value is `nil`.
- scope: The scope to use for instance created by the factory.
- factory: The factory to register.
- numberOfArguments: The number of factory arguments. Will be used on auto-wiring to sort definitions.
- autoWiringFactory: The factory to be used on auto-wiring to resolve component.
- returns: A registered definition.
- note: You _should not_ call this method directly, instead call any of other `register` methods.
You _should_ use this method only to register dependency with more runtime arguments
than _Dip_ supports (currently it's up to six) like in the following example:
```swift
public func register<T, Arg1, Arg2, Arg3, ...>(tag: Tag? = nil, scope: ComponentScope = .Prototype, factory: (Arg1, Arg2, Arg3, ...) throws -> T) -> DefinitionOf<T, (Arg1, Arg2, Arg3, ...) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory, numberOfArguments: ...) { container, tag in
try factory(try container.resolve(tag: tag), ...)
}
}
```
Though before you do so you should probably review your design and try to reduce number of depnedencies.
*/
public func registerFactory<T, F>(tag tag: DependencyTagConvertible? = nil, scope: ComponentScope, factory: F, numberOfArguments: Int, autoWiringFactory: (DependencyContainer, Tag?) throws -> T) -> DefinitionOf<T, F> {
let definition = DefinitionOf<T, F>(scope: scope, factory: factory, autoWiringFactory: autoWiringFactory, numberOfArguments: numberOfArguments)
register(definition, forTag: tag)
return definition
}
/**
Register definiton in the container and associate it with an optional tag.
Will override already registered definition for the same type and factory, associated with the same tag.
@@ -134,12 +188,18 @@ extension DependencyContainer {
- definition: The definition to register in the container.
*/
public func register<T, F>(definition: DefinitionOf<T, F>, forTag tag: Tag? = nil) {
let key = DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: tag)
public func register<T, F>(definition: DefinitionOf<T, F>, forTag tag: DependencyTagConvertible? = nil) {
let key = DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: tag?.dependencyTag)
register(definition, forKey: key)
if case .EagerSingleton = definition.scope {
bootstrapQueue.append({ let _ = try self.resolve(tag: tag) as T })
}
}
func register(definition: Definition, forKey key: DefinitionKey) {
func register(definition: _Definition, forKey key: DefinitionKey) {
precondition(!bootstrapped, "You can not modify container's definitions after it was bootstrapped.")
threadSafe {
definitions[key] = definition
resolvedInstances.singletons[key] = nil
@@ -160,10 +220,7 @@ extension DependencyContainer {
- parameter tag: The arbitrary tag to use to lookup definition.
- throws: An error of type `DipError`:
`ResolutionFailed` - if some error was thrown during resolution;
`DefinitionNotFound` - if no matching definition was registered in that container.
`AutoInjectionFailed` - if failed to auto-inject required property
- throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`
- returns: An instance of type `T`.
@@ -177,7 +234,7 @@ extension DependencyContainer {
```
*/
public func resolve<T>(tag tag: Tag? = nil) throws -> T {
public func resolve<T>(tag tag: DependencyTagConvertible? = nil) throws -> T {
return try resolve(tag: tag) { (factory: () throws -> T) in try factory() }
}
@@ -188,11 +245,8 @@ extension DependencyContainer {
- tag: The arbitrary tag to use to lookup definition.
- builder: Generic closure that accepts generic factory and returns inctance created by that factory.
- throws: An error of type `DipError`:
`ResolutionFailed` - if some error was thrown during resolution;
`DefinitionNotFound` - if no matching definition was registered in that container.
`AutoInjectionFailed` - if failed to auto-inject required property
- throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`
- returns: An instance of type `T`.
- note: You _should not_ call this method directly, instead call any of other
@@ -208,42 +262,54 @@ extension DependencyContainer {
Though before you do so you should probably review your design and try to reduce the number of dependencies.
*/
public func resolve<T, F>(tag tag: Tag? = nil, builder: F throws -> T) throws -> T {
let key = DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: tag)
public func resolve<T, F>(tag tag: DependencyTagConvertible? = nil, builder: F throws -> T) throws -> T {
let key = DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: tag?.dependencyTag)
do {
return try _resolveKey(key, builder: builder)
//first we try to find defintion that exactly matches parameters
return try _resolveKey(key, builder: { definition throws -> T in
guard let factory = definition._factory as? F else {
throw DipError.DefinitionNotFound(key: key)
}
return try builder(factory)
})
}
catch {
switch error {
case let DipError.DefinitionNotFound(errorKey) where key == errorKey:
throw error
//then if no definition found we try atuo-wiring
return try threadSafe {
guard let resolved: T = try _resolveByAutoWiring(key) else {
throw error
}
return resolved
}
default:
throw DipError.ResolutionFailed(key: key, underlyingError: error)
throw error
}
}
}
/// Lookup definition by the key and use it to resolve instance. Fallback to the key with `nil` tag.
func _resolveKey<T, F>(key: DefinitionKey, builder: F throws -> T) throws -> T {
return try threadSafe {
let nilTagKey = key.associatedTag.map { _ in DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: nil) }
guard let definition = (self.definitions[key] ?? self.definitions[nilTagKey]) as? DefinitionOf<T, F> else {
/// Lookup definition by the key and use it to resolve instance. Fallback to the key with `nil` tag.
func _resolveKey<T>(key: DefinitionKey, builder: _Definition throws -> T) throws -> T {
return try threadSafe {
let nilTagKey = key.associatedTag.map { _ in DefinitionKey(protocolType: T.self, factoryType: key.factoryType, associatedTag: nil) }
guard let definition = (self.definitions[key] ?? self.definitions[nilTagKey]) as? _Definition else {
throw DipError.DefinitionNotFound(key: key)
}
return try self._resolveDefinition(definition, key: key, builder: builder)
return try self._resolveDefinition(definition, usingKey: key, builder: builder)
}
}
/// Actually resolve dependency.
private func _resolveDefinition<T, F>(definition: DefinitionOf<T, F>, key: DefinitionKey, builder: F throws -> T) rethrows -> T {
private func _resolveDefinition<T>(definition: _Definition, usingKey key: DefinitionKey, builder: _Definition throws -> T) rethrows -> T {
return try resolvedInstances.resolve {
if let previouslyResolved: T = resolvedInstances.previouslyResolvedInstance(forKey: key, inScope: definition.scope) {
return previouslyResolved
}
else {
let resolvedInstance = try builder(definition.factory)
let resolvedInstance = try builder(definition)
//when builder calls factory it will in turn resolve sub-dependencies (if there are any)
//when it returns instance that we try to resolve here can be already resolved
@@ -255,9 +321,6 @@ extension DependencyContainer {
resolvedInstances.storeResolvedInstance(resolvedInstance, forKey: key, inScope: definition.scope)
try definition.resolveDependenciesOf(resolvedInstance, withContainer: self)
//we perform auto-injection as the last step to be able to reuse instances
//stored when manually resolving dependencies in resolveDependencies block
try autoInjectProperties(resolvedInstance)
return resolvedInstance
@@ -270,18 +333,23 @@ extension DependencyContainer {
class ResolvedInstances {
var resolvedInstances = [DefinitionKey: Any]()
var singletons = [DefinitionKey: Any]()
var resolvableInstances = [Resolvable]()
func storeResolvedInstance<T>(instance: T, forKey key: DefinitionKey, inScope scope: ComponentScope) {
switch scope {
case .Singleton: singletons[key] = instance
case .Singleton, .EagerSingleton: singletons[key] = instance
case .ObjectGraph: resolvedInstances[key] = instance
case .Prototype: break
}
if let resolvable = instance as? Resolvable {
resolvableInstances.append(resolvable)
}
}
func previouslyResolvedInstance<T>(forKey key: DefinitionKey, inScope scope: ComponentScope) -> T? {
switch scope {
case .Singleton: return singletons[key] as? T
case .Singleton, .EagerSingleton: return singletons[key] as? T
case .ObjectGraph: return resolvedInstances[key] as? T
case .Prototype: return nil
}
@@ -295,7 +363,13 @@ extension DependencyContainer {
defer {
depth = depth - 1
if depth == 0 {
// We call didResolveDependencies only at this point
// because this is a point when dependencies graph is complete.
for resolvedInstance in resolvableInstances.reverse() {
resolvedInstance.didResolveDependencies()
}
resolvedInstances.removeAll()
resolvableInstances.removeAll()
}
}
@@ -317,12 +391,14 @@ extension DependencyContainer {
- tag: The tag used to register definition.
- definition: The definition to remove
*/
public func remove<T, F>(definition: DefinitionOf<T, F>, forTag tag: Tag? = nil) {
let key = DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: tag)
public func remove<T, F>(definition: DefinitionOf<T, F>, forTag tag: DependencyTagConvertible? = nil) {
let key = DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: tag?.dependencyTag)
remove(definitionForKey: key)
}
func remove(definitionForKey key: DefinitionKey) {
precondition(!bootstrapped, "You can not modify container's definitions after it was bootstrapped.")
threadSafe {
definitions[key] = nil
resolvedInstances.singletons[key] = nil
@@ -336,6 +412,7 @@ extension DependencyContainer {
threadSafe {
definitions.removeAll()
resolvedInstances.singletons.removeAll()
bootstrapped = false
}
}
@@ -349,16 +426,54 @@ extension DependencyContainer: CustomStringConvertible {
}
extension DependencyContainer.Tag: IntegerLiteralConvertible {
//MARK: - Resolvable
public init(integerLiteral value: IntegerLiteralType) {
self = .Int(value)
/// Conform to this protocol when you need to have a callback when all the dependencies are injected.
public protocol Resolvable {
/// This method is called by the container when all dependencies of the instance are resolved.
func didResolveDependencies()
}
//MARK: - DependencyTagConvertible
/// Implement this protocol of 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.
public protocol DependencyTagConvertible {
var dependencyTag: DependencyContainer.Tag { get }
}
extension DependencyContainer.Tag: DependencyTagConvertible {
public var dependencyTag: DependencyContainer.Tag {
return self
}
}
extension String: DependencyTagConvertible {
public var dependencyTag: DependencyContainer.Tag {
return .String(self)
}
}
extension Int: DependencyTagConvertible {
public var dependencyTag: DependencyContainer.Tag {
return .Int(self)
}
}
extension DependencyTagConvertible where Self: RawRepresentable, Self.RawValue == Int {
public var dependencyTag: DependencyContainer.Tag {
return .Int(rawValue)
}
}
extension DependencyTagConvertible where Self: RawRepresentable, Self.RawValue == String {
public var dependencyTag: DependencyContainer.Tag {
return .String(rawValue)
}
}
extension DependencyContainer.Tag: StringLiteralConvertible {
public init(stringLiteral value: StringLiteralType) {
self = .String(value)
}
@@ -373,6 +488,14 @@ extension DependencyContainer.Tag: StringLiteralConvertible {
}
extension DependencyContainer.Tag: IntegerLiteralConvertible {
public init(integerLiteral value: IntegerLiteralType) {
self = .Int(value)
}
}
public func ==(lhs: DependencyContainer.Tag, rhs: DependencyContainer.Tag) -> Bool {
switch (lhs, rhs) {
case let (.String(lhsString), .String(rhsString)):
@@ -384,20 +507,14 @@ public func ==(lhs: DependencyContainer.Tag, rhs: DependencyContainer.Tag) -> Bo
}
}
//MARK: - DipError
/**
Errors thrown by `DependencyContainer`'s methods.
- seealso: `resolve(tag:)`
*/
public enum DipError: ErrorType, CustomStringConvertible {
/**
Thrown by `resolve(tag:)` if some error was thrown during resolution.
- parameters:
- key: The key, which is associated with definition used to resolve instance
- underlyingError: The error that caused resolution to fail
*/
case ResolutionFailed(key: DefinitionKey, underlyingError: ErrorType)
/**
Thrown by `resolve(tag:)` if no matching definition was registered in container.
@@ -415,15 +532,25 @@ public enum DipError: ErrorType, CustomStringConvertible {
- underlyingError: The error that caused auto-injection to fail
*/
case AutoInjectionFailed(label: String?, type: Any.Type, underlyingError: ErrorType)
/**
Thrown by `resolve(tag:)` if found ambigous definitions registered for resolved type
- parameters:
- type: The type that failed to be resolved
- definitions: Ambiguous definitions
*/
case AmbiguousDefinitions(type: Any.Type, definitions: [Definition])
public var description: String {
switch self {
case let .ResolutionFailed(key, error):
return "Failed to resolve type \(key.protocolType). \(error)"
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.protocolType)."
case let .AutoInjectionFailed(label, type, error):
return "Failed to auto-inject property \"\(label.desc)\" of type \(type). \(error)"
case let .AmbiguousDefinitions(type, definitions):
return "Ambiguous definitions for \(type):\n" +
definitions.map({ "\($0)" }).joinWithSeparator(";\n")
}
}
}
+22 -21
View File
@@ -42,87 +42,88 @@ extension DependencyContainer {
- seealso: `registerFactory(tag:scope:factory:)`
*/
public func register<T, Arg1>(tag tag: Tag? = nil, _ scope: ComponentScope = .Prototype, factory: (Arg1) throws -> T) -> DefinitionOf<T, (Arg1) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory)
public func register<T, Arg1>(tag tag: DependencyTagConvertible? = nil, _ scope: ComponentScope = .Prototype, factory: (Arg1) throws -> T) -> DefinitionOf<T, (Arg1) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory, numberOfArguments: 1) { container, tag in try factory(try container.resolve(tag: tag)) }
}
/**
Resolve a dependency using one runtime argument.
- note: When resolving type container will first try to use definition that matches types of arguments that you pass to resolve method. If it fails or no such definition is found container will try to _auto-wire_ component. For that it will iterate through all the definitions registered for that type which factories accept any number of runtime arguments and are tagged with the same tag, passed to `resolve` method, or with no tag. Container will try to use these definitions to resolve a component one by one until one of them succeeds, starting with tagged definitions in order of decreasing their's factories number of arguments.
- parameters:
- tag: The arbitrary tag to lookup registered definition.
- arg1: The first argument to pass to the definition's factory.
- throws: An error of type `DipError`:
`ResolutionFailed` - if some error was thrown during resolution;
`DefinitionNotFound` - if no matching definition was registered in that container.
- throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`
- returns: An instance of type `T`.
- seealso: `register(tag:_:factory:)`, `resolve(tag:builder:)`
*/
public func resolve<T, Arg1>(tag tag: Tag? = nil, withArguments arg1: Arg1) throws -> T {
public func resolve<T, Arg1>(tag tag: DependencyTagConvertible? = nil, withArguments arg1: Arg1) throws -> T {
return try resolve(tag: tag) { (factory: (Arg1) throws -> T) in try factory(arg1) }
}
// MARK: 2 Runtime Arguments
/// - seealso: `register(tag:scope:factory:)`
public func register<T, Arg1, Arg2>(tag tag: Tag? = nil, _ scope: ComponentScope = .Prototype, factory: (Arg1, Arg2) throws -> T) -> DefinitionOf<T, (Arg1, Arg2) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory)
public func register<T, Arg1, Arg2>(tag tag: DependencyTagConvertible? = nil, _ scope: ComponentScope = .Prototype, factory: (Arg1, Arg2) throws -> T) -> DefinitionOf<T, (Arg1, Arg2) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory, numberOfArguments: 2) { container, tag in try factory(try container.resolve(tag: tag), try container.resolve(tag: tag)) }
}
/// - seealso: `resolve(tag:_:)`
public func resolve<T, Arg1, Arg2>(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2) throws -> T {
public func resolve<T, Arg1, Arg2>(tag tag: DependencyTagConvertible? = nil, withArguments arg1: Arg1, _ arg2: Arg2) throws -> T {
return try resolve(tag: tag) { (factory: (Arg1, Arg2) throws -> T) in try factory(arg1, arg2) }
}
// MARK: 3 Runtime Arguments
/// - seealso: `register(tag:scope:factory:)`
public func register<T, Arg1, Arg2, Arg3>(tag tag: Tag? = nil, _ scope: ComponentScope = .Prototype, factory: (Arg1, Arg2, Arg3) throws -> T) -> DefinitionOf<T, (Arg1, Arg2, Arg3) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory)
public func register<T, Arg1, Arg2, Arg3>(tag tag: DependencyTagConvertible? = nil, _ scope: ComponentScope = .Prototype, factory: (Arg1, Arg2, Arg3) throws -> T) -> DefinitionOf<T, (Arg1, Arg2, Arg3) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory, numberOfArguments: 3) { container, tag in try factory(try container.resolve(tag: tag), try container.resolve(), try container.resolve(tag: tag)) }
}
/// - seealso: `resolve(tag:withArguments:)`
public func resolve<T, Arg1, Arg2, Arg3>(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3) throws -> T {
public func resolve<T, Arg1, Arg2, Arg3>(tag tag: DependencyTagConvertible? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3) throws -> T {
return try resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3) throws -> T) in try factory(arg1, arg2, arg3) }
}
// MARK: 4 Runtime Arguments
/// - seealso: `register(tag:scope:factory:)`
public func register<T, Arg1, Arg2, Arg3, Arg4>(tag tag: Tag? = nil, _ scope: ComponentScope = .Prototype, factory: (Arg1, Arg2, Arg3, Arg4) throws -> T) -> DefinitionOf<T, (Arg1, Arg2, Arg3, Arg4) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory)
public func register<T, Arg1, Arg2, Arg3, Arg4>(tag tag: DependencyTagConvertible? = nil, _ scope: ComponentScope = .Prototype, factory: (Arg1, Arg2, Arg3, Arg4) throws -> T) -> DefinitionOf<T, (Arg1, Arg2, Arg3, Arg4) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory, numberOfArguments: 4) { container, tag in try factory(try container.resolve(tag: tag), try container.resolve(tag: tag), try container.resolve(tag: tag), try container.resolve(tag: tag)) }
}
/// - seealso: `resolve(tag:withArguments:)`
public func resolve<T, Arg1, Arg2, Arg3, Arg4>(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4) throws -> T {
public func resolve<T, Arg1, Arg2, Arg3, Arg4>(tag tag: DependencyTagConvertible? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4) throws -> T {
return try resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, Arg4) throws -> T) in try factory(arg1, arg2, arg3, arg4) }
}
// MARK: 5 Runtime Arguments
/// - seealso: `register(tag:scope:factory:)`
public func register<T, Arg1, Arg2, Arg3, Arg4, Arg5>(tag tag: Tag? = nil, _ scope: ComponentScope = .Prototype, factory: (Arg1, Arg2, Arg3, Arg4, Arg5) throws -> T) -> DefinitionOf<T, (Arg1, Arg2, Arg3, Arg4, Arg5) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory)
public func register<T, Arg1, Arg2, Arg3, Arg4, Arg5>(tag tag: DependencyTagConvertible? = nil, _ scope: ComponentScope = .Prototype, factory: (Arg1, Arg2, Arg3, Arg4, Arg5) throws -> T) -> DefinitionOf<T, (Arg1, Arg2, Arg3, Arg4, Arg5) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory, numberOfArguments: 5) { container, tag in try factory(try container.resolve(tag: tag), try container.resolve(tag: tag), try container.resolve(tag: tag), try container.resolve(tag: tag), try container.resolve(tag: tag)) }
}
/// - seealso: `resolve(tag:withArguments:)`
public func resolve<T, Arg1, Arg2, Arg3, Arg4, Arg5>(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4, _ arg5: Arg5) throws -> T {
public func resolve<T, Arg1, Arg2, Arg3, Arg4, Arg5>(tag tag: DependencyTagConvertible? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4, _ arg5: Arg5) throws -> T {
return try resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, Arg4, Arg5) throws -> T) in try factory(arg1, arg2, arg3, arg4, arg5) }
}
// MARK: 6 Runtime Arguments
/// - seealso: `register(tag:scope:factory:)`
public func register<T, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6>(tag tag: Tag? = nil, _ scope: ComponentScope = .Prototype, factory: (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) throws -> T) -> DefinitionOf<T, (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory)
public func register<T, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6>(tag tag: DependencyTagConvertible? = nil, _ scope: ComponentScope = .Prototype, factory: (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) throws -> T) -> DefinitionOf<T, (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) throws -> T> {
return registerFactory(tag: tag, scope: scope, factory: factory, numberOfArguments: 6) { container, tag in try factory(try container.resolve(tag: tag), try container.resolve(tag: tag), try container.resolve(tag: tag), try container.resolve(tag: tag), try container.resolve(tag: tag), try container.resolve(tag: tag)) }
}
/// - seealso: `resolve(tag:withArguments:)`
public func resolve<T, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6>(tag tag: Tag? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4, _ arg5: Arg5, _ arg6: Arg6) throws -> T {
public func resolve<T, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6>(tag tag: DependencyTagConvertible? = nil, withArguments arg1: Arg1, _ arg2: Arg2, _ arg3: Arg3, _ arg4: Arg4, _ arg5: Arg5, _ arg6: Arg6) throws -> T {
return try resolve(tag: tag) { (factory: (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) throws -> T) in try factory(arg1, arg2, arg3, arg4, arg5, arg6) }
}
}