|
| 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 | +} |
0 commit comments