Better understanding of Dependency Injection - Resolving New Instances? - swift

I have been working a job that requires me to focus on Dependency Injection. For posterity, I am using this in Swift/SwiftUI, though I believe my lack of understanding is more inherent in the concept than the language.
I have created a Dependency Injection container, which can be used to register and resolve types and components. As such;
protocol MyContainerProtocol {
func register<Component>(type: Component.Type, component: Any)
func resolve<Component>(type: Component.Type) -> Component?
}
final class MyContainer: MyContainerProtocol {
static let shared = DependencyContainer()
private init() { }
var components: [String: Any] = [:]
func register<Component>(type: Component.Type, component: Any) {
components["\(type)"] = component
}
func resolve<Component>(type: Component.Type) -> Component? {
return components["\(type)"] as? Component
}
}
This will become relevant below, but I have a class in my project, named VideoProcessor;
class VideoProcessor: SomeProtocol {
var codec: String
var format: String
init(codec: String, format: String) {
self.codec = codec
self.format = format
}
}
Early on in the app's lifecycle, I am registering the component. For example;
let container = DependencyContainer.shared
container.register(type: VideoProcessor.self, component: VideoProcessor(codec: "H264", format: "MP4"))
...
let processor = container.resolve(type: VideoProcessor.self)!
My Confusion: What is being asked of me is to resolve an instance of a type, without having to construct it when registering. Effectively, I'm being asked to resolve a new instance of a registered type each time it is resolved. In my mind, this means my code would resemble something like;
let container = DependencyContainer.shared
container.register(type: VideoProcessor.self)
...
let processorA = container.resolve(type: VideoProcessor.self)!
processorA.codec = "H264"
processorA.format = "MP4"
let processorB = container.resolve(type: VideoProcessor.self)!
processorB.codec = "H265"
processorB.format = "MOV"
However, VideoProcessor has its own dependencies, leading me to be unsure how I would register a type.
I'm unsure if my issue exists in the way my Dependency Container is built, the way my classes are built, or if the question of what's being asked of me I'm just not understanding. Even looking at popular Swift libraries like Swinject or DIP, I don't entirely see what my Container is doing improperly (or if this is where the Factory method comes in).

You would need to add an extra register function.
protocol MyContainerProtocol {
func register<Component>(type: Component.Type, component: Any)
func register<Component>(type: Component.Type, builder: #escaping (MyContainerProtocol) -> Component)
func resolve<Component>(type: Component.Type) -> Component?
}
final class MyContainer: MyContainerProtocol {
static let shared = MyContainer()
private init() { }
var components: [String: Any] = [:]
func register<Component>(type: Component.Type, component: Any) {
components["\(type)"] = component
}
func register<Component>(type: Component.Type, builder: #escaping (MyContainerProtocol) -> Component) {
components["\(type)"] = builder
}
func resolve<Component>(type: Component.Type) -> Component? {
if let singleton = components["\(type)"] as? Component {
return singleton
}
if let builder = components["\(type)"] as? (MyContainerProtocol) -> Component {
return builder(self)
}
return nil
}
}
Then it would look like this at the call site:
struct Animal {
let type: String
let id = UUID()
}
struct Person {
let name: String
let pet: Animal
let id = UUID()
}
class ComplicatedNetworkStack {
let id = UUID()
/// so much stuff in here
}
MyContainer.shared.register(type: Animal.self) { _ in Animal(type: "Dog") }
MyContainer.shared.register(type: Person.self) { container in
Person(
name: "Joe Dirt",
pet: container.resolve(type: Animal.self)!
)
}
MyContainer.shared.register(type: ComplicatedNetworkStack.self, component: ComplicatedNetworkStack())
If you were to run that code in a playground and resolve Person and Animal a few times, you'll see the UUID's are all different, while ComplicatedNetworkStack's id is the same.

Related

How to use type-erase in Swift

Story:
I have some layouts.
A layout have a pattern and keys. The layout can make message from these.
Each patterns have maximum number of keys.
That is my code to expression templates.
protocol LayoutPattern {
static var numberOfKeys: Int { get }
static func make(with keys: [String]) -> String
}
struct Pattern1: LayoutPattern {
static let numberOfKeys: Int = 1
static func make(with keys: [String]) -> String {
return "Pattern 1:" + keys.joined(separator: ",")
}
let value1: String
}
struct Pattern2: LayoutPattern {
static let numberOfKeys: Int = 2
static func make(with keys: [String]) -> String {
return "Pattern 2:" + keys.joined(separator: ",")
}
let value1: String
let value2: String
}
protocol LayoutProtocol {
associatedtype Pattern: LayoutPattern
var keys: [String] { get }
func make() -> String
}
struct Layout<T: LayoutPattern>: LayoutProtocol {
typealias Pattern = T
let keys: [String]
init(keys: [String]) {
assert(keys.count == Pattern.numberOfKeys)
self.keys = keys
}
func make() -> String {
return Pattern.make(with: keys)
}
}
let t1 = Layout<Pattern1>(keys: ["key1"])
t1.make() // Pattern 1: key1
let t2 = Layout<Pattern2>(keys: ["key1", "key2"])
t2.make() // Pattern 2: Key1,Key2
This is valid code.
But I can't write that:
class MyNote {
let layout: LayoutProtocol
}
I know that I should use a technique called type-erase like AnyPokemon!
I wrote that:
struct AnyLayout<T: LayoutPattern>: LayoutProtocol {
typealias Pattern = T
let keys: [String]
private let _make: () -> String
init<U: LayoutProtocol>(_ layout: U) where T == U.Pattern {
self.keys = layout.keys
self._make = { layout.make() }
}
func make() -> String {
_make()
}
}
let anyLayout = AnyLayout(Layout<Pattern2>(keys: ["key1", "key2"]))
anyLayout.make() // Pattern 2: Key1,Key2
This can be executed. But MyNote class can't still have a property as AnyLayout.
What should I do?
The issue is the addition of the associatedtype. It isn't doing any work here. Nothing relies on it. Remove it, and the issue goes away. Don't add associatedtypes until you have a specific requirement for them.
As a rule, if you think you need type-erasure, first ask if your protocol is designed correctly. There are definitely times that type erasers are needed, but they're far rarer than people expect.
If you have an algorithm that relies on Pattern, then show that, and we can discuss the way to build that. (There are many techniques, including using multiple protocols.)
It's also worth asking whether Layout needs to be generic here. Do you want Layout<Pattern1> to be a different type than Layout<Pattern2>? The fact that you're then trying to type-erase it suggests you don't. In that case, there's no reason for the extra generic layers. In your example, Layout isn't really doing any work. Again, you can probably just get rid of it. Let each pattern be its own thing and let Layout be a protocol that binds them with make():
protocol Layout {
func make() -> String
}
struct Pattern1: Layout {
let key: String
func make() -> String {
return "Pattern 1:" + key
}
}
struct Pattern2: Layout {
let keys: [String]
init(key1: String, key2: String) {
keys = [key1, key2]
}
func make() -> String {
return "Pattern 2:" + keys.joined(separator: ",")
}
}
let t1 = Pattern1(key: "key1")
t1.make() // Pattern 1: key1
let t2 = Pattern2(key1: "key1", key2: "key2")
t2.make() // Pattern 2: Key1,Key2
class MyNote {
let layout: Layout
init(layout: Layout) {
self.layout = layout
}
}
let note = MyNote(layout: t1)
This lets you make your Pattern initializers much stronger types. The need for an assert means you're not letting the types do the work. With the above design, you can't pass the wrong number of keys.

Protocol Composition for Dependency Injection - Causality Dilemma / Compile Issue

I am trying to use Swift "Protocol Composition" for dependency injection for the first time. There are various blog posts from well respected engineers in the field advocating this approach but I am unable to get the code to compile once there are dependencies that depend upon other dependencies.
The problem is that until your main concrete AllDependencies instance is initialised it cannot be used to initialise the child dependencies, but conversely you cannot create the child dependencies without the concrete AllDependencies instance.
Chicken & egg. Rock and hard place.
I'll try and provide the simplest example I can...
protocol HasAppInfo {
var appInfo: AppInfoProtocol { get }
}
protocol AppInfoProtocol {
var apiKey: String { get }
}
struct AppInfo: AppInfoProtocol {
let apiKey: String
}
protocol HasNetworking {
var networking: NetworkingProtocol { get }
}
protocol NetworkingProtocol {
func makeRequest()
}
class Networking: NetworkingProtocol {
typealias Dependencies = HasAppInfo
let dependencies: Dependencies
init(dependencies: Dependencies) {
self.dependencies = dependencies
}
func makeRequest() {
let apiKey = self.dependencies.appInfo.apiKey
// perform request sending API Key
// ...
}
}
class AllDependencies: HasAppInfo, HasNetworking {
let appInfo: AppInfoProtocol
let networking: NetworkingProtocol
init() {
self.appInfo = AppInfo(apiKey: "whatever")
/// **********************************************************
/// *** ERROR: Constant 'self.networking' used before being initialized
/// **********************************************************
self.networking = Networking(dependencies: self)
}
}
It seems like it might be possible to resolve this with use of lazy var, {get set} or mutating dependencies but that seems extermely unsafe because any code in your system can mutate your dependencies at will.
Would appreciate understanding how others have resolved what seems like a pretty fundamental issue with this approach.
References
http://merowing.info/2017/04/using-protocol-compositon-for-dependency-injection/
https://swiftwithmajid.com/2019/03/06/dependency-injection-in-swift-with-protocols/
You could use a private(set) lazy var:
private(set) lazy var networking: NetworkingProtocol = {
return Networking(dependencies: self)
}()
PS: I posted the original question. I have already accepted the lazy var answer, this is the simplest way forward, however I wanted to post this alternative solution that uses a generic DependencyFactory in case it helps others.
protocol Dependencies:
HasAppInfo &
HasNetworking
{}
class DependencyFactory {
typealias Factory<T> = (Dependencies) -> T
private enum DependencyState<T> {
case registered(Factory<T>)
case initialised(T)
}
private var dependencyStates = [String: Any]()
func register<T>(_ type: T.Type, factory: #escaping Factory<T>) {
dependencyStates[key(for: type)] = DependencyState<T>.registered(factory)
}
func unregister<T>(_ type: T.Type) {
dependencyStates[key(for: type)] = nil
}
func resolve<T>(_ type: T.Type, dependencies: Dependencies) -> T {
let key = self.key(for: type)
guard let dependencyState = dependencyStates[key] as? DependencyState<T> else {
fatalError("Attempt to access unregistered `\(type)` dependency")
}
switch dependencyState {
case let .registered(factoryClosure):
let dependency = factoryClosure(dependencies)
dependencyStates[key] = DependencyState<T>.initialised(dependency)
return dependency
case let .initialised(dependency):
return dependency
}
}
private func key<T>(for type: T.Type) -> String {
return String(reflecting: type)
}
}
class AllDependencies: Dependencies {
private let dependencyFactory = DependencyFactory()
init() {
dependencyFactory.register(AppInfoProtocol.self, factory: { dependencies in
return AppInfo(apiKey: "whatever")
})
dependencyFactory.register(NetworkingProtocol.self, factory: { dependencies in
return Networking(dependencies: dependencies)
})
}
var appInfo: AppInfo {
return dependencyFactory.resolve(AppInfoProtocol.self, dependencies: self)
}
var networking: Networking {
return dependencyFactory.resolve(NetworkingProtocol.self, dependencies: self)
}
}

Can you simultaneously define and instantiate implicit types in Swift?

Just messing around with the language thinking of how I want to structure some UserDefaults that automatically generate keys based on the hierarchy. That got me wondering... Is it possible to simultaneously define, and instantiate a type, like this?
let myUserSettings = {
let formatting = {
var lastUsedFormat:String
}
}
let lastUsedFormat = myUserSettings.formatting.lastUsedFormat
Note: I can't use statics because I specifically need instancing so nested structs/classes with static members will not work for my case.
Here's the closest thing I could come up with, but I hate that I have to create initializers to set the members. I'm hoping for something a little less verbose.
class DefaultsScope {
init(_ userDefaults:UserDefaults){
self.userDefaults = userDefaults
}
let userDefaults:UserDefaults
func keyForSelf(property:String = #function) -> String {
return "\(String(reflecting: self)).\(property)"
}
}
let sharedDefaults = SharedDefaults(UserDefaults(suiteName: "A")!)
class SharedDefaults : DefaultsScope {
override init(_ userDefaults:UserDefaults){
formatting = Formatting(userDefaults)
misc = Misc(userDefaults)
super.init(userDefaults)
}
let formatting:Formatting
class Formatting:DefaultsScope {
let maxLastUsedFormats = 5
fileprivate(set) var lastUsedFormats:[String]{
get { return userDefaults.stringArray(forKey:keyForSelf()) ?? [] }
set { userDefaults.set(newValue, forKey:keyForSelf()) }
}
func appendFormat(_ format:String) -> [String] {
var updatedListOfFormats = Array<String>(lastUsedFormats.suffix(maxLastUsedFormats - 1))
updatedListOfFormats.append(format)
lastUsedFormats = updatedListOfFormats
return updatedListOfFormats
}
}
let misc:Misc
class Misc:DefaultsScope {
var someBool:Bool{
get { return userDefaults.bool(forKey:keyForSelf()) }
set { userDefaults.set(newValue, forKey:keyForSelf()) }
}
}
}
So is there a simpler way?
Disclaimer: this is, probably, just an abstract solution that should not be used in real life :)
enum x {
enum y {
static func success() {
print("Success")
}
}
}
x.y.success()
Update: Sorry, folks, I can't stop experimenting. This one looks pretty awful :)
let x2= [
"y2": [
"success": {
print("Success")
}
]
]
x2["y2"]?["success"]?()
Update 2: One more try, this time with tuples. And since tuples must have at least two values, I had to add some dummies in there. Also, tuples cannot have mutating functions.
let x3 = (
y3: (
success: {
print("Success")
},
failure: {
print("Failure")
}
),
z3: 0
)
x3.y3.success()
How about you try nesting some swift structs?
struct x {
struct y {
static func success() {
print("success")
}
}
}
x.y.success()
You cannot have that kind of structure but you cant access y from inside x, since y is only visible inside the scope of x and so is success inside the scope of y. There is no way that you can access them from outside
One other alternative is to have higher order function like so, which return closure which is callable.
let x = {
{
{
print("Success")
}
}
}
let y = x()
let success = y()
success()
or
x()()()
The real world usage of higher order function for userdefaults could be something like this,
typealias StringType = (String) -> ((String) -> Void)
typealias IntType = (String) -> ((Int) -> Void)
typealias BoolType = (String) -> ((Bool) -> Void)
typealias StringValue = (String) -> String?
typealias IntValue = (String) -> Int?
typealias BoolValue = (String) -> Bool?
func userDefaults<T>(_ defaults: UserDefaults) -> (String) -> ((T) -> Void) {
return { key in
return { value in
defaults.setValue(value, forKey: key)
}
}
}
func getDefaultsValue<T>(_ defaults: UserDefaults) -> (String) -> T? {
return { key in
return defaults.value(forKey: key) as? T
}
}
let setStringDefaults: StringType = userDefaults(.standard)
setStringDefaults("Name")("Jack Jones")
setStringDefaults("Address")("Australia")
let setIntDefaults: IntType = userDefaults(.standard)
setIntDefaults("Age")(35)
setIntDefaults("Salary")(2000)
let setBoolDefaults: BoolType = userDefaults(.standard)
setBoolDefaults("Married")(false)
setBoolDefaults("Employed")(true)
let getStringValue: StringValue = getDefaultsValue(.standard)
let name = getStringValue("Name")
let address = getStringValue("Address")
let getIntValue: IntValue = getDefaultsValue(.standard)
let age = getIntValue("Age")
let salary = getIntValue("Salary")
let getBoolValue: BoolValue = getDefaultsValue(.standard)
let married = getBoolValue("Married")
let employed = getBoolValue("Employed")
I am not sure if you like the pattern, but it has some good use cases as you can see from below, setStringDefaults you can set strings value to string key and all of them are typesafe.
You can extend this for your use case. But, you could use struct as well and use imperative code, which could be easier to understand. I see beauty in this as well.
Ok, I think I've figured it out. This first class can go in some common library that you use for all your apps.
class SettingsScopeBase {
private init(){}
static func getKey(setting:String = #function) -> String {
return "\(String(reflecting:self)).\(setting)"
}
}
The next part is a pair of classes:
The 'Scoping' class where you define which user defaults instance to use (along with anything else you may want to specify for this particular settings instance)
The actual hierarchy that defines your settings
Here's the first. I'm setting this up for my shared settings between my application and it's extension:
class SharedSettingsScope : SettingsScopeBase{
static let defaults = UserDefaults(suiteName: "group.com.myco.myappgroup")!
}
And finally, here's how you 'set up' your hierarchy as well as how you implement the properties' bodies.
class SharedSettings:SharedSettingsScope{
class Formatting:SharedSettingsScope{
static var groupsOnWhitespaceOnlyLines:Bool{
get { return defaults.bool(forKey: getKey()) }
set { defaults.set(newValue, forKey: getKey()) }
}
}
}
And here's how you use them...
let x = SharedSettings.Formatting.groupsOnWhitespaceOnlyLines
// x = false
SharedSettings.Formatting.groupsOnWhitespaceOnlyLines = true
let y = SharedSettings.Formatting.groupsOnWhitespaceOnlyLines
// y = true
I'm going to see if I can refine/optimize it a little more, but this is pretty close to where I want to be. No hard-coded strings, keys defined by the hierarchy where they're used, and only setting the specific UserDefaults instance in one place.

Does injecting factory make sense in this scenario (Swinject)

What is the right (cleanest and most concise) way to have PetOwner that can at any later point in program create new instances of Cat?
Let's assume that createAnotherAnimal can be called by PetOwner itself after it gets response to some async request, therefore creating as many instances of Cat as needed at the time of creating PetOwner is not possible.
I solved the problem with injecting factory, but I am not convinced that it is the best way to tackle the problem, what are the alternatives in Swinject?
protocol AnimalType {
var name: String? { get set }
func sound() -> String
}
class Cat: AnimalType {
var name: String?
init(name: String?) {
self.name = name
}
func sound() -> String {
return "Meow!"
}
}
protocol PersonType {
func play() -> String
func createAnotherAnimal() -> Void
}
class PetOwner: PersonType {
var pets: [AnimalType] = []
let petFactory : AnimalFactory
init(petFactory : AnimalFactory) {
self.petFactory = petFactory
}
func createAnotherAnimal() {
let pet = petFactory.factoryMethod()
self.pets.append(pet)
}
func play() -> String {
if(pets.count>0) {
let pet : AnimalType = pets[0];
let name = pet.name ?? "someone"
return "I'm playing with \(name). \(pet.sound())"
} else {
return "No animals"
}
}
}
class AnimalFactory {
let factoryMethod : () -> AnimalType
init(factoryMethod: () -> AnimalType) {
self.factoryMethod = factoryMethod
}
}
// Create a container and register service and component pairs.
let container = Container()
container.register(AnimalType.self) { _ in Cat(name: "Mimi") }
container.register(PersonType.self) { r in PetOwner(petFactory: r.resolve(AnimalFactory.self)!) }
container.register(AnimalFactory.self){r in AnimalFactory(factoryMethod:{ () -> AnimalType in r.resolve(AnimalType.self)!}) }
// The person is resolved to a PetOwner with a Cat.
let person = container.resolve(PersonType.self)!
person.createAnotherAnimal()
print(person.play())

Is there a better way to do dependency injection in Swift than this ?

New to swift, I was trying to create a service registry:
class ServiceRegistry {
static var instance = ServiceRegistry()
private var registry = [String:AnyObject]()
private init(){}
func register<T>(key:T, value:AnyObject) {
self.registry["\(T.self)"] = value
}
func get<T>(_:T) -> AnyObject? {
return registry["\(T.self)"]
}
}
but is not super friendly:
Register:
ServiceRegistry.instance.register(CacheServiceProtocol.self, value:ImageCacheService())
Retrieve:
if let cache = ServiceRegistry.instance.get(CacheServiceProtocol) as? CacheServiceProtocol { ... }
Any better way ? It would be useful to get rid of the as? CacheServiceProtocol in the if let ...
Swinject is a dependency injection framework for Swift. In your case, you can use it without the cast with as?.
Register:
let container = Container()
container.register(CacheServiceProtocol.self) { _ in ImageCacheService() }
Retrieve:
let cache = container.resolve(CacheServiceProtocol.self)!
Here cache is inferred as CacheServiceProtocol type. The resolve method returns nil if the specified type is not registered. We know CacheServiceProtocol is already registered, so the force-unwrap with ! is used.
UPDATE
I didn't exactly answer to the question. An implementation to remove the cast is storing factory closures instead of values in the registry. Here is the example. I also modified the type of key.
class ServiceRegistry {
static var instance = ServiceRegistry()
private var registry = [String:Any]()
private init(){}
func register<T>(key:T.Type, factory: () -> T) {
self.registry["\(T.self)"] = factory
}
func get<T>(_:T.Type) -> T? {
let factory = registry["\(T.self)"] as? () -> T
return factory.map { $0() }
}
}
Register:
ServiceRegistry.instance.register(CacheServiceProtocol.self) {
return ImageCacheService()
}
Retrieve:
// The type of cache is CacheServiceProtocol? without a cast.
let cache = ServiceRegistry.instance.get(CacheServiceProtocol.self)
Using #autoclosure might be also good.
I see your attempt to implement Service Locator design pattern. It's not Dependency Injection itself, but these two patterns actually can supplement each other.
I did implement a Service Locator in Swift 2 as well and I'm pretty happy with the result. Take a look at my code here: ServiceLocator.swift (ready to use) or BasicServiceLocator.swift and LazyServiceLocator.swift (with usage examples).
Here is the basic concept:
protocol ServiceLocator {
func getService<T>(type: T.Type) -> T?
func getService<T>() -> T?
}
extension ServiceLocator {
func getService<T>() -> T? {
return getService(T)
}
}
func typeName(some: Any) -> String {
return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)"
}
final class BasicServiceLocator: ServiceLocator {
// Service registry
private lazy var reg: Dictionary<String, Any> = [:]
func addService<T>(instance: T) {
let key = typeName(T)
reg[key] = instance
//print("Service added: \(key) / \(typeName(service))")
}
func getService<T>(type: T.Type) -> T? {
return reg[typeName(T)] as? T
}
}
And demonstration:
// Services declaration
protocol S1 {
func f1() -> String
}
protocol S2 {
func f2() -> String
}
// Services imlementation
class S1Impl: S1 {
func f1() -> String {
return "S1 OK"
}
}
class S2Impl: S2 {
func f2() -> String {
return "S2 OK"
}
}
// Service Locator initialization
let sl: ServiceLocator = {
let sl = BasicServiceLocator()
sl.addService(S1Impl() as S1)
sl.addService(S2Impl() as S2)
return sl
}()
// Test run
let s1 = sl.getService(S1)
let s2: S2? = sl.getService(S2)
print(s1?.f1() ?? "S1 NOT FOUND") // S1 OK
print(s2?.f2() ?? "S2 NOT FOUND") // S2 OK
As one of the other posters pointed out, the Service Locator pattern is not actually DI. Some would even go so far as to say it's an anti-pattern.
As a general answer to your question - I do believe first class DI is a better way to accomplish the above. My suggestion would be to use Typhoon but there are several other DI libs available for Swift such as Cleanse which looks very promising.