Compare commits

...

24 Commits

Author SHA1 Message Date
Ilya Puchka 7ec008a5c0 Merge pull request #237 from michalsrutek/develop
Fix couple of typos
2020-05-29 14:42:25 +01:00
Ilya Puchka 772bc13daa Merge pull request #238 from michalsrutek/use-anyobject-keyword
Use preferred AnyObject keyword
2020-05-29 14:42:03 +01:00
Michal Srutek fa4325399a Use preferred AnyObject keyword 2020-04-23 10:22:20 +02:00
Michal Srutek f7fbbda748 Fix couple of typos 2020-04-23 09:59:03 +02:00
Ilya Puchka 56b0768d60 bump version to 7.1.1 2019-12-27 11:14:55 +00:00
Ilya Puchka 8cf87efb95 Update podspec author 2019-12-27 11:07:21 +00:00
Ilya Puchka 3aa9b334f1 Merge pull request #233 from AliSoftware/spm-fix
Enabled StoryboardInstantiatable for SPM builds
2019-11-15 11:53:25 +00:00
Ilya Puchka b2a0cda242 Update StoryboardInstantiatable.swift 2019-11-13 13:16:52 +00:00
Ilya Puchka 82e3d02497 Merge branch 'master' into develop 2019-11-11 14:22:24 +00:00
Ilya Puchka 3b2f5a272f bumpe version to 7.1.0 2019-11-10 16:11:17 +00:00
Ilya Puchka eab2d424e7 allow to disable thread safety on container level 2019-11-10 16:01:20 +00:00
Ilya Puchka 0f2cda0a52 update changelog 2019-11-10 15:53:45 +00:00
Ilya Puchka 05c02c9645 Merge pull request #225 from AliSoftware/property-delegates
Property wrappers
2019-11-10 15:52:49 +00:00
Ilya Puchka fa086dad8f add xcode 11 and swift 5.1 to travis build 2019-11-10 15:35:53 +00:00
Ilya Puchka cdc09f5a03 removed unneeded initial nil values 2019-11-10 15:22:38 +00:00
Ilya Puchka b94ed3bea2 fix warning 2019-11-10 15:22:16 +00:00
Ilya Puchka 0fd70e65dd Merge branch 'develop' into property-delegates 2019-11-10 15:13:55 +00:00
Ilya Puchka 97e7f4782f Merge branch 'pr/221' into develop 2019-11-10 15:12:41 +00:00
Ilya Puchka 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
31 changed files with 410 additions and 149 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>
@@ -13,7 +13,7 @@ On the previous page you saw how auto-wiring helps us to get rid of boilerplate
Let's say you have following related components:
*/
protocol Service: class {
protocol Service: AnyObject {
var logger: Logger? { get }
var tracker: Tracker? { get }
}
@@ -101,11 +101,11 @@ serverWithNoClient.optionalClient.value
Another example of using auto-injection is circular dependencies. Let's say you have a `Server` and a `ServerClient` both referencing each other.
*/
protocol Server: class {
protocol Server: AnyObject {
weak var client: ServerClient? { get }
}
protocol ServerClient: class {
protocol ServerClient: AnyObject {
var server: Server? { get }
}
@@ -13,11 +13,11 @@ Very often we encounter situations when we have circular dependencies between co
Let's say you have some network client and it's delegate defined like this:
*/
protocol NetworkClientDelegate: class {
protocol NetworkClientDelegate: AnyObject {
var networkClient: NetworkClient { get }
}
protocol NetworkClient: class {
protocol NetworkClient: AnyObject {
weak var delegate: NetworkClientDelegate? { get set }
}
@@ -1,6 +1,6 @@
import Foundation
public protocol Service: class {}
public protocol Service: AnyObject {}
public class ServiceImp1: Service {
public init() {}
@@ -22,7 +22,7 @@ public class ServiceImp4: Service {
}
public protocol Client: class {
public protocol Client: AnyObject {
var service: Service {get}
init(service: Service)
}
@@ -74,9 +74,9 @@ public class DataProviderImp: DataProvider {
public init() {}
}
public protocol ListInteractorOutput: class {}
public protocol ListModuleInterface: class {}
public protocol ListInteractorInput: class {}
public protocol ListInteractorOutput: AnyObject {}
public protocol ListModuleInterface: AnyObject {}
public protocol ListInteractorInput: AnyObject {}
public class ListPresenter: NSObject {
public var listInteractor : ListInteractorInput?
public var listWireframe : ListWireframe?
@@ -96,8 +96,8 @@ public class ListWireframe : NSObject {
}
}
public protocol AddModuleDelegate: class {}
public protocol AddModuleInterface: class {}
public protocol AddModuleDelegate: AnyObject {}
public protocol AddModuleInterface: AnyObject {}
public class AddWireframe: NSObject {
let addPresenter : AddPresenter
public init(addPresenter: AddPresenter) {
+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>
@@ -8,7 +8,7 @@
import Foundation
///Provides Person entitis fetching them with web service
///Provides Person entities fetching them with web service
struct SWAPIPersonProvider : PersonProviderAPI {
let ws: NetworkLayer
@@ -8,7 +8,7 @@
import UIKit
protocol FetchableTrait: class {
protocol FetchableTrait: AnyObject {
associatedtype ObjectType
var objects: [ObjectType]? { get set }
var batchRequestID: Int { get set }
+1 -1
View File
@@ -9,7 +9,7 @@
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>7.0.1</string>
<string>7.1.1</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
+204 -78
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.
@@ -175,7 +237,7 @@ public final class Injected<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox
For that reason if you resolve instance that has a _weak_ auto-injected property this property
will be released when `resolve` will complete.
Use `InjectedWeak<T>` to define one of two circular dependecies if another dependency is defined as `Injected<U>`.
Use `InjectedWeak<T>` to define one of two circular dependencies if another dependency is defined as `Injected<U>`.
This will prevent a retain cycle between resolved instances.
- warning: Do not define this property as optional or container will not be able to inject it.
@@ -185,42 +247,124 @@ public final class Injected<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox
```swift
class ServiceImp: Service {
var client = InjectedWeak<Client>()
@InjectedWeak var client: Client?
}
```
- seealso: `Injected`
*/
public final class InjectedWeak<T>: _InjectedPropertyBox<T>, AutoInjectedPropertyBox {
@propertyWrapper
public struct InjectedWeak<T>: _InjectedPropertyBox, AutoInjectedPropertyBox {
//Only classes (means AnyObject) can be used as `weak` properties
//but we can not make <T: AnyObject> because that will prevent using protocol as generic type
//so we just rely on user reading documentation and passing AnyObject in runtime
//also we will throw fatal error if type can not be casted to AnyObject during resolution.
///The type of wrapped property.
public static var wrappedType: Any.Type {
return T.self
}
var valueBox: WeakBox<T>? = nil {
didSet {
if let value = value { didInject(value) }
let valueBox: WeakBox<T> = WeakBox(nil)
///Wrapped value.
public var wrappedValue: T? {
get {
return valueBox.value
}
set {
guard (required && newValue != nil) || !required else {
fatalError("Can not set required property to nil.")
}
valueBox.unboxed = newValue as AnyObject
}
}
let required: Bool
let didInject: (T) -> ()
let tag: DependencyContainer.Tag?
let overrideTag: Bool
public init(wrappedValue initialValue: T?) {
self.init()
}
}
#else
/**
Use this wrapper to identify _weak_ properties of the instance that should be
auto-injected by `DependencyContainer`. Type T should be a **class** type.
Otherwise it will cause runtime exception when container will try to resolve the property.
Use this wrapper to define one of two circular dependencies to avoid retain cycle.
- note: The only difference between `InjectedWeak` and `Injected` is that `InjectedWeak` uses
_weak_ reference to store underlying value, when `Injected` uses _strong_ reference.
For that reason if you resolve instance that has a _weak_ auto-injected property this property
will be released when `resolve` will complete.
Use `InjectedWeak<T>` to define one of two circular dependencies if another dependency is defined as `Injected<U>`.
This will prevent a retain cycle between resolved instances.
- warning: Do not define this property as optional or container will not be able to inject it.
Instead define it with initial value of `InjectedWeak<T>()`.
**Example**:
```swift
class ServiceImp: Service {
var client = InjectedWeak<Client>()
}
```
- seealso: `Injected`
*/
public struct InjectedWeak<T>: _InjectedPropertyBox, AutoInjectedPropertyBox {
//Only classes (means AnyObject) can be used as `weak` properties
//but we can not make <T: AnyObject> because that will prevent using protocol as generic type
//so we just rely on user reading documentation and passing AnyObject in runtime
//also we will throw fatal error if type can not be casted to AnyObject during resolution.
let valueBox: WeakBox<T> = WeakBox(nil)
///Wrapped value.
public var value: T? {
return valueBox?.value
return valueBox.value
}
init(value: T?, required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> ()) {
self.valueBox = value.map(WeakBox.init)
super.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
let required: Bool
let didInject: (T) -> ()
let tag: DependencyContainer.Tag?
let overrideTag: Bool
/// Returns a new wrapper with provided value.
func setValue(_ value: T?) -> InjectedWeak {
guard (required && value != nil) || !required else {
fatalError("Can not set required property to nil.")
}
return InjectedWeak(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
}
}
#endif
public extension InjectedWeak {
///The type of wrapped property.
static var wrappedType: Any.Type {
return T.self
}
init(value: T?, required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> ()) {
self.init(required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
self.valueBox.unboxed = value as AnyObject
}
init(required: Bool = true, tag: DependencyTagConvertible?, overrideTag: Bool, didInject: @escaping (T) -> () = { _ in }) {
self.required = required
self.tag = tag?.dependencyTag
self.overrideTag = overrideTag
self.didInject = didInject
}
/**
Creates a new wrapper for weak auto-injected property.
@@ -232,46 +376,28 @@ public final class InjectedWeak<T>: _InjectedPropertyBox<T>, AutoInjectedPropert
- didInject: Block that will be called when concrete instance is injected in this property.
Similar to `didSet` property observer. Default value does nothing.
*/
public convenience init(required: Bool = true, didInject: @escaping (T) -> () = { _ in }) {
init(required: Bool = true, didInject: @escaping (T) -> () = { _ in }) {
self.init(value: nil, required: required, tag: nil, overrideTag: false, didInject: didInject)
}
public convenience init(required: Bool = true, tag: DependencyTagConvertible?, didInject: @escaping (T) -> () = { _ in }) {
init(required: Bool = true, tag: DependencyTagConvertible?, didInject: @escaping (T) -> () = { _ in }) {
self.init(value: nil, required: required, tag: tag, overrideTag: true, didInject: didInject)
}
public func resolve(_ container: DependencyContainer) throws {
let resolved: T? = try super.resolve(with: container)
valueBox = resolved.map(WeakBox.init)
}
/// Returns a new wrapper with provided value.
public func setValue(_ value: T?) -> InjectedWeak {
guard (required && value != nil) || !required else {
fatalError("Can not set required property to nil.")
func resolve(_ container: DependencyContainer) throws {
let resolved: T? = try self.resolve(with: container, tag: tag, overrideTag: overrideTag, required: required)
valueBox.unboxed = resolved as AnyObject
if let resolved = resolved {
didInject(resolved)
}
return InjectedWeak(value: value, required: required, tag: tag, overrideTag: overrideTag, didInject: didInject)
}
}
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)
})
+5 -5
View File
@@ -61,7 +61,7 @@ public struct DefinitionKey: Hashable, CustomStringConvertible {
}
///Dummy protocol to store definitions for different types in collection
public protocol DefinitionType: class { }
public protocol DefinitionType: AnyObject { }
/**
`Definition<T, U>` describes how instances of type `T` should be created when this type is resolved by the `DependencyContainer`.
@@ -206,7 +206,7 @@ public final class Definition<T, U>: DefinitionType {
//definitions for types that can be resolved by `forwardsTo` definition
//can also be used to resolve self type and it's implementing types
//this way container properly reuses previosly resolved instances
//this way container properly reuses previously resolved instances
//when there are several forwarded definitions
//see testThatItReusesInstanceResolvedByTypeForwarding)
for definition in forwardsTo.forwardsFrom {
@@ -292,7 +292,7 @@ class DefinitionBuilder<T, U> {
typealias KeyDefinitionPair = (key: DefinitionKey, definition: _Definition)
/// Definitions are matched if they are registered for the same tag and thier factories accept the same number of runtime arguments.
/// Definitions are matched if they are registered for the same tag and their factories accept the same number of runtime arguments.
private func ~=(lhs: KeyDefinitionPair, rhs: KeyDefinitionPair) -> Bool {
guard lhs.key.type == rhs.key.type else { return false }
guard lhs.key.tag == rhs.key.tag else { return false }
@@ -300,9 +300,9 @@ private func ~=(lhs: KeyDefinitionPair, rhs: KeyDefinitionPair) -> Bool {
return true
}
/// Returns key-defintion pairs with definitions able to resolve that type (directly or via type forwarding)
/// Returns key-definition pairs with definitions able to resolve that type (directly or via type forwarding)
/// and which tag matches provided key's tag or is nil if strictByTag is false.
/// In the end filters defintions by type of runtime arguments.
/// In the end filters definitions by type of runtime arguments.
func filter(definitions _definitions: [KeyDefinitionPair], byKey key: DefinitionKey, strictByTag: Bool = false, byTypeOfArguments: Bool = false) -> [KeyDefinitionPair] {
let definitions = _definitions
.filter({ $0.key.type == key.type || $0.definition.doesImplements(type: key.type) })
+11 -4
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)
}
@@ -91,7 +94,7 @@ public final class DependencyContainer {
Call this method to complete container setup. After container is bootstrapped
you can not add or remove definitions. Trying to do so will cause runtime exception.
You can completely reset container, after reset you can bootstrap it again.
During bootsrap container will instantiate components registered with `EagerSingleton` scope.
During bootstrap container will instantiate components registered with `EagerSingleton` scope.
- throws: `DipError` if failed to instantiate any component
*/
@@ -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()
@@ -291,7 +298,7 @@ extension DependencyContainer {
for collaborator in _collaborators {
//if container is already in a context resolving this type
//it means that it has been already called to resolve this type,
//so there is probably a cercular reference between containers.
//so there is probably a circular reference between containers.
//To break it skip this container
if let context = collaborator.context, context.resolvingType == key.type && context.tag == key.tag { continue }
@@ -446,7 +453,7 @@ extension DependencyContainer: CustomStringConvertible {
//MARK: - DependencyTagConvertible
/// Implement this protocol on your type if you want to use its instances as `DependencyContainer`'s tags.
/// `DependencyContainer.Tag`, `String`, `Int` and any `RawRepresentable` with `RawType` of `String` or `Int` by default confrom to this protocol.
/// `DependencyContainer.Tag`, `String`, `Int` and any `RawRepresentable` with `RawType` of `String` or `Int` by default conform to this protocol.
public protocol DependencyTagConvertible {
var dependencyTag: DependencyContainer.Tag { get }
}
+1 -1
View File
@@ -66,7 +66,7 @@ public enum DipError: Error, CustomStringConvertible {
case ambiguousDefinitions(type: Any.Type, definitions: [DefinitionType])
/**
Thrown by `resolve(tag:)` if resolved instance does not implemenet resolved type (i.e. when type-forwarding).
Thrown by `resolve(tag:)` if resolved instance does not implement resolved type (i.e. when type-forwarding).
- parameters:
- resolved: Resolved instance
+1 -1
View File
@@ -81,7 +81,7 @@ extension DependencyContainer {
- parameters:
- tag: The arbitrary tag to use to lookup definition.
- builder: Generic closure that accepts generic factory and returns inctance created by that factory.
- builder: Generic closure that accepts generic factory and returns instance created by that factory.
- throws: `DipError.DefinitionNotFound`, `DipError.AutoInjectionFailed`, `DipError.AmbiguousDefinitions`, `DipError.InvalidType`
+2 -2
View File
@@ -98,7 +98,7 @@ extension DependencyContainer {
}
```
Though before you do so you should probably review your design and try to reduce number of depnedencies.
Though before you do so you should probably review your design and try to reduce number of dependencies.
*/
public func register<T, U>(scope: ComponentScope, type: T.Type, tag: DependencyTagConvertible?, factory: @escaping (U) throws -> T, numberOfArguments: Int, autoWiringFactory: @escaping (DependencyContainer, Tag?) throws -> T) -> Definition<T, U> {
let definition = DefinitionBuilder<T, U> {
@@ -141,7 +141,7 @@ extension DependencyContainer {
which factories accept any number of runtime arguments and are tagged with the same tag,
passed to `resolve` method, or with no tag. Container will try to use these definitions
to resolve a component one by one until one of them succeeds, starting with tagged definitions
in order of decreasing their's factories number of arguments. If none of them succeds it will
in order of decreasing their's factories number of arguments. If none of them succeeds it will
throw an error. If it finds two definitions with the same number of arguments - it will throw
an error.
+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
}
}
+97 -6
View File
@@ -25,16 +25,44 @@
import XCTest
@testable import Dip
private protocol Server: class {
private protocol Server: AnyObject {
var client: Client! {get}
var anotherClient: Client! {get set}
}
private protocol Client: class {
private protocol Client: AnyObject {
var server: Server? {get}
var anotherServer: Server! {get set}
}
#if swift(>=5.1)
private class ServerImp: Server {
@InjectedWeak(didInject: { _ in
AutoInjectionTests.clientDidInjectCalled = true
}) var client: Client!
@InjectedWeak(required: false) var _optionalProperty: AnyObject?
weak var anotherClient: Client!
}
private class ClientImp: Client {
@Injected(didInject: { _ in
AutoInjectionTests.serverDidInjectCalled = true
}) var server: Server
@Injected(required: false) var _optionalProperty: AnyObject?
@Injected(tag: "tagged") var taggedServer: Server
@Injected(tag: nil) var nilTaggedServer: Server
var anotherServer: Server!
}
#else
private class ServerImp: Server {
var _client = InjectedWeak<Client>() { _ in
@@ -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
@@ -397,7 +488,7 @@ class AutoInjectionTests: XCTestCase {
XCTAssertNil(server)
}
func testThatItAutoInjectsWhenOverridenInDefinition() {
func testThatItAutoInjectsWhenOverriddenInDefinition() {
let container = DependencyContainer(autoInjectProperties: false)
container.register { ServerImp() as Server }
container.register { ClientImp() as Client }
+2 -2
View File
@@ -25,12 +25,12 @@
import XCTest
@testable import Dip
private protocol Service: class { }
private protocol Service: AnyObject { }
private class ServiceImp1: Service { }
private class ServiceImp2: Service { }
private class ServiceImp3 {}
private protocol AutoWiredClient: class {
private protocol AutoWiredClient: AnyObject {
var service1: Service! { get set }
var service2: Service! { get set }
}
+2 -2
View File
@@ -25,7 +25,7 @@
import XCTest
@testable import Dip
private protocol Service: class {}
private protocol Service: AnyObject {}
private class ServiceImp1: Service {}
private class ServiceImp2: Service {}
@@ -316,7 +316,7 @@ class ComponentScopeTests: XCTestCase {
container.register(service, type: Service.self)
//when
//resolve and realease reight away
//resolve and release right away
_ = try? container.resolve() as ServiceImp1
//then
+1 -1
View File
@@ -100,7 +100,7 @@ class DefinitionTests: XCTestCase {
XCTAssertFalse(blockCalled)
}
func testThatItRegisteresOptionalTypesAsForwardedTypes() {
func testThatItRegistersOptionalTypesAsForwardedTypes() {
let def = Definition<Service, ()>(scope: .unique) { ServiceImp() as Service }
XCTAssertTrue(def.implementingTypes.contains(where: { $0 == Service?.self }))
+3 -3
View File
@@ -25,14 +25,14 @@
import XCTest
@testable import Dip
private protocol Service: class { }
private protocol Service: AnyObject { }
private class ServiceImp1: Service { }
private class ServiceImp2: Service { }
private protocol Server: class {
private protocol Server: AnyObject {
var client: Client! { get }
}
private protocol Client: class {
private protocol Client: AnyObject {
var server: Server! { get }
}
+5 -5
View File
@@ -164,10 +164,10 @@ class DipUITests: XCTestCase {
}
}
protocol SomeService: class {
protocol SomeService: AnyObject {
var delegate: SomeServiceDelegate? { get set }
}
protocol SomeServiceDelegate: class { }
protocol SomeServiceDelegate: AnyObject { }
class SomeServiceImp: SomeService {
weak var delegate: SomeServiceDelegate?
init(delegate: SomeServiceDelegate) {
@@ -176,10 +176,10 @@ class SomeServiceImp: SomeService {
init(){}
}
protocol OtherService: class {
protocol OtherService: AnyObject {
var delegate: OtherServiceDelegate? { get set }
}
protocol OtherServiceDelegate: class {}
protocol OtherServiceDelegate: AnyObject {}
class OtherServiceImp: OtherService {
weak var delegate: OtherServiceDelegate?
init(delegate: OtherServiceDelegate){
@@ -189,7 +189,7 @@ class OtherServiceImp: OtherService {
}
protocol SomeScreen: class {
protocol SomeScreen: AnyObject {
var someService: SomeService? { get set }
var otherService: OtherService? { get set }
}
+1 -1
View File
@@ -268,7 +268,7 @@ class RuntimeArgumentsTests: XCTestCase {
//Due to incomplete implementation of SE-0054 (bug: https://bugs.swift.org/browse/SR-2143)
//registering definition with T? and T! arguments types will produce two different definitions
//but when argement of T! will be passed to `resolve` method it will be transformed to T?
//but when argument of T! will be passed to `resolve` method it will be transformed to T?
//and wrong definition will be used
//When fixed using T? and T! should not register two different definitions
+2 -2
View File
@@ -26,11 +26,11 @@
import XCTest
@testable import Dip
private protocol Server: class {
private protocol Server: AnyObject {
var client: Client! { get set }
}
private protocol Client: class {
private protocol Client: AnyObject {
var server: Server { get }
}
+3 -3
View File
@@ -25,8 +25,8 @@
import XCTest
@testable import Dip
private protocol Service: class { }
private protocol ForwardedType: class { }
private protocol Service: AnyObject { }
private protocol ForwardedType: AnyObject { }
#if os(Linux)
private class ServiceImp1: Service, ForwardedType { }
private class ServiceImp2: Service, ForwardedType { }
@@ -136,7 +136,7 @@ class TypeForwardingTests: XCTestCase {
resolveDependenciesCalled = true
//when
//resolving via type-forawrding for tag different then tag for original definition
//resolving via type-forwarding for tag different then tag for original definition
let forwardType = try container.resolve(tag: "tag") as ForwardedType
let anyForwardType = try container.resolve(ForwardedType.self, tag: "tag") as! ForwardedType