Skip to content

Commit

Permalink
Merge branch 'release/4.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
AliSoftware committed Feb 4, 2016
2 parents 8f3fad7 + 7de69e1 commit 126c5d3
Show file tree
Hide file tree
Showing 41 changed files with 1,896 additions and 547 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# CHANGELOG

## 4.1.0

#### New features

* Added auto-injection feature.
[#13](https://github.com/AliSoftware/Dip/pull/13), [@ilyapuchka](https://github.com/ilyapuchka)
* Factories and `resolveDependencies` blocks of `DefinitionOf` are now allowed to `throw`. Improved errors handling.
[#32](https://github.com/AliSoftware/Dip/pull/13), [@ilyapuchka](https://github.com/ilyapuchka)
* Thread safety reimplemented with support for recursive methods calls.
[#31](https://github.com/AliSoftware/Dip/pull/31), [@mwoollard](https://github.com/mwoollard)


## 4.0.0

#### New Features
Expand Down
2 changes: 1 addition & 1 deletion Dip.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Dip"
s.version = "4.0.0"
s.version = "4.1.0"
s.summary = "A simple Dependency Resolver: Dependency Injection using Protocol resolution."

s.description = <<-DESC
Expand Down
40 changes: 38 additions & 2 deletions Dip/Dip.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@
0919F4EC1C16419500DC3B10 /* DefinitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919F4CF1C16417000DC3B10 /* DefinitionTests.swift */; };
0919F4ED1C16419500DC3B10 /* RuntimeArgumentsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919F4D21C16417000DC3B10 /* RuntimeArgumentsTests.swift */; };
0919F4EE1C16419500DC3B10 /* ComponentScopeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919F4CE1C16417000DC3B10 /* ComponentScopeTests.swift */; };
0982AF0C1C5183A000B62463 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982AF0B1C5183A000B62463 /* Utils.swift */; };
0982AF0D1C5183A000B62463 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982AF0B1C5183A000B62463 /* Utils.swift */; };
0982AF0E1C5183A000B62463 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982AF0B1C5183A000B62463 /* Utils.swift */; };
0982AF0F1C5183A000B62463 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982AF0B1C5183A000B62463 /* Utils.swift */; };
09873F561C1E0237000C02F6 /* AutoInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09873F551C1E0237000C02F6 /* AutoInjection.swift */; };
09873F771C1E024E000C02F6 /* AutoInjectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09873F751C1E0249000C02F6 /* AutoInjectionTests.swift */; };
09873F781C1E024E000C02F6 /* AutoInjectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09873F751C1E0249000C02F6 /* AutoInjectionTests.swift */; };
09873F791C1E024F000C02F6 /* AutoInjectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09873F751C1E0249000C02F6 /* AutoInjectionTests.swift */; };
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 */; };
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 */
Expand Down Expand Up @@ -82,6 +96,10 @@
0919F4D01C16417000DC3B10 /* DipTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DipTests.swift; sourceTree = "<group>"; };
0919F4D11C16417000DC3B10 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0919F4D21C16417000DC3B10 /* RuntimeArgumentsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeArgumentsTests.swift; sourceTree = "<group>"; };
0982AF0B1C5183A000B62463 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
09873F551C1E0237000C02F6 /* AutoInjection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutoInjection.swift; sourceTree = "<group>"; };
09873F751C1E0249000C02F6 /* AutoInjectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutoInjectionTests.swift; sourceTree = "<group>"; };
2C15B94F1C25EF7800EA3486 /* ThreadSafetyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadSafetyTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -147,6 +165,8 @@
0919F4CA1C16417000DC3B10 /* Dip.swift */,
0919F4C81C16417000DC3B10 /* Definition.swift */,
0919F4CC1C16417000DC3B10 /* RuntimeArguments.swift */,
09873F551C1E0237000C02F6 /* AutoInjection.swift */,
0982AF0B1C5183A000B62463 /* Utils.swift */,
0919F4CB1C16417000DC3B10 /* Info.plist */,
);
path = Dip;
Expand All @@ -159,6 +179,8 @@
0919F4CF1C16417000DC3B10 /* DefinitionTests.swift */,
0919F4D21C16417000DC3B10 /* RuntimeArgumentsTests.swift */,
0919F4CE1C16417000DC3B10 /* ComponentScopeTests.swift */,
09873F751C1E0249000C02F6 /* AutoInjectionTests.swift */,
2C15B94F1C25EF7800EA3486 /* ThreadSafetyTests.swift */,
0919F4D11C16417000DC3B10 /* Info.plist */,
);
path = DipTests;
Expand Down Expand Up @@ -466,8 +488,10 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0982AF0C1C5183A000B62463 /* Utils.swift in Sources */,
0919F4D51C16417B00DC3B10 /* Definition.swift in Sources */,
0919F4D41C16417B00DC3B10 /* Dip.swift in Sources */,
09873F561C1E0237000C02F6 /* AutoInjection.swift in Sources */,
0919F4D61C16417B00DC3B10 /* RuntimeArguments.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -477,18 +501,22 @@
buildActionMask = 2147483647;
files = (
0919F4E61C16419300DC3B10 /* ComponentScopeTests.swift in Sources */,
2C15B9511C25F01200EA3486 /* ThreadSafetyTests.swift in Sources */,
0919F4E41C16419300DC3B10 /* DefinitionTests.swift in Sources */,
0919F4E31C16419300DC3B10 /* DipTests.swift in Sources */,
0919F4E51C16419300DC3B10 /* RuntimeArgumentsTests.swift in Sources */,
09873F771C1E024E000C02F6 /* AutoInjectionTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0903B3731C1615EC002241C1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0982AF0D1C5183A000B62463 /* Utils.swift in Sources */,
0919F4D91C16417C00DC3B10 /* Definition.swift in Sources */,
0919F4D81C16417C00DC3B10 /* Dip.swift in Sources */,
09873F7A1C1E0252000C02F6 /* AutoInjection.swift in Sources */,
0919F4DA1C16417C00DC3B10 /* RuntimeArguments.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -498,18 +526,22 @@
buildActionMask = 2147483647;
files = (
0919F4EA1C16419400DC3B10 /* ComponentScopeTests.swift in Sources */,
2C15B9521C25F01300EA3486 /* ThreadSafetyTests.swift in Sources */,
0919F4E81C16419400DC3B10 /* DefinitionTests.swift in Sources */,
0919F4E71C16419400DC3B10 /* DipTests.swift in Sources */,
0919F4E91C16419400DC3B10 /* RuntimeArgumentsTests.swift in Sources */,
09873F781C1E024E000C02F6 /* AutoInjectionTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0903B3A11C1618AF002241C1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0982AF0E1C5183A000B62463 /* Utils.swift in Sources */,
0919F4DD1C16417D00DC3B10 /* Definition.swift in Sources */,
0919F4DC1C16417D00DC3B10 /* Dip.swift in Sources */,
09873F7B1C1E0253000C02F6 /* AutoInjection.swift in Sources */,
0919F4DE1C16417D00DC3B10 /* RuntimeArguments.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -519,18 +551,22 @@
buildActionMask = 2147483647;
files = (
0919F4EE1C16419500DC3B10 /* ComponentScopeTests.swift in Sources */,
2C15B9531C25F01500EA3486 /* ThreadSafetyTests.swift in Sources */,
0919F4EC1C16419500DC3B10 /* DefinitionTests.swift in Sources */,
0919F4EB1C16419500DC3B10 /* DipTests.swift in Sources */,
0919F4ED1C16419500DC3B10 /* RuntimeArgumentsTests.swift in Sources */,
09873F791C1E024F000C02F6 /* AutoInjectionTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0903B3FF1C162862002241C1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0982AF0F1C5183A000B62463 /* Utils.swift in Sources */,
0919F4E11C16417E00DC3B10 /* Definition.swift in Sources */,
0919F4E01C16417E00DC3B10 /* Dip.swift in Sources */,
09873F7C1C1E0254000C02F6 /* AutoInjection.swift in Sources */,
0919F4E21C16417E00DC3B10 /* RuntimeArguments.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -804,7 +840,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 4.0.0;
CURRENT_PROJECT_VERSION = 4.1.0;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
Expand Down Expand Up @@ -853,7 +889,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 4.0.0;
CURRENT_PROJECT_VERSION = 4.1.0;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
Expand Down
3 changes: 2 additions & 1 deletion Dip/Dip.xcodeproj/xcshareddata/xcschemes/Dip-iOS.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
Expand Down
210 changes: 210 additions & 0 deletions Dip/Dip/AutoInjection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
//
// Dip
//
// Copyright (c) 2015 Olivier Halligon <[email protected]>
//
// 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.
//

//MARK: Public

extension DependencyContainer {

/**
Resolves properties of passed object wrapped with `Injected<T>` or `InjectedWeak<T>`
*/
func autoInjectProperties(instance: Any) throws {
try Mirror(reflecting: instance).children.forEach(_resolveChild)
}

private func _resolveChild(child: Mirror.Child) throws {
guard let injectedPropertyBox = child.value as? _AnyInjectedPropertyBox else { return }

do {
try injectedPropertyBox.resolve(self)
}
catch {
throw DipError.AutoInjectionFailed(label: child.label, type: injectedPropertyBox.dynamicType.wrappedType, underlyingError: error)
}
}

}

private protocol _AnyInjectedPropertyBox: class {
static var wrappedType: Any.Type { get }
func resolve(container: DependencyContainer) throws
}


/**
Use this wrapper to identifiy 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>, _AnyInjectedPropertyBox {

var _value: Any? = nil {
didSet {
if let value = value { didInject(value) }
}
}

///Wrapped value.
public var value: T? {
return _value as? T
}

/**
Creates new wrapper for auto-injected property.

- parameters:
- required: Defines if the property is required or not.
If container fails to inject required property it will als fail to resolve
the instance that defines that property. Default is `true`.
- tag: An optional tag to use to lookup definitions when injecting this property. Default is `nil`.
- didInject: block that will be called when concrete instance is injected in this property.
Similar to `didSet` property observer. Default value does nothing.
*/
public override init(required: Bool = true, tag: DependencyContainer.Tag? = nil, didInject: T -> () = { _ in }) {
super.init(required: required, tag: tag, didInject: didInject)
}

private func resolve(container: DependencyContainer) throws {
let resolved: T? = try super.resolve(container)
_value = resolved
}

}

/**
Use this wrapper to identifiy strong 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 _weak_ auto-injected property this property
will be released when `resolve` returns because no one else holds reference to it except
the container during dependency graph resolution.

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 final class InjectedWeak<T>: _InjectedPropertyBox<T>, _AnyInjectedPropertyBox {

//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.

weak var _value: AnyObject? = nil {
didSet {
if let value = value { didInject(value) }
}
}

///Wrapped value.
public var value: T? {
return _value as? T
}

/**
Creates new wrapper for weak auto-injected property.

- parameters:
- required: Defines if the property is required or not.
If container fails to inject required property it will als fail to resolve
the instance that defines that property. Default is `true`.
- tag: An optional tag to use to lookup definitions when injecting this property. Default is `nil`.
- didInject: block that will be called when concrete instance is injected in this property.
Similar to `didSet` property observer. Default value does nothing.
*/
public override init(required: Bool = true, tag: DependencyContainer.Tag? = nil, didInject: T -> () = { _ in }) {
super.init(required: required, tag: tag, didInject: didInject)
}

private func resolve(container: DependencyContainer) throws {
let resolved: T? = try super.resolve(container)
guard let resolvedObject = resolved as? AnyObject else {
fatalError("\(T.self) can not be casted to AnyObject. InjectedWeak wrapper should be used to wrap only classes.")
}
_value = resolvedObject
}

}

private class _InjectedPropertyBox<T> {

static var wrappedType: Any.Type {
return T.self
}

let required: Bool
let didInject: T -> ()
let tag: DependencyContainer.Tag?

init(required: Bool = true, tag: DependencyContainer.Tag?, didInject: T -> () = { _ in }) {
self.required = required
self.tag = tag
self.didInject = didInject
}

private func resolve(container: DependencyContainer) throws -> T? {
let resolved: T?
if required {
resolved = try container.resolve(tag: tag, builder: { (factory: () throws -> T) in try factory() }) as T
}
else {
resolved = try? container.resolve(tag: tag, builder: { (factory: () throws -> T) in try factory() }) as T
}
return resolved
}

}


Loading

0 comments on commit 126c5d3

Please sign in to comment.