Last active
July 9, 2019 22:49
-
-
Save Revolucent/25daa75bda879dd20bb2 to your computer and use it in GitHub Desktop.
DependencyResolver - a simple, elegant, flexible dependency resolver for Swift 2.x
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
// | |
// DependencyResolver.swift | |
// | |
// Created by Gregory Higley on 2/15/16. | |
// Copyright © 2016 Revolucent LLC. | |
// | |
// This file is released under the MIT license. | |
// https://gist.github.com/Revolucent/25daa75bda879dd20bb2 | |
// | |
import Foundation | |
/** | |
A simple, elegant, flexible dependency resolver. | |
Registration uses a `type` and an optional `name`. The combination | |
of `type` and `name` must be unique. Registering the same `type` | |
and `name` **overwrites** the previous registration. | |
*/ | |
public struct DependencyResolver { | |
private struct Key: Hashable { | |
let type: String | |
let name: String? | |
init(type: String, name: String? = nil) { | |
self.type = type | |
self.name = name | |
// djb2 hash algorithm: http://www.cse.yorku.ca/~oz/hash.html | |
// &+ operator handles Int overflow | |
var hash = 5381 | |
hash = ((hash << 5) &+ hash) &+ type.hashValue | |
if let name = name { | |
hash = ((hash << 5) &+ hash) &+ name.hashValue | |
} | |
hashValue = hash | |
} | |
let hashValue: Int | |
} | |
private class Dependency { | |
private let create: Any -> Any | |
private let shared: Bool | |
private var instance: Any? | |
init<P, D>(shared: Bool, create: P -> D) { | |
self.shared = shared | |
self.create = { param in create(param as! P) } | |
} | |
func resolve<P, D>(parameters: P) -> D { | |
var result: D | |
if shared { | |
if instance == nil { | |
instance = create(parameters) | |
} | |
result = instance! as! D | |
} else { | |
result = create(parameters) as! D | |
} | |
return result | |
} | |
} | |
private static var dependencies = [Key: Dependency]() | |
private init() {} | |
/** | |
Registers `create` with the DependencyResolver. | |
- parameter type: Usually the type of `D`, but can be any string. | |
- parameter name: An optional name to disambiguate similar `type`s. | |
- parameter shared: Whether or not the instance is lazily created and then shared. | |
- parameter create: The lambda to register with the DependencyResolver. | |
*/ | |
public static func register<P, D>(type type: String = String(D), name: String? = nil, shared: Bool = false, create: P -> D) { | |
let key = Key(type: type, name: name) | |
dependencies[key] = Dependency(shared: shared, create: create) | |
} | |
/** | |
Registers an existing instance with the DependencyResolver. | |
- note: This effectively creates a singleton. If you want your singleton created lazily, | |
register it with a lambda and set `shared` to true. | |
- parameter instance: The instance to register. | |
- parameter type: Usually the type of `D`, but can be any string. | |
- parameter name: An optional name to disambiguate similar `type`s. | |
*/ | |
public static func register<D>(instance: D, type: String = String(D), name: String? = nil) { | |
register(type: type, name: name, shared: true) { instance } | |
} | |
/** | |
Resolves an instance of `D` in the DependencyResolver. | |
- parameter parameters: The parameters to pass to the registered lambda. | |
- parameter type: Usually the type of `D`, can be any string. | |
- parameter name: An optional name to disambiguate similar `type`s. | |
- returns: The result of the registered lambda, or nil if not registered. | |
*/ | |
public static func resolve<P, D>(parameters: P, type: String = String(D), name: String? = nil) -> D? { | |
let key = Key(type: type, name: name) | |
guard let dependency = dependencies[key] else { return nil } | |
return (dependency.resolve(parameters) as D) | |
} | |
/** | |
Resolves an instance of `D` in the DependencyResolver. | |
- parameter parameters: The parameters to pass to the registered lambda. | |
- parameter type: Usually the type of `D`, can be any string. | |
- parameter name: An optional name to disambiguate similar `type`s. | |
- returns: The result of the registered lambda, or nil if not registered. | |
*/ | |
public static func resolve<D>(type type: String = String(D), name: String? = nil) -> D? { | |
return resolve((), type: type, name: name) | |
} | |
} | |
private func ==(lhs: DependencyResolver.Key, rhs: DependencyResolver.Key) -> Bool { | |
if lhs.hashValue != rhs.hashValue { return false } | |
if lhs.type != rhs.type { return false } | |
if lhs.name != rhs.name { return false } | |
return true | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The basic way this works is that a lambda which describes how to create a particular type in registered with the resolver. The type (along with an optional string) is used to create a key with which we can later resolve that type. The type is ultimately resolved by calling the lambda and passing our parameters (if any) to it.
Examples:
What if we want more than one
Bovine
implementation?What about shared instances? There are various ways, but the simplest is just this:
Using names, you can have multiple different shared instances of the same type.