ServiceLocator
The Service Locator is a design pattern used to decouple the way objects are obtained from the concrete classes that implement them. This is achieved by centralizing object creation to a single location, known as a service locator.
Install / Use
/learn @kibotu/ServiceLocatorREADME
ServiceLocator for iOS
The Service Locator is a design pattern used to decouple the way objects are obtained from the concrete classes that implement them. This is achieved by centralizing object creation to a single location, known as a service locator.
This tiny project has been inspired by RouterService and Swinject
Getting Started
1. Define Modules for Dependency Registration
Modules group related service registrations. They override the build() method to define how services should be registered with the locator.
class MyModule: ServiceLocatorModule {
override func build() {
// singletons
single(ConfigProviderProtocol.self) {
ConfigProvider()
}
// get dependencies with resolve
single(URLFactory.self) {
URLFactory(remoteConfigProvider: self.resolve())
}
// factory
factory(AppLinkFactory.self) {
let settingsManager: SettingsManager = self.resolve()
return AppLinkFactory(descriptionFactory: settingsManager)
}
}
}
Tip: Define an application-wide module that includes other modules.
class AppModule: ServiceLocatorModule {
override func build() {
module { MyModule() }
}
}
2. Initialize the Service Locator
lazy var myServiceLocator = startServiceLocator {
AppModule()
}
myServiceLocator.build()
3. Retrieve dependency
3.1. In data structures
Retrieve dependencies in classes or structs using @Inject. The property wrapper automatically resolves the dependency from the service locator:
class MyCass {
@Inject(myServiceLocator) public var contactSheet: ContactSheet
}
3.2 Local Variable Injection in Functions
For injecting dependencies within functions or methods, use @Inject before local variable declarations:
func doSomething() {
@Inject(myServiceLocator) var contactSheet: ContactSheet
// Use contactSheet here
}
3.3 Direct Resolution with resolve
If you prefer not using property wrappers, directly resolve dependencies using the service locator's resolve method:
let configProvider : ConfigProvider = serviceLocator.resolve()
4. Resetting the Service Locator
You can reset the ServiceLocator to its initial state, which can be useful in testing scenarios:
// Reset the entire service locator
resetServiceLocator(myServiceLocator)
// Or, if you have direct access to the ServiceLocator instance:
myServiceLocator.reset()
Tips and Customizations
Custom Property Wrapper
Create a tailored property wrapper if you have specific needs or want to simplify usage for a particular scope, like plugins in this example:
@propertyWrapper
internal final class PluginInject<T>: Dependency<T> {
public var wrappedValue: T {
resolvedWrappedValue()
}
public init() {
super.init(MyPlugin.shared.myServiceLocator)
}
}
Usage:
class MyClass {
@PluginInject var contactSheet: ContactSheet
}
func doSomething() {
@PluginInject var contactSheet: ContactSheet
}
Enabling Logging
You can enable logging for the ServiceLocator to help with debugging:
ServiceLocator.enableLogging = true
How to install
Swift Package Manager
Add the dependency to your Package.swift
products: [
...
]
dependencies: [
.package(url: "https://github.com/kibotu/ServiceLocator", from: "1.0.2"),
],
targets: [
...
]
Requirements
- iOS 16.0 or later
- Xcode 15.0 or later
- Swift 5.10 or later
Contributions welcome!
