Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ...

like image 889
fabrizioM Avatar asked Jun 11 '15 01:06

fabrizioM


People also ask

What is the alternative to dependency injection?

An alternative to dependency injection is using a service locator. The service locator design pattern also improves decoupling of classes from concrete dependencies. You create a class known as the service locator that creates and stores dependencies and then provides those dependencies on demand.

Which choice is best of using dependency injection?

Constructor injection should be the main way that you do dependency injection. It's simple: A class needs something and thus asks for it before it can even be constructed. By using the guard pattern, you can use the class with confidence, knowing that the field variable storing that dependency will be a valid instance.

Is it good to use dependency injection?

The dependency injection technique enables you to improve this even further. It provides a way to separate the creation of an object from its usage. By doing that, you can replace a dependency without changing any code and it also reduces the boilerplate code in your business logic.

Is dependency injection an overkill?

If you have a really small project with 12 classes, then a DI framework is almost certainly overkill. As a rule of thumb, the point where it becomes truly useful is when you find yourself repeatedly writing code that wires up object graphs with multiple dependencies and have to think about where to put that code.


1 Answers

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.

like image 175
Yoichi Tagaya Avatar answered Oct 20 '22 03:10

Yoichi Tagaya