Compare commits

...

22 Commits

Author SHA1 Message Date
Ilya Puchka c3b601df0f Merge pull request #235 from AliSoftware/develop
Release 7.1.1
2019-12-27 11:36:42 +00:00
Ilya Puchka 56b0768d60 bump version to 7.1.1 2019-12-27 11:14:55 +00:00
Ilya Puchka 8cf87efb95 Update podspec author 2019-12-27 11:07:21 +00:00
Ilya Puchka 3aa9b334f1 Merge pull request #233 from AliSoftware/spm-fix
Enabled StoryboardInstantiatable for SPM builds
2019-11-15 11:53:25 +00:00
Ilya Puchka b2a0cda242 Update StoryboardInstantiatable.swift 2019-11-13 13:16:52 +00:00
Ilya Puchka 07eb66e87d Merge pull request #231 from AliSoftware/develop
Release 7.1.0
2019-11-11 14:41:08 +00:00
Ilya Puchka 82e3d02497 Merge branch 'master' into develop 2019-11-11 14:22:24 +00:00
Ilya Puchka 3b2f5a272f bumpe version to 7.1.0 2019-11-10 16:11:17 +00:00
Ilya Puchka eab2d424e7 allow to disable thread safety on container level 2019-11-10 16:01:20 +00:00
Ilya Puchka 0f2cda0a52 update changelog 2019-11-10 15:53:45 +00:00
Ilya Puchka 05c02c9645 Merge pull request #225 from AliSoftware/property-delegates
Property wrappers
2019-11-10 15:52:49 +00:00
Ilya Puchka fa086dad8f add xcode 11 and swift 5.1 to travis build 2019-11-10 15:35:53 +00:00
Ilya Puchka cdc09f5a03 removed unneeded initial nil values 2019-11-10 15:22:38 +00:00
Ilya Puchka b94ed3bea2 fix warning 2019-11-10 15:22:16 +00:00
Ilya Puchka 0fd70e65dd Merge branch 'develop' into property-delegates 2019-11-10 15:13:55 +00:00
Ilya Puchka 97e7f4782f Merge branch 'pr/221' into develop 2019-11-10 15:12:41 +00:00
Ilya Puchka be0c39ab6b update for beta 3 2019-07-07 23:54:58 +01:00
Ilya Puchka 0de45e7d53 fixed checking for new injected value 2019-07-07 23:53:45 +01:00
Ilya Puchka 3b421d0cff refactor property wrappers to struct 2019-07-07 16:16:50 +01:00
Ilya Puchka f1234bf2b9 introduce swift 5.1 property delegates 2019-06-30 19:12:42 +01:00
Ilya Puchka b0d7153bfe ignore swiftpm files 2019-06-30 18:27:59 +01:00
Ilya Puchka b9bf9c4d75 Merge pull request #216 from AliSoftware/develop
Release 7.0.1
2018-12-19 22:54:14 +00:00
14 changed files with 361 additions and 100 deletions
+1
View File
@@ -36,3 +36,4 @@ Carthage
# SPM
.build/
Packages
.swiftpm
-1
View File
@@ -1 +0,0 @@
5.0.1
+26 -1
View File
@@ -12,7 +12,32 @@ matrix:
osx_image: xcode10.2
language: objective-c
before_install:
- gem install cocoapods --version 1.4.0 --no-document
- gem install cocoapods --version 1.8.4 --no-document
- script:
- set -o pipefail && xcodebuild test -workspace Dip.xcworkspace -scheme Dip -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 11,OS=latest' ONLY_ACTIVE_ARCH=NO | xcpretty -c
- set -o pipefail && xcodebuild test -workspace Dip.xcworkspace -scheme Dip -sdk macosx -destination 'platform=macOS,arch=x86_64' ONLY_ACTIVE_ARCH=NO | xcpretty -c
# - pod spec lint --allow-warnings
- carthage build --no-skip-current
- swift package clean && swift build && swift test
os: osx
osx_image: xcode11.2
language: objective-c
before_install:
- gem install cocoapods --version 1.8.4 --no-document
- script:
- swift package clean && swift build && swift test
os: linux
dist: trusty
sudo: required
language: generic
before_install:
- wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import -
- cd ..
- export SWIFT_VERSION=swift-5.1-RELEASE
- wget https://swift.org/builds/swift-5.1-release/ubuntu1404/$SWIFT_VERSION/$SWIFT_VERSION-ubuntu14.04.tar.gz
- tar xzf $SWIFT_VERSION-ubuntu14.04.tar.gz
- export PATH="${PWD}/${SWIFT_VERSION}-ubuntu14.04/usr/bin:${PATH}"
- cd Dip
- script:
- swift package clean && swift build && swift test
os: linux
+11 -1
View File
@@ -2,7 +2,17 @@
## Develop
* You can now use a shorthand syntax for resolving a single property using a key path, i.e. `resolvingProperty(\.value)`. This allows to inject properties without making them publicly writable (they still should be writable, but `private(set)` is good enough) and without manually casting resolved instance to its concrete type to set the property (this will be still done under the hood).
## 7.1.1
* Fixed using `StoryboardInstantiatable` with SPM ([#233](https://github.com/AliSoftware/Dip/pull/233)).
## 7.1.0
* You can now use a shorthand syntax for resolving a single property using a key path, i.e. `resolvingProperty(\.value)`.
* Swift 5.0 support ([#224](https://github.com/AliSoftware/Dip/pull/224)).
* Fixed resolving nested types with the same local names ([#221](https://github.com/AliSoftware/Dip/pull/221)).
* `@Injected` and `@IntectedWeak` property wrappers ([#225](https://github.com/AliSoftware/Dip/pull/225)).
* Thread safety can be disabled on container level.
## 7.0.1
+4 -2
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Dip"
s.version = "7.0.1"
s.version = "7.1.1"
s.summary = "Dependency Injection for Swift made easy."
s.description = <<-DESC
@@ -11,7 +11,7 @@ Pod::Spec.new do |s|
s.homepage = "https://github.com/AliSoftware/Dip"
s.license = 'MIT'
s.authors = { "Olivier Halligon" => "olivier@halligon.net", "Ilya Puchka" => "ilya@puchka.me" }
s.authors = { "Olivier Halligon" => "olivier@halligon.net", "Ilya Puchka" => "ilyapuchka@gmail.com" }
s.source = { :git => "https://github.com/AliSoftware/Dip.git", :tag => s.version.to_s }
s.social_media_url = 'https://twitter.com/aligatr'
@@ -23,4 +23,6 @@ Pod::Spec.new do |s|
s.requires_arc = true
s.source_files = 'Sources/**/*.swift'
s.swift_version = "5.0", "5.1"
end
+1 -1
View File
@@ -9,7 +9,7 @@
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>7.0.1</string>
<string>7.1.1</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
+1 -1
View File
@@ -9,7 +9,7 @@
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>7.0.1</string>
<string>7.1.1</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
+1 -1
View File
@@ -9,7 +9,7 @@
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>7.0.1</string>
<string>7.1.1</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
+1 -1
View File
@@ -9,7 +9,7 @@
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>7.0.1</string>
<string>7.1.1</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
+203 -77
View File
@@ -78,7 +78,7 @@ extension DependencyContainer {
```
*/
public protocol AutoInjectedPropertyBox: class {
public protocol AutoInjectedPropertyBox {
///The type of wrapped property.
static var wrappedType: Any.Type { get }
@@ -93,40 +93,108 @@ public protocol AutoInjectedPropertyBox: class {
func resolve(_ container: DependencyContainer) throws
}
#if swift(>=5.1)
/**
Use this wrapper to identify _strong_ properties of the instance that should be
auto-injected by `DependencyContainer`. Type T can be any type.
- warning: Do not define this property as optional or container will not be able to inject it.
Instead define it with initial value of `Injected<T>()`.
**Example**:
```swift
class ClientImp: Client {
@Injected var service: Service?
}
```
- seealso: `InjectedWeak`
*/
@propertyWrapper
public struct Injected<T>: _InjectedPropertyBox, AutoInjectedPropertyBox {
let valueBox: NullableBox<T> = NullableBox(nil)
///Wrapped value.
public var wrappedValue: T? {
get {
return valueBox.unboxed
}
set {
guard (required && newValue != nil) || !required else {
fatalError("Can not set required property to nil.")
}
valueBox.unboxed = newValue
}
}
let required: Bool
let didInject: (T) -> ()
let tag: DependencyContainer.Tag?
let overrideTag: Bool
public init(wrappedValue initialValue: T?) {
self.init()
}
}
#else
/**
Use this wrapper to identify _strong_ properties of the instance that should be
auto-injected by `DependencyContainer`. Type T can be any type.
- warning: Do not define this property as optional or container will not be able to inject it.
Instead define it with initial value of `Injected<T>()`.
**Example**:
```swift
class ClientImp: Client {
var service = Injected<Service>()
}
```
- seealso: `InjectedWeak`
*/
public final class Injected<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox {
*/
public struct Injected<T>: _InjectedPropertyBox, AutoInjectedPropertyBox {
let valueBox: NullableBox<T> = NullableBox(nil)
///The type of wrapped property.
public static var wrappedType: Any.Type {
return T.self
///Wrapped value.
public var value: T? {
return valueBox.unboxed
}
///Wrapped value.
public fileprivate(set) var value: T? {
didSet {
if let value = value { didInject(value) }
let required: Bool
let didInject: (T) -> ()
let tag: DependencyContainer.Tag?
let overrideTag: Bool
/// Returns a new wrapper with provided value.
func setValue(_ value: T?) -> Injected {
guard (required && value != nil) || !required else {
fatalError("Can not set required property to nil.")
}
return Injected(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
}
}
#endif
public extension Injected {
///The type of wrapped property.
static var wrappedType: Any.Type {
return T.self
}
init(value: T?, required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> ()) {
self.value = value
super.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
self.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
self.valueBox.unboxed = value
}
init(required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> () = { _ in }) {
self.required = required
self.tag = tag?.dependencyTag
self.overrideTag = overrideTag
self.didInject = didInject
}
/**
@@ -140,30 +208,24 @@ public final class Injected<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox
- didInject: Block that will be called when concrete instance is injected in this property.
Similar to `didSet` property observer. Default value does nothing.
*/
public convenience init(required: Bool = true, didInject: @escaping (T) -> () = { _ in }) {
init(required: Bool = true, didInject: @escaping (T) -> () = { _ in }) {
self.init(value: nil, required: required, tag: nil, overrideTag: false, didInject: didInject)
}
public convenience init(required: Bool = true, tag: DependencyTagConvertible?, didInject: @escaping (T) -> () = { _ in }) {
init(required: Bool = true, tag: DependencyTagConvertible?, didInject: @escaping (T) -> () = { _ in }) {
self.init(value: nil, required: required, tag: tag, overrideTag: true, didInject: didInject)
}
public func resolve(_ container: DependencyContainer) throws {
let resolved: T? = try super.resolve(with: container)
value = resolved
}
/// Returns a new wrapper with provided value.
public func setValue(_ value: T?) -> Injected {
guard (required && value != nil) || !required else {
fatalError("Can not set required property to nil.")
func resolve(_ container: DependencyContainer) throws {
let resolved: T? = try self.resolve(with: container, tag: tag, overrideTag: overrideTag, required: required)
valueBox.unboxed = resolved
if let resolved = resolved {
didInject(resolved)
}
return Injected(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
}
}
#if swift(>=5.1)
/**
Use this wrapper to identify _weak_ properties of the instance that should be
auto-injected by `DependencyContainer`. Type T should be a **class** type.
@@ -185,42 +247,124 @@ public final class Injected<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox
```swift
class ServiceImp: Service {
var client = InjectedWeak<Client>()
@InjectedWeak var client: Client?
}
```
- seealso: `Injected`
*/
public final class InjectedWeak<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox {
@propertyWrapper
public struct InjectedWeak<T>: _InjectedPropertyBox, AutoInjectedPropertyBox {
//Only classes (means AnyObject) can be used as `weak` properties
//but we can not make <T: AnyObject> because that will prevent using protocol as generic type
//so we just rely on user reading documentation and passing AnyObject in runtime
//also we will throw fatal error if type can not be casted to AnyObject during resolution.
///The type of wrapped property.
public static var wrappedType: Any.Type {
return T.self
}
var valueBox: WeakBox<T>? = nil {
didSet {
if let value = value { didInject(value) }
let valueBox: WeakBox<T> = WeakBox(nil)
///Wrapped value.
public var wrappedValue: T? {
get {
return valueBox.value
}
set {
guard (required && newValue != nil) || !required else {
fatalError("Can not set required property to nil.")
}
valueBox.unboxed = newValue as AnyObject
}
}
let required: Bool
let didInject: (T) -> ()
let tag: DependencyContainer.Tag?
let overrideTag: Bool
public init(wrappedValue initialValue: T?) {
self.init()
}
}
#else
/**
Use this wrapper to identify _weak_ properties of the instance that should be
auto-injected by `DependencyContainer`. Type T should be a **class** type.
Otherwise it will cause runtime exception when container will try to resolve the property.
Use this wrapper to define one of two circular dependencies to avoid retain cycle.
- note: The only difference between `InjectedWeak` and `Injected` is that `InjectedWeak` uses
_weak_ reference to store underlying value, when `Injected` uses _strong_ reference.
For that reason if you resolve instance that has a _weak_ auto-injected property this property
will be released when `resolve` will complete.
Use `InjectedWeak<T>` to define one of two circular dependecies if another dependency is defined as `Injected<U>`.
This will prevent a retain cycle between resolved instances.
- warning: Do not define this property as optional or container will not be able to inject it.
Instead define it with initial value of `InjectedWeak<T>()`.
**Example**:
```swift
class ServiceImp: Service {
var client = InjectedWeak<Client>()
}
```
- seealso: `Injected`
*/
public struct InjectedWeak<T>: _InjectedPropertyBox, AutoInjectedPropertyBox {
//Only classes (means AnyObject) can be used as `weak` properties
//but we can not make <T: AnyObject> because that will prevent using protocol as generic type
//so we just rely on user reading documentation and passing AnyObject in runtime
//also we will throw fatal error if type can not be casted to AnyObject during resolution.
let valueBox: WeakBox<T> = WeakBox(nil)
///Wrapped value.
public var value: T? {
return valueBox?.value
return valueBox.value
}
init(value: T?, required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> ()) {
self.valueBox = value.map(WeakBox.init)
super.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
let required: Bool
let didInject: (T) -> ()
let tag: DependencyContainer.Tag?
let overrideTag: Bool
/// Returns a new wrapper with provided value.
func setValue(_ value: T?) -> InjectedWeak {
guard (required && value != nil) || !required else {
fatalError("Can not set required property to nil.")
}
return InjectedWeak(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
}
}
#endif
public extension InjectedWeak {
///The type of wrapped property.
static var wrappedType: Any.Type {
return T.self
}
init(value: T?, required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> ()) {
self.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
self.valueBox.unboxed = value as AnyObject
}
init(required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> () = { _ in }) {
self.required = required
self.tag = tag?.dependencyTag
self.overrideTag = overrideTag
self.didInject = didInject
}
/**
Creates a new wrapper for weak auto-injected property.
@@ -232,46 +376,28 @@ public final class InjectedWeak<T>: _InjectedPropertyBox<T>, AutoInjectedPropert
- didInject: Block that will be called when concrete instance is injected in this property.
Similar to `didSet` property observer. Default value does nothing.
*/
public convenience init(required: Bool = true, didInject: @escaping (T) -> () = { _ in }) {
init(required: Bool = true, didInject: @escaping (T) -> () = { _ in }) {
self.init(value: nil, required: required, tag: nil, overrideTag: false, didInject: didInject)
}
public convenience init(required: Bool = true, tag: DependencyTagConvertible?, didInject: @escaping (T) -> () = { _ in }) {
init(required: Bool = true, tag: DependencyTagConvertible?, didInject: @escaping (T) -> () = { _ in }) {
self.init(value: nil, required: required, tag: tag, overrideTag: true, didInject: didInject)
}
public func resolve(_ container: DependencyContainer) throws {
let resolved: T? = try super.resolve(with: container)
valueBox = resolved.map(WeakBox.init)
}
/// Returns a new wrapper with provided value.
public func setValue(_ value: T?) -> InjectedWeak {
guard (required && value != nil) || !required else {
fatalError("Can not set required property to nil.")
func resolve(_ container: DependencyContainer) throws {
let resolved: T? = try self.resolve(with: container, tag: tag, overrideTag: overrideTag, required: required)
valueBox.unboxed = resolved as AnyObject
if let resolved = resolved {
didInject(resolved)
}
return InjectedWeak(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
}
}
public class _InjectedPropertyBox<T> {
protocol _InjectedPropertyBox {}
let required: Bool
let didInject: (T) -> ()
let tag: DependencyContainer.Tag?
let overrideTag: Bool
init(required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> () = { _ in }) {
self.required = required
self.tag = tag?.dependencyTag
self.overrideTag = overrideTag
self.didInject = didInject
}
func resolve(with container: DependencyContainer) throws -> T? {
let tag = overrideTag ? self.tag : container.context.tag
extension _InjectedPropertyBox {
func resolve<T>(with container: DependencyContainer, tag: DependencyContainer.Tag?, overrideTag: Bool, required: Bool) throws -> T? {
let tag = overrideTag ? tag : container.context.tag
do {
container.context.key = container.context.key.tagged(with: tag)
let key = DefinitionKey(type: T.self, typeOfArguments: Void.self, tag: tag?.dependencyTag)
@@ -289,7 +415,7 @@ public class _InjectedPropertyBox<T> {
}
}
private func resolve<U>(with container: DependencyContainer, key: DefinitionKey, builder: ((U) throws -> Any) throws -> Any) throws -> Any {
func resolve<U>(with container: DependencyContainer, key: DefinitionKey, builder: ((U) throws -> Any) throws -> Any) throws -> Any {
return try container._resolve(key: key, builder: { definition throws -> Any in
try builder(definition.weakFactory)
})
+8 -1
View File
@@ -40,6 +40,7 @@ public final class DependencyContainer {
}
var autoInjectProperties: Bool
var threadSafe: Bool
internal(set) public var context: Context!
var definitions = [DefinitionKey: _Definition]()
var resolvedInstances = ResolvedInstances()
@@ -63,6 +64,7 @@ public final class DependencyContainer {
- Parameters:
- autoInjectProperties: Whether container should perform properties auto-injection. Default is `true`.
- threadSafe: Whether container should be thread-safe. Default is `true`. You may want to disable it for better performance.
- configBlock: A configuration block in which you typically put all you `register` calls.
- note: The `configBlock` is simply called at the end of the `init` to let you configure everything.
@@ -82,8 +84,9 @@ public final class DependencyContainer {
- returns: A new DependencyContainer.
*/
public init(autoInjectProperties: Bool = true, configBlock: (DependencyContainer)->() = { _ in }) {
public init(autoInjectProperties: Bool = true, threadSafe: Bool = true, configBlock: (DependencyContainer)->() = { _ in }) {
self.autoInjectProperties = autoInjectProperties
self.threadSafe = threadSafe
configBlock(self)
}
@@ -104,6 +107,10 @@ public final class DependencyContainer {
}
func threadSafe<T>(_ closure: () throws -> T) rethrows -> T {
guard threadSafe else {
return try closure()
}
lock.lock()
defer { lock.unlock() }
return try closure()
+1 -1
View File
@@ -22,7 +22,7 @@
// THE SOFTWARE.
//
#if (canImport(UIKit) || canImport(AppKit) || canImport(WatchKit)) && !SWIFT_PACKAGE
#if (canImport(UIKit) || canImport(AppKit) || canImport(WatchKit))
extension DependencyContainer {
///Containers that will be used to resolve dependencies of instances, created by stroyboards.
+9 -9
View File
@@ -56,6 +56,13 @@ class Box<T> {
}
}
class NullableBox<T> {
var unboxed: T?
init(_ value: T?) {
self.unboxed = value
}
}
protocol WeakBoxType {
var unboxed: AnyObject? { get }
}
@@ -66,15 +73,8 @@ class WeakBox<T>: WeakBoxType {
return unboxed as? T
}
init(_ value: T) {
#if _runtime(_ObjC)
weak var value: AnyObject? = value as AnyObject
#else
weak var value: AnyObject? = value as? AnyObject
#endif
guard value != nil else {
fatalError("Can not store weak reference to not a class instance (\(T.self))")
}
init(_ value: T?) {
weak var value: AnyObject? = value as AnyObject
self.unboxed = value
}
}
+94 -3
View File
@@ -35,6 +35,34 @@ private protocol Client: class {
var anotherServer: Server! {get set}
}
#if swift(>=5.1)
private class ServerImp: Server {
@InjectedWeak(didInject: { _ in
AutoInjectionTests.clientDidInjectCalled = true
}) var client: Client!
@InjectedWeak(required: false) var _optionalProperty: AnyObject?
weak var anotherClient: Client!
}
private class ClientImp: Client {
@Injected(didInject: { _ in
AutoInjectionTests.serverDidInjectCalled = true
}) var server: Server
@Injected(required: false) var _optionalProperty: AnyObject?
@Injected(tag: "tagged") var taggedServer: Server
@Injected(tag: nil) var nilTaggedServer: Server
var anotherServer: Server!
}
#else
private class ServerImp: Server {
var _client = InjectedWeak<Client>() { _ in
@@ -67,7 +95,18 @@ private class ClientImp: Client {
var taggedServer = Injected<Server>(tag: "tagged")
var nilTaggedServer = Injected<Server>(tag: nil)
}
#endif
#if swift(>=5.1)
private class Obj1 {
@InjectedWeak var obj2: Obj2?
@Injected var obj3: Obj3?
}
private class Obj2 {
@Injected var obj1: Obj1?
}
#else
private class Obj1 {
let obj2 = InjectedWeak<Obj2>()
let obj3 = Injected<Obj3>()
@@ -76,6 +115,7 @@ private class Obj1 {
private class Obj2 {
let obj1 = Injected<Obj1>()
}
#endif
private class Obj3 {
@@ -108,10 +148,18 @@ class AutoInjectionTests: XCTestCase {
func testThatItResolvesInheritedDependencies() {
class ServerImp2: ServerImp {
#if swift(>=5.1)
@InjectedWeak(didInject: { _ in
XCTAssertTrue(AutoInjectionTests.serverDidInjectCalled, "Inherited properties should be resolved first")
}) var client2: Client?
#else
var _client2 = InjectedWeak<Client>() { _ in
XCTAssertTrue(AutoInjectionTests.serverDidInjectCalled, "Inherited properties should be resolved first")
}
var client2: Client? { return _client2.value }
var client2: Client? {
return _client2.value
}
#endif
}
container.register { ServerImp2() as Server }
@@ -124,6 +172,28 @@ class AutoInjectionTests: XCTestCase {
XCTAssertTrue(client === server?.client2)
}
func testThatItCanSetInjectedProperty() {
container.register { ServerImp() as Server }
container.register { ClientImp() as Client }
let client = (try! container.resolve() as Client) as! ClientImp
let server = client.server as! ServerImp
let newServer = ServerImp()
let newClient = ClientImp()
#if swift(>=5.1)
client.server = newServer
server.client = newClient
#else
client._server = client._server.setValue(newServer)
server._client = server._client.setValue(newClient)
#endif
XCTAssertTrue(client.server === newServer)
XCTAssertTrue(server.client === newClient)
}
func testThatItThrowsErrorIfFailsToAutoInjectDependency() {
container.register { ClientImp() as Client }
@@ -209,11 +279,19 @@ class AutoInjectionTests: XCTestCase {
let obj2 = try! container.resolve() as Obj2
//then
XCTAssertTrue(obj2 === obj2.obj1.value!.obj2.value!,
#if swift(>=5.1)
XCTAssertTrue(obj2 === obj2.obj1!.obj2!,
"Auto-injected instance should be reused on next auto-injection")
XCTAssertTrue(obj2.obj1.value! === obj2.obj1.value!.obj3.value!.obj1,
XCTAssertTrue(obj2.obj1! === obj2.obj1!.obj3!.obj1,
"Auto-injected instance should be reused on next resolve")
#else
XCTAssertTrue(obj2 === obj2.obj1.value!.obj2.value!,
"Auto-injected instance should be reused on next auto-injection")
XCTAssertTrue(obj2.obj1.value! === obj2.obj1.value!.obj3.value!.obj1,
"Auto-injected instance should be reused on next resolve")
#endif
}
func testThatThereIsNoRetainCycleBetweenAutoInjectedCircularDependencies() {
@@ -274,7 +352,11 @@ class AutoInjectionTests: XCTestCase {
let client = try! container.resolve() as Client
//then
#if swift(>=5.1)
let taggedServer = (client as! ClientImp).taggedServer!
#else
let taggedServer = (client as! ClientImp).taggedServer.value!
#endif
let server = client.server!
//server and tagged server should be resolved as different instances
@@ -293,7 +375,11 @@ class AutoInjectionTests: XCTestCase {
let client = try! container.resolve(tag: "tagged") as Client
//then
#if swift(>=5.1)
let taggedServer = (client as! ClientImp).taggedServer!
#else
let taggedServer = (client as! ClientImp).taggedServer.value!
#endif
let server = client.server!
//server and tagged server should be resolved as the same instance
@@ -314,8 +400,13 @@ class AutoInjectionTests: XCTestCase {
let client = try! container.resolve(tag: "otherTag") as Client
//then
#if swift(>=5.1)
let taggedServer = (client as! ClientImp).taggedServer!
let nilTaggedServer = (client as! ClientImp).nilTaggedServer!
#else
let taggedServer = (client as! ClientImp).taggedServer.value!
let nilTaggedServer = (client as! ClientImp).nilTaggedServer.value!
#endif
let server = client.server!
//server and tagged server should be resolved as different instances