Skip to content

Commit 7a9c59d

Browse files
committed
added auto injection with tags
1 parent 92f80e5 commit 7a9c59d

File tree

4 files changed

+134
-46
lines changed

4 files changed

+134
-46
lines changed

Dip/Dip/AutoInjection.swift

+83-29
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,6 @@ class ClientImp: Client {
4646
*/
4747
public final class Injected<T>: _InjectedPropertyBox {
4848

49-
static var tag: DependencyContainer.Tag {
50-
return .String("\(Injected<T>.self)")
51-
}
52-
5349
var _value: Any?
5450

5551
public var value: T? {
@@ -61,7 +57,11 @@ public final class Injected<T>: _InjectedPropertyBox {
6157
}
6258
}
6359

64-
public init() {}
60+
var tag: DependencyContainer.Tag?
61+
62+
public init(tag: DependencyContainer.Tag? = nil) {
63+
self.tag = tag
64+
}
6565

6666
}
6767

@@ -101,10 +101,6 @@ public final class InjectedWeak<T>: _InjectedWeakPropertyBox {
101101
//so we just rely on user reading documentation and passing AnyObject in runtime
102102
//also we will throw fatal error if type can not be casted to AnyObject during resolution
103103

104-
static var tag: DependencyContainer.Tag {
105-
return .String("\(InjectedWeak<T>.self)")
106-
}
107-
108104
weak var _value: AnyObject?
109105

110106
public var value: T? {
@@ -116,7 +112,11 @@ public final class InjectedWeak<T>: _InjectedWeakPropertyBox {
116112
}
117113
}
118114

119-
public init() {}
115+
var tag: DependencyContainer.Tag?
116+
117+
public init(tag: DependencyContainer.Tag? = nil) {
118+
self.tag = tag
119+
}
120120

121121
}
122122

@@ -166,60 +166,88 @@ typealias InjectedWeakFactory = ()->AnyObject
166166

167167
extension DependencyContainer {
168168

169-
func registerInjected(definition: AutoInjectedDefinition) {
170-
guard let key = definition.injectedKey,
171-
definition = definition.injectedDefinition else { return }
172-
definitions[key] = definition
169+
func registerInjected(definition: AutoInjectedDefinition, tag: Tag?) {
170+
guard let key = definition.injectedKey(tag),
171+
definitionToInject = definition.injectedDefinition else { return }
172+
definitions[key] = definitionToInject
173173
}
174174

175-
func registerInjectedWeak(definition: AutoInjectedDefinition) {
176-
guard let key = definition.injectedWeakKey,
177-
definition = definition.injectedWeakDefinition else { return }
178-
definitions[key] = definition
175+
func registerInjectedWeak(definition: AutoInjectedDefinition, tag: Tag?) {
176+
guard let key = definition.injectedWeakKey(tag),
177+
definitionToInject = definition.injectedWeakDefinition else { return }
178+
definitions[key] = definitionToInject
179179
}
180180

181-
func removeInjected(definition: AutoInjectedDefinition) {
181+
func removeInjected(definition: AutoInjectedDefinition, tag: Tag?) {
182182
guard definition.injectedDefinition != nil else { return }
183-
definitions[definition.injectedKey] = nil
183+
let key = definition.injectedKey(tag)
184+
definitions[key] = nil
184185
}
185186

186-
func removeInjectedWeak(definition: AutoInjectedDefinition) {
187+
func removeInjectedWeak(definition: AutoInjectedDefinition, tag: Tag?) {
187188
guard definition.injectedWeakDefinition != nil else { return }
188-
definitions[definition.injectedWeakKey] = nil
189+
let key = definition.injectedWeakKey(tag)
190+
definitions[key] = nil
189191
}
190192

191193
}
192194

193-
protocol _AutoInjectedPropertyBox {
195+
protocol _AutoInjectedPropertyBox: class {
194196
func resolve(container: DependencyContainer) throws
195-
static var tag: DependencyContainer.Tag { get }
197+
var tag: DependencyContainer.Tag? { get }
198+
static func tagForTag(tag: DependencyContainer.Tag?) -> DependencyContainer.Tag
199+
}
200+
201+
extension _AutoInjectedPropertyBox {
202+
203+
static func tagForTag(tag: DependencyContainer.Tag?) -> DependencyContainer.Tag {
204+
return .String(String(self)) + tag
205+
}
206+
207+
func _resolve<T>(container: DependencyContainer) throws -> T {
208+
do {
209+
return try container.resolve(tag: self.dynamicType.tagForTag(self.tag)) as T
210+
}
211+
catch {
212+
//fallback to definition with nil tag if needed
213+
if self.tag != nil {
214+
return try container.resolve(tag: self.dynamicType.tagForTag(nil)) as T
215+
}
216+
else {
217+
//rethrow error if instance tag is already nil
218+
throw error
219+
}
220+
}
221+
222+
}
223+
196224
}
197225

198-
protocol _InjectedPropertyBox: class, _AutoInjectedPropertyBox {
226+
protocol _InjectedPropertyBox: _AutoInjectedPropertyBox {
199227
var _value: Any? { get set }
200228
}
201229

202230
extension _InjectedPropertyBox {
203231
func resolve(container: DependencyContainer) throws {
204-
self._value = try container.resolve(tag: self.dynamicType.tag) as Any
232+
self._value = try _resolve(container) as Any
205233
}
206234
}
207235

208-
protocol _InjectedWeakPropertyBox: class, _AutoInjectedPropertyBox {
236+
protocol _InjectedWeakPropertyBox: _AutoInjectedPropertyBox {
209237
weak var _value: AnyObject? { get set }
210238
}
211239

212240
extension _InjectedWeakPropertyBox {
213241
func resolve(container: DependencyContainer) throws {
214-
self._value = try container.resolve(tag: self.dynamicType.tag) as AnyObject
242+
self._value = try _resolve(container) as AnyObject
215243
}
216244
}
217245

218246
func isInjectedTag(tag: DependencyContainer.Tag?) -> String? {
219247
guard let tag = tag else { return nil }
220248
guard case let .String(stringTag) = tag else { return nil }
221249

222-
return try! stringTag.match("^Injected(?:Weak){0,1}<\\((.+)\\)>$")?.first
250+
return try! stringTag.match("^Injected(?:Weak){0,1}<\\((.+)\\)>.*$")?.first
223251
}
224252

225253
extension String {
@@ -238,4 +266,30 @@ extension String {
238266
}
239267
}
240268

269+
func +(lhs: DependencyContainer.Tag, rhs: DependencyContainer.Tag?) -> DependencyContainer.Tag {
270+
guard let rhs = rhs else { return lhs }
271+
return lhs + rhs
272+
}
273+
274+
func +(lhs: DependencyContainer.Tag?, rhs: DependencyContainer.Tag) -> DependencyContainer.Tag {
275+
guard let lhs = lhs else { return rhs }
276+
return lhs + rhs
277+
}
278+
279+
func +(lhs: DependencyContainer.Tag, rhs: DependencyContainer.Tag) -> DependencyContainer.Tag {
280+
let lhsValue: String
281+
switch lhs {
282+
case let .String(stringValue): lhsValue = stringValue
283+
case let .Int(intValue): lhsValue = String(intValue)
284+
}
285+
286+
let rhsValue: String
287+
switch rhs {
288+
case let .String(stringValue): rhsValue = stringValue
289+
case let .Int(intValue): rhsValue = String(intValue)
290+
}
291+
292+
return .String("\(lhsValue);\(rhsValue)")
293+
}
294+
241295

Dip/Dip/Definition.swift

+18-7
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ public final class DefinitionOf<T, F>: Definition {
118118
if let factory = factory as? ()->T {
119119
injectedDefinition = DefinitionOf<Any, ()->Any>(factory: { factory() })
120120
injectedDefinition!.scope = scope
121-
injectedKey = DefinitionKey(protocolType: Any.self, factoryType: InjectedFactory.self, associatedTag: Injected<T>.tag)
122121

123122
injectedWeakDefinition = DefinitionOf<AnyObject, ()->AnyObject>(factory: {
124123
guard let result = factory() as? AnyObject else {
@@ -127,7 +126,6 @@ public final class DefinitionOf<T, F>: Definition {
127126
return result
128127
})
129128
injectedWeakDefinition!.scope = scope
130-
injectedWeakKey = DefinitionKey(protocolType: AnyObject.self, factoryType: InjectedWeakFactory.self, associatedTag: InjectedWeak<T>.tag)
131129
}
132130
}
133131

@@ -153,11 +151,20 @@ public final class DefinitionOf<T, F>: Definition {
153151

154152
///Accessory definition used to auto-inject strong properties
155153
private(set) var injectedDefinition: DefinitionOf<Any,()->Any>?
156-
private(set) var injectedKey: DefinitionKey?
157154

158155
///Accessory definition used to auto-inject weak properties
159156
private(set) var injectedWeakDefinition: DefinitionOf<AnyObject,()->AnyObject>?
160-
private(set) var injectedWeakKey: DefinitionKey?
157+
158+
159+
func injectedKey(tag: DependencyContainer.Tag?) -> DefinitionKey? {
160+
guard let _ = factory as? ()->T else { return nil }
161+
return DefinitionKey(protocolType: Any.self, factoryType: InjectedFactory.self, associatedTag: Injected<T>.tagForTag(tag))
162+
}
163+
164+
func injectedWeakKey(tag: DependencyContainer.Tag?) -> DefinitionKey? {
165+
guard let _ = factory as? ()->T else { return nil }
166+
return DefinitionKey(protocolType: AnyObject.self, factoryType: InjectedWeakFactory.self, associatedTag: InjectedWeak<T>.tagForTag(tag))
167+
}
161168

162169
}
163170

@@ -166,13 +173,17 @@ public protocol Definition: class { }
166173

167174
protocol AutoInjectedDefinition: Definition {
168175
var injectedDefinition: DefinitionOf<Any,()->Any>? { get }
169-
var injectedKey: DefinitionKey? { get }
176+
177+
func injectedKey(tag: DependencyContainer.Tag?) -> DefinitionKey?
170178

171179
var injectedWeakDefinition: DefinitionOf<AnyObject,()->AnyObject>? { get }
172-
var injectedWeakKey: DefinitionKey? { get }
180+
181+
func injectedWeakKey(tag: DependencyContainer.Tag?) -> DefinitionKey?
173182
}
174183

175-
extension DefinitionOf: AutoInjectedDefinition {}
184+
extension DefinitionOf: AutoInjectedDefinition {
185+
186+
}
176187

177188
extension DefinitionOf: CustomStringConvertible {
178189
public var description: String {

Dip/Dip/Dip.swift

+8-8
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ public final class DependencyContainer {
7373
public func remove(definition: Definition, forKey key: DefinitionKey) {
7474
definitions[key] = nil
7575
if let definition = definition as? AutoInjectedDefinition {
76-
removeInjected(definition)
77-
removeInjectedWeak(definition)
76+
removeInjected(definition, tag: key.associatedTag)
77+
removeInjectedWeak(definition, tag: key.associatedTag)
7878
}
7979
}
8080

@@ -151,9 +151,9 @@ public final class DependencyContainer {
151151
public func register(definition: Definition, forKey key: DefinitionKey) {
152152
definitions[key] = definition
153153

154-
if let definition = definition as? AutoInjectedDefinition where key.associatedTag == nil {
155-
registerInjected(definition)
156-
registerInjectedWeak(definition)
154+
if let definition = definition as? AutoInjectedDefinition {
155+
registerInjected(definition, tag: key.associatedTag)
156+
registerInjectedWeak(definition, tag: key.associatedTag)
157157
}
158158
}
159159

@@ -256,9 +256,9 @@ public final class DependencyContainer {
256256

257257
func storeResolvedInstance<T, F>(instance: T, forKey key: DefinitionKey?, definition: DefinitionOf<T, F>) {
258258
resolvedInstances[key] = instance
259-
if key != nil {
260-
resolvedInstances[definition.injectedKey] = instance
261-
resolvedInstances[definition.injectedWeakKey] = instance
259+
if let key = key {
260+
resolvedInstances[definition.injectedKey(key.associatedTag)] = instance
261+
resolvedInstances[definition.injectedWeakKey(key.associatedTag)] = instance
262262
}
263263
}
264264

Dip/DipTests/AutoInjectionTests.swift

+25-2
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ import XCTest
2727

2828
private protocol Server: class {
2929
weak var client: Client? {get}
30-
3130
var anotherClient: Client? {get set}
31+
32+
var name: String? { get }
3233
}
3334

3435
private protocol Client: class {
3536
var server: Server? {get}
3637
var anotherServer: Server? {get set}
38+
var taggedServer: Server? { get }
3739
}
3840

3941
class AutoInjectionTests: XCTestCase {
@@ -54,6 +56,8 @@ class AutoInjectionTests: XCTestCase {
5456
}
5557

5658
weak var anotherClient: Client?
59+
60+
var name: String?
5761
}
5862

5963
private class ClientImp: Client {
@@ -69,6 +73,11 @@ class AutoInjectionTests: XCTestCase {
6973
return _server.value
7074
}
7175

76+
var _taggedServer = Injected<Server>(tag: "tagged")
77+
var taggedServer: Server? {
78+
return _taggedServer.value
79+
}
80+
7281
}
7382

7483
let container = DependencyContainer()
@@ -82,6 +91,11 @@ class AutoInjectionTests: XCTestCase {
8291

8392
container.register(.ObjectGraph) { ServerImp() as Server }
8493
container.register(.ObjectGraph) { ClientImp() as Client }
94+
container.register(tag: "tagged", .ObjectGraph) { {
95+
let server = ServerImp()
96+
server.name = "tagged"
97+
return server
98+
}() as Server }
8599
}
86100

87101
func testThatItResolvesInjectedDependencies() {
@@ -183,6 +197,15 @@ class AutoInjectionTests: XCTestCase {
183197

184198
XCTAssertNil(weakClient)
185199
XCTAssertNil(server)
186-
187200
}
201+
202+
func testThatItInjectsTaggedDefinition() {
203+
let client = try! container.resolve() as Client
204+
let taggedServer = client.taggedServer
205+
XCTAssertTrue(taggedServer?.name == "tagged")
206+
207+
let server = client.server
208+
XCTAssertNil(server?.name)
209+
}
210+
188211
}

0 commit comments

Comments
 (0)