Skip to content

Commit 74dfc3e

Browse files
Ilya Puchkailyapuchka
Ilya Puchka
authored andcommitted
reverted DefinitionOf to final class, added auto-injection implementation
1 parent 0c93e86 commit 74dfc3e

7 files changed

+438
-52
lines changed

Diff for: Dip/Dip.xcodeproj/project.pbxproj

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
094526B41BEA51540034E72A /* RuntimeArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094526B31BEA51540034E72A /* RuntimeArguments.swift */; };
1515
094526B61BEA520B0034E72A /* Definition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094526B51BEA520B0034E72A /* Definition.swift */; };
1616
094526B81BEA536A0034E72A /* RuntimeArgumentsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094526B71BEA536A0034E72A /* RuntimeArgumentsTests.swift */; };
17+
097EE5B21BED394B00A358BB /* AutoInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097EE5B11BED394B00A358BB /* AutoInjection.swift */; };
18+
097EE5B41BED4EFE00A358BB /* AutoInjectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097EE5B31BED4EFE00A358BB /* AutoInjectionTests.swift */; };
1719
0989323F1BEBC8CD00ACDA2B /* ComponentScopeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0989323E1BEBC8CD00ACDA2B /* ComponentScopeTests.swift */; };
1820
09969C551BEB7C0A00F93C70 /* Dip.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 09969C541BEB7C0A00F93C70 /* Dip.podspec */; };
1921
/* End PBXBuildFile section */
@@ -39,6 +41,8 @@
3941
094526B31BEA51540034E72A /* RuntimeArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = RuntimeArguments.swift; sourceTree = "<group>"; tabWidth = 2; };
4042
094526B51BEA520B0034E72A /* Definition.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = Definition.swift; sourceTree = "<group>"; tabWidth = 2; };
4143
094526B71BEA536A0034E72A /* RuntimeArgumentsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuntimeArgumentsTests.swift; sourceTree = "<group>"; };
44+
097EE5B11BED394B00A358BB /* AutoInjection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutoInjection.swift; sourceTree = "<group>"; };
45+
097EE5B31BED4EFE00A358BB /* AutoInjectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutoInjectionTests.swift; sourceTree = "<group>"; };
4246
0989323E1BEBC8CD00ACDA2B /* ComponentScopeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComponentScopeTests.swift; sourceTree = "<group>"; };
4347
09969C541BEB7C0A00F93C70 /* Dip.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Dip.podspec; path = ../Dip.podspec; sourceTree = "<group>"; };
4448
/* End PBXFileReference section */
@@ -89,6 +93,7 @@
8993
094526AB1BEA1D200034E72A /* Dip.swift */,
9094
094526B31BEA51540034E72A /* RuntimeArguments.swift */,
9195
094526B51BEA520B0034E72A /* Definition.swift */,
96+
097EE5B11BED394B00A358BB /* AutoInjection.swift */,
9297
094526961BEA1CFF0034E72A /* Info.plist */,
9398
);
9499
path = Dip;
@@ -100,6 +105,7 @@
100105
094526A01BEA1CFF0034E72A /* DipTests.swift */,
101106
094526B71BEA536A0034E72A /* RuntimeArgumentsTests.swift */,
102107
0989323E1BEBC8CD00ACDA2B /* ComponentScopeTests.swift */,
108+
097EE5B31BED4EFE00A358BB /* AutoInjectionTests.swift */,
103109
094526A21BEA1CFF0034E72A /* Info.plist */,
104110
);
105111
path = DipTests;
@@ -215,6 +221,7 @@
215221
files = (
216222
094526AC1BEA1D200034E72A /* Dip.swift in Sources */,
217223
094526B61BEA520B0034E72A /* Definition.swift in Sources */,
224+
097EE5B21BED394B00A358BB /* AutoInjection.swift in Sources */,
218225
094526B41BEA51540034E72A /* RuntimeArguments.swift in Sources */,
219226
);
220227
runOnlyForDeploymentPostprocessing = 0;
@@ -225,6 +232,7 @@
225232
files = (
226233
094526A11BEA1CFF0034E72A /* DipTests.swift in Sources */,
227234
0989323F1BEBC8CD00ACDA2B /* ComponentScopeTests.swift in Sources */,
235+
097EE5B41BED4EFE00A358BB /* AutoInjectionTests.swift in Sources */,
228236
094526B81BEA536A0034E72A /* RuntimeArgumentsTests.swift in Sources */,
229237
);
230238
runOnlyForDeploymentPostprocessing = 0;

Diff for: Dip/Dip/AutoInjection.swift

+213
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
//
2+
// Dip
3+
//
4+
// Copyright (c) 2015 Olivier Halligon <[email protected]>
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
//
24+
25+
//MARK: Public
26+
27+
/**
28+
Use this wrapper to identifiy strong properties of the instance that should be injected when you call
29+
`resolveDependencies()` on this instance. Type T can be any type.
30+
31+
- warning:
32+
Do not define this property as optional or container will not be able to inject it.
33+
Instead define it with initial value of `Injected<T>()`.
34+
If you need to nilify wrapped value, assing property to `Injected<T>()`.
35+
36+
**Example**:
37+
38+
```swift
39+
class ClientImp: Client {
40+
var service = Injected<Service>()
41+
}
42+
43+
```
44+
- seealso: `InjectedWeak`, `DependencyContainer.resolveDependencies(_:)`
45+
46+
*/
47+
public final class Injected<T>: _Injected {
48+
49+
var _value: Any?
50+
51+
public init(_ value: T? = nil) {
52+
self._value = value
53+
}
54+
55+
public var value: T? {
56+
get {
57+
return _value as? T
58+
}
59+
set {
60+
_value = newValue
61+
}
62+
}
63+
64+
}
65+
66+
/**
67+
Use this wrapper to identifiy weak properties of the instance that should be injected when you call
68+
`resolveDependencies()` on this instance. Type T should be a **class** type.
69+
Otherwise it will cause runtime exception when container will try to resolve the property.
70+
Use this wrapper to define one of two circular dependencies to avoid retain cycle.
71+
72+
- warning:
73+
Do not define this property as optional or container will not be able to inject it.
74+
Instead define it with initial value of `InjectedWeak<T>()`.
75+
If you need to nilify wrapped value, assing property to `InjectedWeak<T>()`.
76+
77+
**Example**:
78+
79+
```swift
80+
class ServiceImp: Service {
81+
var client = InjectedWeak<Client>()
82+
}
83+
84+
```
85+
86+
- note:
87+
The only difference between `InjectedWeak` and `Injected` is that `InjectedWeak` uses _weak_ reference
88+
to store underlying value, when `Injected` uses _strong_ reference.
89+
For that reason if you resolve instance that holds weakly injected property
90+
this property will be released when `resolve` returns 'cause no one else holds reference to it.
91+
92+
- seealso: `Injected`, `DependencyContainer.resolveDependencies(_:)`
93+
94+
*/
95+
public final class InjectedWeak<T>: _InjectedWeak {
96+
97+
//Only classes (means AnyObject) can be used as `weak` properties
98+
//but we can not make <T: AnyObject> cause that will prevent using protocol as generic type
99+
//so we just rely on user reading documentation and passing AnyObject in runtime
100+
//also we will throw fatal error if type can not be casted to AnyObject during resolution
101+
102+
weak var _value: AnyObject?
103+
104+
public init(_ value: T? = nil) {
105+
self._value = value as? AnyObject
106+
}
107+
108+
public var value: T? {
109+
get {
110+
return _value as? T
111+
}
112+
set {
113+
_value = newValue as? AnyObject
114+
}
115+
}
116+
117+
}
118+
119+
extension DependencyContainer {
120+
121+
/**
122+
Resolves dependencies of passed object. Properties that should be injected must be of type `Injected<T>` or `InjectedWeak<T>`. This method will also recursively resolve their dependencies, building full object graph.
123+
124+
- parameter instance: object whose dependecies should be resolved
125+
126+
- Note:
127+
Use `InjectedWeak<T>` to define one of two circular dependecies if another dependency is defined as `Injected<U>`.
128+
This will prevent retain cycle between resolved instances.
129+
130+
**Example**:
131+
```swift
132+
class ClientImp: Client {
133+
var service = Injected<Service>()
134+
}
135+
136+
class ServiceImp: Service {
137+
var client = InjectedWeak<Client>()
138+
}
139+
140+
//when resolved client will have service injected
141+
let client = container.resolve() as Client
142+
143+
```
144+
145+
*/
146+
public func resolveDependencies(instance: Any) {
147+
for child in Mirror(reflecting: instance).children
148+
where child.value is _Injected || child.value is _InjectedWeak {
149+
150+
let tag = Tag.String("\(child.value.dynamicType)")
151+
if let value = child.value as? _Injected {
152+
value._value = resolve(tag: tag) as Any
153+
}
154+
else if let weakValue = child.value as? _InjectedWeak {
155+
weakValue._value = resolve(tag: tag) as AnyObject
156+
}
157+
}
158+
}
159+
160+
}
161+
162+
//MARK: - Private
163+
164+
extension DependencyContainer {
165+
private typealias InjectedFactory = ()->Any
166+
private typealias InjectedWeakFactory = ()->AnyObject
167+
168+
private func injectedKey(T: Any.Type) -> DefinitionKey {
169+
return DefinitionKey(protocolType: Any.self, factoryType: InjectedFactory.self, associatedTag: Tag.String(injectedTag(T.self)))
170+
}
171+
172+
private func injectedWeakKey(T: Any.Type) -> DefinitionKey {
173+
return DefinitionKey(protocolType: AnyObject.self, factoryType: InjectedWeakFactory.self, associatedTag: Tag.String(injectedWeakTag(T.self)))
174+
}
175+
176+
func registerInjected<T, F>(definition: DefinitionOf<T, F>) {
177+
guard let definition = definition.injectedDefinition else { return }
178+
definitions[injectedKey(T.self)] = definition
179+
}
180+
181+
func registerInjectedWeak<T, F>(definition: DefinitionOf<T, F>) {
182+
guard let definition = definition.injectedWeakDefinition else { return }
183+
definitions[injectedWeakKey(T.self)] = definition
184+
}
185+
186+
func removeInjected<T, F>(definition: DefinitionOf<T, F>) {
187+
guard definition.injectedDefinition != nil else { return }
188+
definitions[injectedKey(T.self)] = nil
189+
}
190+
191+
func removeInjectedWeak<T, F>(definition: DefinitionOf<T, F>) {
192+
guard definition.injectedWeakDefinition != nil else { return }
193+
definitions[injectedWeakKey(T.self)] = nil
194+
}
195+
196+
}
197+
198+
func injectedTag(T: Any.Type) -> String {
199+
return "Injected<\(T.self)>"
200+
}
201+
202+
func injectedWeakTag(T: Any.Type) -> String {
203+
return "InjectedWeak<\(T.self)>"
204+
}
205+
206+
protocol _Injected: class {
207+
var _value: Any? { get set }
208+
}
209+
210+
protocol _InjectedWeak: class {
211+
weak var _value: AnyObject? { get set }
212+
}
213+

Diff for: Dip/Dip/Definition.swift

+33-14
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222
// THE SOFTWARE.
2323
//
2424

25-
import Foundation
26-
2725
///Internal representation of a key used to associate definitons and factories by tag, type and factory.
2826
struct DefinitionKey : Hashable, Equatable, CustomDebugStringConvertible {
2927
var protocolType: Any.Type
@@ -64,7 +62,7 @@ public enum ComponentScope {
6462

6563
For example `DefinitionOf<Service,(String)->Service>` is the type of definition that during resolution will produce instance of type `Service` using closure that accepts `String` argument.
6664
*/
67-
public struct DefinitionOf<T, F>: Definition {
65+
public final class DefinitionOf<T, F>: Definition {
6866

6967
/**
7068
Sets the block that will be used to resolve dependencies of the component.
@@ -92,10 +90,8 @@ public struct DefinitionOf<T, F>: Definition {
9290
guard resolveDependenciesBlock == nil else {
9391
fatalError("You can not change resolveDependencies block after it was set.")
9492
}
95-
var newDefinition = self
96-
newDefinition.resolveDependenciesBlock = block
97-
container.register(newDefinition)
98-
return newDefinition
93+
self.resolveDependenciesBlock = block
94+
return self
9995
}
10096

10197
let factory: F
@@ -107,23 +103,46 @@ public struct DefinitionOf<T, F>: Definition {
107103
self.factory = factory
108104
self.scope = scope
109105
self.tag = tag
106+
107+
if let factory = factory as? ()->T where tag == nil {
108+
injectedDefinition = DefinitionOf<Any, ()->Any>(factory: { factory() }, scope: scope, tag: DependencyContainer.Tag.String(injectedTag(T.self)))
109+
110+
injectedWeakDefinition = DefinitionOf<AnyObject, ()->AnyObject>(factory: {
111+
guard let result = factory() as? AnyObject else {
112+
fatalError("\(T.self) can not be casted to AnyObject. InjectedWeak wrapper should be used to wrap only classes.")
113+
}
114+
return result
115+
}, scope: scope, tag: DependencyContainer.Tag.String(injectedWeakTag(T.self)))
116+
}
117+
110118
}
111119

112120
///Will be stored only if scope is `Singleton`
113121
var resolvedInstance: T? {
114122
get {
115123
guard scope == .Singleton else { return nil }
116-
return _resolvedInstance
124+
125+
return _resolvedInstance ??
126+
injectedDefinition?._resolvedInstance as? T ??
127+
injectedWeakDefinition?._resolvedInstance as? T
128+
}
129+
set {
130+
guard scope == .Singleton else { return }
131+
132+
_resolvedInstance = newValue
133+
injectedDefinition?._resolvedInstance = newValue
134+
injectedWeakDefinition?._resolvedInstance = newValue as? AnyObject
117135
}
118-
}
119-
120-
mutating func resolvedInstance(container: DependencyContainer, tag: DependencyContainer.Tag? = nil, instance: T) {
121-
guard scope == .Singleton else { return }
122-
_resolvedInstance = instance
123-
container.register(self)
124136
}
125137

126138
private var _resolvedInstance: T?
139+
140+
///Accessory definition used to auto-inject strong properties
141+
var injectedDefinition: DefinitionOf<Any,()->Any>?
142+
143+
///Accessory definition used to auto-inject weak properties
144+
var injectedWeakDefinition: DefinitionOf<AnyObject,()->AnyObject>?
145+
127146
}
128147

129148
///Dummy protocol to store definitions for different types in collection

0 commit comments

Comments
 (0)