Last active
March 18, 2020 10:32
-
-
Save werediver/66ff8f13c900e9871070 to your computer and use it in GitHub Desktop.
Service Locator pattern implementation in Swift 2 with lazy (on demand) service initialization.
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
protocol ServiceLocator { | |
func getService<T>() -> T? | |
} | |
final class LazyServiceLocator: ServiceLocator { | |
/// Registry record | |
enum RegistryRec { | |
case Instance(Any) | |
case Recipe(() -> Any) | |
func unwrap() -> Any { | |
switch self { | |
case .Instance(let instance): | |
return instance | |
case .Recipe(let recipe): | |
return recipe() | |
} | |
} | |
} | |
/// Service registry | |
private lazy var reg: Dictionary<String, RegistryRec> = [:] | |
private func typeName(some: Any) -> String { | |
return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)" | |
} | |
func addService<T>(recipe: () -> T) { | |
let key = typeName(T) | |
reg[key] = .Recipe(recipe) | |
} | |
func addService<T>(instance: T) { | |
let key = typeName(T) | |
reg[key] = .Instance(instance) | |
//print("Service added: \(key) / \(typeName(instance))") | |
} | |
func getService<T>() -> T? { | |
let key = typeName(T) | |
var instance: T? = nil | |
if let registryRec = reg[key] { | |
instance = registryRec.unwrap() as? T | |
// Replace the recipe with the produced instance if this is the case | |
switch registryRec { | |
case .Recipe: | |
if let instance = instance { | |
addService(instance) | |
} | |
default: | |
break | |
} | |
} | |
return instance | |
} | |
} | |
// Demo | |
// Services declaration | |
protocol S1 { | |
func f1() -> String | |
} | |
protocol S2 { | |
func f2() -> String | |
} | |
// Services imlementation | |
class S1Impl: S1 { | |
let s2: S2 | |
init(s2: S2) { | |
self.s2 = s2 | |
} | |
func f1() -> String { | |
return "S1 OK" | |
} | |
} | |
class S2Impl: S2 { | |
func f2() -> String { | |
return "S2 OK" | |
} | |
} | |
// Service Locator initialization | |
let sl: ServiceLocator = { | |
let sl = LazyServiceLocator() | |
sl.addService { S1Impl(s2: sl.getService()!) as S1 } | |
sl.addService { S2Impl() as S2 } | |
return sl | |
}() | |
// Test run | |
let s1: S1? = sl.getService() | |
let s2: S2? = sl.getService() | |
//let s2_: S2? = sl.getService() | |
print(s1?.f1() ?? "S1 NOT FOUND") // S1 OK | |
print(s2?.f2() ?? "S2 NOT FOUND") // S2 OK |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment