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

New to fast, I tried to create a registry of services:

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 not very friendly:

Registration:

  ServiceRegistry.instance.register(CacheServiceProtocol.self, value:ImageCacheService()) 

Download:

 if let cache = ServiceRegistry.instance.get(CacheServiceProtocol) as? CacheServiceProtocol { ... } 

Any better way? Would it be helpful to get rid of as? CacheServiceProtocol as? CacheServiceProtocol in if let ...

+6
source share
3 answers

Swinject is an injection environment for Swift. In your case, you can use it without translation from as? .

Registration:

 let container = Container() container.register(CacheServiceProtocol.self) { _ in ImageCacheService() } 

Download:

 let cache = container.resolve(CacheServiceProtocol.self)! 

Here cache is output as CacheServiceProtocol . The resolve method returns nil if the specified type is not registered. We know that the CacheServiceProtocol already registered, so a forced U-turn is used with ! .

UPDATE

I definitely did not answer the question. The implementation to remove the throw stores the factory closure instead of the values ​​in the registry . Here is an example. I also changed the key type.

 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() } } } 

Registration:

 ServiceRegistry.instance.register(CacheServiceProtocol.self) { return ImageCacheService() } 

Download:

 // The type of cache is CacheServiceProtocol? without a cast. let cache = ServiceRegistry.instance.get(CacheServiceProtocol.self) 

Using @autoclosure can also be good.

+6
source

I see your attempt to implement the Locator design pattern. This is not an injection dependency, but these two patterns can really complement each other.

I also used the Service Locator in Swift 2, and I am very pleased 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 a 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 
+1
source

As one of the other posters pointed out, the Locator service template is not actually a DI. Some even went so far as to say this is an anti-pattern .

As a general answer to your question - I believe that the first DI class is the best way to accomplish the above. My suggestion would be to use Typhoon , but there are several other DI libs for Swift, like Cleanse, which looks very promising.

0
source

Source: https://habr.com/ru/post/988881/


All Articles