Skip to content

Commit efeede4

Browse files
author
Hadevs
committed
reworked project
0 parents  commit efeede4

32 files changed

+1406
-0
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019 Danil Kovalev
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
![alt text](header.png)
2+
3+
# SwiftInjector
4+
[![Swift](https://img.shields.io/badge/swift-language-orange.svg)](https://swift.org)
5+
[![Swift](https://img.shields.io/badge/dependency--injection-convinient-brightgreen.svg)]()
6+
[![GitHub license](https://img.shields.io/badge/License-MIT-lightgrey.svg)](https://raw.githubusercontent.com/Boerworz/Gagat/master/LICENSE)
7+
8+
9+
SwiftInjector - library for dependency injection, maden for convinient and fast properties connection
10+
11+
## Things already implemented:
12+
1. Auto registration and autoresolving
13+
Just for example:
14+
![alt text](example_1.png)
15+
![alt text](example_2.png)
16+
![alt text](example_3.png)
17+
18+
2. Works even with private properties!
19+
3. Resolving and registration with 'name' parameter
20+
4. Classes each-to-each references without recursive crash (like in Swinject or DIP libraries)
21+
22+
## Things TO DO for first release
23+
1. Add manual registration and resolving
24+
2. Add certain registration by String Key or specific arguments
25+
3. Perfomance test
26+
4. 70% Code coverage
27+
5. Documentation
28+
29+
## Rules
30+
1. Properties will be automatically injected only on your class, not your parent classes.
31+
![alt text](footer.png)

SwiftInjector.xcodeproj/project.pbxproj

Lines changed: 529 additions & 0 deletions
Large diffs are not rendered by default.

SwiftInjector.xcodeproj/project.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>SchemeUserState</key>
6+
<dict>
7+
<key>SwiftInjector.xcscheme_^#shared#^_</key>
8+
<dict>
9+
<key>orderHint</key>
10+
<integer>0</integer>
11+
</dict>
12+
</dict>
13+
</dict>
14+
</plist>

SwiftInjector/Container.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// Container.swift
3+
// SwiftInjector
4+
//
5+
// Created by Ghost on 30.06.2019.
6+
// Copyright © 2019 Ghost. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
protocol Container: class {
12+
var parentContainer: Containerable { get set }
13+
func register()
14+
}
15+
16+
extension Container {
17+
func resolve<T: Containerable.Object>() -> T? {
18+
return parentContainer.resolve()
19+
}
20+
}

SwiftInjector/Containerable.swift

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
//
2+
// Containerable.swift
3+
// SwiftInjector
4+
//
5+
// Created by Ghost on 30.06.2019.
6+
// Copyright © 2019 Ghost. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
protocol Containerable: class {
12+
typealias Object = AnyObject
13+
typealias ServiceName = String
14+
typealias Service = (() -> Object)
15+
typealias RelationIn = String
16+
typealias RelationOut = String
17+
18+
var dispatchRegistrationGroup: DispatchGroup { get }
19+
20+
var services: [ServiceName: [ContainerObject]] { get set }
21+
var relations: [RelationIn: [RelationOut]] { get set }
22+
var recursiveNotResolvedObjects: [Object] { get set }
23+
}
24+
25+
struct ContainerObject {
26+
enum RegistrationType {
27+
case manual
28+
case automatic
29+
}
30+
var name: String? = nil
31+
var registrationType: RegistrationType
32+
var registration: Containerable.Service
33+
var object: Containerable.Object? = nil
34+
init(_ registration: @escaping Containerable.Service,
35+
name: String? = nil,
36+
registrationType: RegistrationType) {
37+
self.registration = registration
38+
self.name = name
39+
self.registrationType = registrationType
40+
}
41+
}
42+
43+
44+
//MARK: TODO: MOVE IT TO DIContainer.swift
45+
extension Containerable {
46+
private func resolveAny(typeString: String, name: String? = nil) -> Object? {
47+
let array: [ContainerObject]? = {
48+
if let filterName = name {
49+
return services[typeString]?.filter { $0.name == filterName }
50+
} else {
51+
return services[typeString]
52+
}
53+
}()
54+
55+
if (array?.count ?? 0) > 1 {
56+
SILogger("Warning in \(typeString) resolving. You registered two different objects for this type. Try to provide \"name\" argument while registering your object. But you will receive first object of all services you registered.").log(priority: .high)
57+
}
58+
59+
if let object = array?.first?.object {
60+
print(1)
61+
return object
62+
} else if let object = array?.first?.registration() {
63+
64+
var objects = services[typeString]
65+
if objects?.count ?? 0 > 0 {
66+
objects?[0].object = object
67+
}
68+
69+
services[typeString] = objects ?? []
70+
if array?.first?.registrationType == .automatic {
71+
autoresolve(on: object)
72+
finishRegistrations()
73+
}
74+
75+
return object
76+
} else {
77+
return nil
78+
}
79+
}
80+
81+
func resolve<T: Object>(name: String? = nil) -> T? {
82+
let key = String(describing: T.self)
83+
let object = resolveAny(typeString: key, name: name) as? T
84+
85+
return object
86+
}
87+
88+
func finishRegistrations() {
89+
relations = [:]
90+
for object in recursiveNotResolvedObjects {
91+
autoresolve(on: object)
92+
}
93+
}
94+
95+
private func autoresolve<T: Object>(on object: T) {
96+
let mirror = Mirror(reflecting: object)
97+
98+
for attr in mirror.children {
99+
if let property_name = attr.label {
100+
let objectType = type(of: attr.value)
101+
let typeString = String(describing: objectType).replacingOccurrences(of: "Optional<", with: "").replacingOccurrences(of: ">", with: "")
102+
103+
// MARK: MUTATE VARIABLE (DANGER OBJC RUNTIME ZONE)
104+
if let ivar = class_getInstanceVariable(type(of: object), property_name) {
105+
let typeStringOfMyObject = formattedString(of: object)
106+
recordRelations(relationIn: typeString, relationOut: typeStringOfMyObject)
107+
if relations[typeStringOfMyObject]?.contains(typeString) ?? false {
108+
relations[typeString]?.removeAll(where: { (string) -> Bool in
109+
return string == typeStringOfMyObject
110+
})
111+
recursiveNotResolvedObjects.append(object)
112+
return
113+
}
114+
if let value = resolveAny(typeString: typeString) {
115+
object_setIvar(object, ivar, value)
116+
}
117+
}
118+
}
119+
}
120+
}
121+
122+
private func memmoryAddress(_ object: Object) -> String {
123+
return "\(Unmanaged.passUnretained(object).toOpaque())"
124+
}
125+
126+
127+
// MARK: *TODO: Unable to test*
128+
private func recordRelations(relationIn: RelationIn, relationOut: RelationOut) {
129+
if let relationsOut = relations[relationIn], !relationsOut.contains(relationOut) {
130+
var newRelationsOut = relationsOut
131+
newRelationsOut.append(relationOut)
132+
relations[relationIn] = newRelationsOut
133+
} else {
134+
relations[relationIn] = [relationOut]
135+
}
136+
}
137+
138+
// MARK: *TESTED*
139+
func formattedString<T: Object>(of object: T) -> String {
140+
return String(describing: type(of: object)).replacingOccurrences(of: "Optional<", with: "").replacingOccurrences(of: ">", with: "")
141+
}
142+
143+
// MARK: *TESTED*
144+
func property<T>(object: Object, propertyName: String) -> T? {
145+
let mirror = Mirror(reflecting: object)
146+
for attr in mirror.children {
147+
if attr.label == propertyName {
148+
return attr.value as? T
149+
}
150+
}
151+
152+
return nil
153+
}
154+
155+
// MARK: *TESTED*
156+
// Returns only first variable of type
157+
func propertyName(by typeString: String, in object: Object) -> String? {
158+
let mirror = Mirror(reflecting: object)
159+
for attr in mirror.children {
160+
let propertyType = type(of: attr.value)
161+
let typeStringInside = String(describing: propertyType).replacingOccurrences(of: "Optional<", with: "").replacingOccurrences(of: ">", with: "")
162+
if typeStringInside == typeString {
163+
return attr.label
164+
}
165+
}
166+
167+
return nil
168+
}
169+
170+
// MARK: *TESTED*
171+
func register<T: Object>(_ registration: @escaping (() -> T),
172+
name: String? = nil,
173+
registrationType: ContainerObject.RegistrationType = .automatic) {
174+
dispatchRegistrationGroup.enter()
175+
let object = registration()
176+
let key = String(describing: type(of: object))
177+
if let array = services[key] {
178+
var newArray = array
179+
newArray.append(ContainerObject(registration, name: name, registrationType: registrationType))
180+
services[key] = newArray
181+
} else {
182+
services[key] = [ContainerObject(registration, name: name, registrationType: registrationType)]
183+
}
184+
dispatchRegistrationGroup.leave()
185+
}
186+
}

SwiftInjector/DIContainer.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//
2+
// DIContainer.swift
3+
// SwiftInjector
4+
//
5+
// Created by Ghost on 30.06.2019.
6+
// Copyright © 2019 Ghost. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
class DIContainer: Container {
12+
var parentContainer: Containerable
13+
14+
init(parentContainer: Containerable? = nil) {
15+
self.parentContainer = parentContainer ?? RootContainer()
16+
self.register()
17+
}
18+
19+
final func register<T: Containerable.Object>(registrationType: ContainerObject.RegistrationType = .automatic,
20+
name: String? = nil,
21+
_ registration: @escaping (() -> T)) {
22+
parentContainer.register(registration, name: name, registrationType: registrationType)
23+
}
24+
25+
final func resolve<T: Containerable.Object>(name: String? = nil) -> T? {
26+
return parentContainer.resolve(name: name)
27+
}
28+
29+
func register() {
30+
// Mark: leave empty only in this class
31+
}
32+
}

0 commit comments

Comments
 (0)