-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Hadevs
committed
Jul 8, 2019
0 parents
commit efeede4
Showing
32 changed files
with
1,406 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2019 Danil Kovalev | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
![alt text](header.png) | ||
|
||
# SwiftInjector | ||
[![Swift](https://img.shields.io/badge/swift-language-orange.svg)](https://swift.org) | ||
[![Swift](https://img.shields.io/badge/dependency--injection-convinient-brightgreen.svg)]() | ||
[![GitHub license](https://img.shields.io/badge/License-MIT-lightgrey.svg)](https://raw.githubusercontent.com/Boerworz/Gagat/master/LICENSE) | ||
|
||
|
||
SwiftInjector - library for dependency injection, maden for convinient and fast properties connection | ||
|
||
## Things already implemented: | ||
1. Auto registration and autoresolving | ||
Just for example: | ||
![alt text](example_1.png) | ||
![alt text](example_2.png) | ||
![alt text](example_3.png) | ||
|
||
2. Works even with private properties! | ||
3. Resolving and registration with 'name' parameter | ||
4. Classes each-to-each references without recursive crash (like in Swinject or DIP libraries) | ||
|
||
## Things TO DO for first release | ||
1. Add manual registration and resolving | ||
2. Add certain registration by String Key or specific arguments | ||
3. Perfomance test | ||
4. 70% Code coverage | ||
5. Documentation | ||
|
||
## Rules | ||
1. Properties will be automatically injected only on your class, not your parent classes. | ||
![alt text](footer.png) |
Large diffs are not rendered by default.
Oops, something went wrong.
7 changes: 7 additions & 0 deletions
7
SwiftInjector.xcodeproj/project.xcworkspace/contents.xcworkspacedata
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
8 changes: 8 additions & 0 deletions
8
SwiftInjector.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>IDEDidComputeMac32BitWarning</key> | ||
<true/> | ||
</dict> | ||
</plist> |
Binary file added
BIN
+18.8 KB
...xcodeproj/project.xcworkspace/xcuserdata/ghost.xcuserdatad/UserInterfaceState.xcuserstate
Binary file not shown.
14 changes: 14 additions & 0 deletions
14
SwiftInjector.xcodeproj/xcuserdata/ghost.xcuserdatad/xcschemes/xcschememanagement.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>SchemeUserState</key> | ||
<dict> | ||
<key>SwiftInjector.xcscheme_^#shared#^_</key> | ||
<dict> | ||
<key>orderHint</key> | ||
<integer>0</integer> | ||
</dict> | ||
</dict> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// | ||
// Container.swift | ||
// SwiftInjector | ||
// | ||
// Created by Ghost on 30.06.2019. | ||
// Copyright © 2019 Ghost. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
protocol Container: class { | ||
var parentContainer: Containerable { get set } | ||
func register() | ||
} | ||
|
||
extension Container { | ||
func resolve<T: Containerable.Object>() -> T? { | ||
return parentContainer.resolve() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// | ||
// Containerable.swift | ||
// SwiftInjector | ||
// | ||
// Created by Ghost on 30.06.2019. | ||
// Copyright © 2019 Ghost. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
protocol Containerable: class { | ||
typealias Object = AnyObject | ||
typealias ServiceName = String | ||
typealias Service = (() -> Object) | ||
typealias RelationIn = String | ||
typealias RelationOut = String | ||
|
||
var dispatchRegistrationGroup: DispatchGroup { get } | ||
|
||
var services: [ServiceName: [ContainerObject]] { get set } | ||
var relations: [RelationIn: [RelationOut]] { get set } | ||
var recursiveNotResolvedObjects: [Object] { get set } | ||
} | ||
|
||
struct ContainerObject { | ||
enum RegistrationType { | ||
case manual | ||
case automatic | ||
} | ||
var name: String? = nil | ||
var registrationType: RegistrationType | ||
var registration: Containerable.Service | ||
var object: Containerable.Object? = nil | ||
init(_ registration: @escaping Containerable.Service, | ||
name: String? = nil, | ||
registrationType: RegistrationType) { | ||
self.registration = registration | ||
self.name = name | ||
self.registrationType = registrationType | ||
} | ||
} | ||
|
||
|
||
//MARK: TODO: MOVE IT TO DIContainer.swift | ||
extension Containerable { | ||
private func resolveAny(typeString: String, name: String? = nil) -> Object? { | ||
let array: [ContainerObject]? = { | ||
if let filterName = name { | ||
return services[typeString]?.filter { $0.name == filterName } | ||
} else { | ||
return services[typeString] | ||
} | ||
}() | ||
|
||
if (array?.count ?? 0) > 1 { | ||
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) | ||
} | ||
|
||
if let object = array?.first?.object { | ||
print(1) | ||
return object | ||
} else if let object = array?.first?.registration() { | ||
|
||
var objects = services[typeString] | ||
if objects?.count ?? 0 > 0 { | ||
objects?[0].object = object | ||
} | ||
|
||
services[typeString] = objects ?? [] | ||
if array?.first?.registrationType == .automatic { | ||
autoresolve(on: object) | ||
finishRegistrations() | ||
} | ||
|
||
return object | ||
} else { | ||
return nil | ||
} | ||
} | ||
|
||
func resolve<T: Object>(name: String? = nil) -> T? { | ||
let key = String(describing: T.self) | ||
let object = resolveAny(typeString: key, name: name) as? T | ||
|
||
return object | ||
} | ||
|
||
func finishRegistrations() { | ||
relations = [:] | ||
for object in recursiveNotResolvedObjects { | ||
autoresolve(on: object) | ||
} | ||
} | ||
|
||
private func autoresolve<T: Object>(on object: T) { | ||
let mirror = Mirror(reflecting: object) | ||
|
||
for attr in mirror.children { | ||
if let property_name = attr.label { | ||
let objectType = type(of: attr.value) | ||
let typeString = String(describing: objectType).replacingOccurrences(of: "Optional<", with: "").replacingOccurrences(of: ">", with: "") | ||
|
||
// MARK: MUTATE VARIABLE (DANGER OBJC RUNTIME ZONE) | ||
if let ivar = class_getInstanceVariable(type(of: object), property_name) { | ||
let typeStringOfMyObject = formattedString(of: object) | ||
recordRelations(relationIn: typeString, relationOut: typeStringOfMyObject) | ||
if relations[typeStringOfMyObject]?.contains(typeString) ?? false { | ||
relations[typeString]?.removeAll(where: { (string) -> Bool in | ||
return string == typeStringOfMyObject | ||
}) | ||
recursiveNotResolvedObjects.append(object) | ||
return | ||
} | ||
if let value = resolveAny(typeString: typeString) { | ||
object_setIvar(object, ivar, value) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
private func memmoryAddress(_ object: Object) -> String { | ||
return "\(Unmanaged.passUnretained(object).toOpaque())" | ||
} | ||
|
||
|
||
// MARK: *TODO: Unable to test* | ||
private func recordRelations(relationIn: RelationIn, relationOut: RelationOut) { | ||
if let relationsOut = relations[relationIn], !relationsOut.contains(relationOut) { | ||
var newRelationsOut = relationsOut | ||
newRelationsOut.append(relationOut) | ||
relations[relationIn] = newRelationsOut | ||
} else { | ||
relations[relationIn] = [relationOut] | ||
} | ||
} | ||
|
||
// MARK: *TESTED* | ||
func formattedString<T: Object>(of object: T) -> String { | ||
return String(describing: type(of: object)).replacingOccurrences(of: "Optional<", with: "").replacingOccurrences(of: ">", with: "") | ||
} | ||
|
||
// MARK: *TESTED* | ||
func property<T>(object: Object, propertyName: String) -> T? { | ||
let mirror = Mirror(reflecting: object) | ||
for attr in mirror.children { | ||
if attr.label == propertyName { | ||
return attr.value as? T | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// MARK: *TESTED* | ||
// Returns only first variable of type | ||
func propertyName(by typeString: String, in object: Object) -> String? { | ||
let mirror = Mirror(reflecting: object) | ||
for attr in mirror.children { | ||
let propertyType = type(of: attr.value) | ||
let typeStringInside = String(describing: propertyType).replacingOccurrences(of: "Optional<", with: "").replacingOccurrences(of: ">", with: "") | ||
if typeStringInside == typeString { | ||
return attr.label | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// MARK: *TESTED* | ||
func register<T: Object>(_ registration: @escaping (() -> T), | ||
name: String? = nil, | ||
registrationType: ContainerObject.RegistrationType = .automatic) { | ||
dispatchRegistrationGroup.enter() | ||
let object = registration() | ||
let key = String(describing: type(of: object)) | ||
if let array = services[key] { | ||
var newArray = array | ||
newArray.append(ContainerObject(registration, name: name, registrationType: registrationType)) | ||
services[key] = newArray | ||
} else { | ||
services[key] = [ContainerObject(registration, name: name, registrationType: registrationType)] | ||
} | ||
dispatchRegistrationGroup.leave() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// | ||
// DIContainer.swift | ||
// SwiftInjector | ||
// | ||
// Created by Ghost on 30.06.2019. | ||
// Copyright © 2019 Ghost. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
class DIContainer: Container { | ||
var parentContainer: Containerable | ||
|
||
init(parentContainer: Containerable? = nil) { | ||
self.parentContainer = parentContainer ?? RootContainer() | ||
self.register() | ||
} | ||
|
||
final func register<T: Containerable.Object>(registrationType: ContainerObject.RegistrationType = .automatic, | ||
name: String? = nil, | ||
_ registration: @escaping (() -> T)) { | ||
parentContainer.register(registration, name: name, registrationType: registrationType) | ||
} | ||
|
||
final func resolve<T: Containerable.Object>(name: String? = nil) -> T? { | ||
return parentContainer.resolve(name: name) | ||
} | ||
|
||
func register() { | ||
// Mark: leave empty only in this class | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>CFBundleDevelopmentRegion</key> | ||
<string>$(DEVELOPMENT_LANGUAGE)</string> | ||
<key>CFBundleExecutable</key> | ||
<string>$(EXECUTABLE_NAME)</string> | ||
<key>CFBundleIdentifier</key> | ||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
<key>CFBundleInfoDictionaryVersion</key> | ||
<string>6.0</string> | ||
<key>CFBundleName</key> | ||
<string>$(PRODUCT_NAME)</string> | ||
<key>CFBundlePackageType</key> | ||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>1.0</string> | ||
<key>CFBundleVersion</key> | ||
<string>$(CURRENT_PROJECT_VERSION)</string> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// | ||
// Container.swift | ||
// SwiftInjector | ||
// | ||
// Created by Ghost on 30.06.2019. | ||
// Copyright © 2019 Ghost. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
class RootContainer: Containerable { | ||
var recursiveNotResolvedObjects: [Object] = [] | ||
|
||
var relations: [RelationIn: [RelationOut]] = [:] | ||
var services: [ServiceName: [ContainerObject]] = [:] | ||
|
||
var dispatchRegistrationGroup: DispatchGroup = DispatchGroup() | ||
|
||
|
||
init() { | ||
let date = Date() | ||
dispatchRegistrationGroup.notify(queue: .main) { | ||
|
||
self.finishRegistrations() | ||
let seconds = Date().timeIntervalSince(date) | ||
print("All properties have been successfully injected for \(seconds) seconds.") | ||
} | ||
} | ||
} |
Oops, something went wrong.