Description
Generate your dependency injections. Aimed for safety.
AnnotationInject alternatives and similar libraries
Based on the "Dependency Injection" category.
Alternatively, view AnnotationInject alternatives based on common mentions on social networks and blogs.
-
Swinject
Dependency injection framework for Swift with iOS/macOS/Linux -
Resolver
Swift Ultralight Dependency Injection / Service Locator framework -
Weaver
Dependency Injection framework for Swift (iOS/macOS/Linux) -
Locatable
A micro-framework that leverages Swift Property Wrappers to implement the Service Locator pattern
Appwrite - The Open Source Firebase alternative introduces iOS support
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of AnnotationInject or a related project?
README
AnnotationInject
Generate your dependency injections. Aimed for safety.
AnnotationInject | |
---|---|
:statue_of_liberty: | Free you from manually registering your dependencies. |
⚡ | Spend less time to configure and more time to code! |
🛡 | No more runtime crash because dependency is not up-to-date. Everything is checked at compile-time. |
👐 | Based on open source tools you like as Sourcery and Swinject. |
:book: | 100% open source under the MIT license |
Documentation for a specific release might slightly differ. If you have troubles please check the release doc first (by selecting the release in Github switch branches/tags).
What's the issue with injection?
Without annotations
Using a dependency injection library (say, Swinject) you need to remember to register your dependencies:
container.register(CoffeeMaker.self) { r in
return CoffeeMaker(heater: r.resolve()!) // Trouble ahead, not sure Heater is in fact registered!
}
/// later in your code
let coffeeMaker = container.resolve(CoffeeMaker.self) // crash, missing Heater dependency!
Running this code we'll get a crash at runtime: we didn't register any heater
, resulting in CoffeeMaker resolver to crash.
With annotations
Annotations will generate your dependencies and make sure everything resolves at compile time.
/// sourcery: inject
class CoffeeMaker {
init(heater: Heater) {
}
}
This time we'll get a compile time error because we forgot to declare a Heater
dependency. Houray!
Usage
1. Annotate your dependencies
/// sourcery: inject
class CoffeeMaker {
init(heater: Heater) { }
}
/// sourcery: inject
class Heater {
init() { }
}
2. Add a build phase to generate dependencies
See Installation for more details.
If not all dependencies can be resolved, the build phase will fail, preventing your code from compiling succesfully.
3. Add generated files and use generated code
let resolver = Assembler([AnnotationAssembly()]).resolver
// `registeredService` is generated code. It is completely safe at compile time.
let coffeeMaker = resolver.registeredService() as CoffeeMaker
let heater = resolver.registeredService() as Heater
Installation
Note: AnnotationInject depends/relies on Sourcery for annotations declaration, and Swinject as dependency injecter.
- Swift Package Manager
dependencies: [
.package(url: "https://github.com/pjechris/AnnotationInject.git", from: "0.6.0")
]
Then add a Build phases
to your project:
swift run annotationinject-cli --sources <path to your sources> --output <path to output generated code> (--args imports=<MyLib1> -args imports=<MyLib2>>)
- CocoaPods
Add pod AnnotationInject
to your Podfile
and a new Build phases
to your project:
"$(PODS_ROOT)"/AnnotationInject/Scripts/annotationinject --sources <path to your sources> --output <path to output generated code> (--args imports=<MyLib1> -args imports=<MyLib2>>)
Note: You can pass all
sourcery
command line options toannotationinject
script.
- Manually
Copy-paste Sources and Templates folders inside and add a new
Build phases
to your project:sourcery --templates <path to copied templates> --sources <path to your sources> --output <path to output generated code> (--args imports=<MyLib1> -args imports=<MyLib2>>)
Available annotations
inject
Registers a class into the dependency container.
/// sourcery: inject
class CoffeeMaker { }
Generated code
container.register(CoffeeMaker.self) {
return CoffeeMaker()
}
extension SafeDependencyResolver {
func registeredService() -> CoffeeMaker {
return resolve(CoffeeMaker.self)!
}
}
Options name Define a name for the service. Generated method will use that name. scope See Swinject Object Scopes type Defines the type on which the class is registered. Use it when you want to resolve against a protocol.
/// sourcery:inject: scope = "weak", type = "Maker", name = "Arabica"
class CoffeeMaker: Maker { }
inject
(init)
Registers a specific init for injection. If annotation is not provided, first found is used.
Note: Class still needs to be
inject
annotated.
// sourcery: inject
class CoffeeMaker {
init(heater: Heater) { }
// sourcery: inject
convenience init() {
self.init(heater: CoffeHeater())
}
}
Generated code
container.register(CoffeeMaker.self) {
return CoffeeMaker()
}
extension SafeDependencyResolver {
func registeredService() -> CoffeeMaker {
return resolve(CoffeeMaker.self)!
}
}
inject
(attribute)
Injects an attribute after init. Attribute requires to be marked as Optional (?
or !
).
Note: Class still needs to be
inject
annotated.
// sourcery: inject
class CoffeeMaker {
/// sourcery: inject
var heater: Heater!
init() { }
}
Generated code
container.register(CoffeeMaker.self) {
return CoffeeMaker()
}
.initCompleted { service, resolver in
service.heater = resolver.registeredService()
}
provider
Uses a custom function to register your dependency. It is the same as implementing container.register
manually while keeping safety.
Note that provided method must be called instantiate
.
Note: If you're providing 3rd party libraries (coming from Cocoapods for example), you will need to pass those imports to AnnotationInject using
args.imports MyLib,MyLib2,...
command line argument.
class CoffeeMaker {
init(heater: Heater) { }
}
// sourcery: provider
class AppProvider {
static func instantiate(resolver: SafeDependencyResolver) -> CoffeeMaker {
return CoffeeMaker(heater: CoffeHeater())
}
}
Generated code
container.register(CoffeeMaker, factory: AppProvider.instantiate(resolver:))
extension SafeDependencyResolver {
func registeredService() -> CoffeeMaker {
return resolve(CoffeeMaker.self)!
}
}
provided
(no longer needed with 0.5.0)
Declares a parameter as argument to define into the resolver method. Work on init and provider methods.
Caveats
Generated code does not compile because of missing imports
Set --args imports=<MyLib1> -args imports=<MyLib2>>
so that generated code includes 3rd party libraries.
Foundation types (URLSession, NSNotificationCenter, ...) are empty (.self) in generated code
Sourcery is not yet able to find those types. As such they are seen as non existent. Workaround: Define the surrounded type inside a Provider and give it foundation types.
Build phase is failing with no error reported
This might be coming from Sourcery having some incompatibilities with Xcode 11.4. Workaround: Install Sourcery using Homebrew then add to the build step SOURCERY_BINPATH=sourcery
as environment variable.
Pods/Sourcery/bin/Sourcery.app/Contents/MacOS/Sourcery: No such file or directory
You're probably using Sourcery as a Cocoapods dependency which unfortunately doesn't always work well. Workaround: Install Sourcery using Homebrew then add to the build step SOURCERY_BINPATH=sourcery
as environment variable.
License
This project is released under the MIT License. Please see the LICENSE file for details.
*Note that all licence references and agreements mentioned in the AnnotationInject README section above
are relevant to that project's source code only.