{"id":15391219,"url":"https://github.com/pjechris/annotationinject","last_synced_at":"2025-12-11T23:00:15.415Z","repository":{"id":42025690,"uuid":"130196540","full_name":"pjechris/AnnotationInject","owner":"pjechris","description":"Compile-time Swift dependency injection annotations","archived":false,"fork":false,"pushed_at":"2024-02-12T09:37:26.000Z","size":6788,"stargazers_count":43,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-12T15:56:58.051Z","etag":null,"topics":["annotations","cocoapods","compile-time","dependency-injection","injection","injection-container","safety","sourcery","swift","swinject"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pjechris.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-04-19T10:03:39.000Z","updated_at":"2024-07-27T10:13:10.000Z","dependencies_parsed_at":"2024-10-18T22:26:38.841Z","dependency_job_id":"428dce70-395e-4d4c-9a4d-cee6b4b16cff","html_url":"https://github.com/pjechris/AnnotationInject","commit_stats":{"total_commits":103,"total_committers":6,"mean_commits":"17.166666666666668","dds":"0.27184466019417475","last_synced_commit":"171b250e51fc7b1e7717fa3a80291b1be6d5c64c"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjechris%2FAnnotationInject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjechris%2FAnnotationInject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjechris%2FAnnotationInject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjechris%2FAnnotationInject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pjechris","download_url":"https://codeload.github.com/pjechris/AnnotationInject/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249167412,"owners_count":21223503,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["annotations","cocoapods","compile-time","dependency-injection","injection","injection-container","safety","sourcery","swift","swinject"],"created_at":"2024-10-01T15:10:22.015Z","updated_at":"2025-12-11T23:00:15.095Z","avatar_url":"https://github.com/pjechris.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AnnotationInject\n\n![Cocoapods](https://img.shields.io/static/v1?label=cocoapods\u0026message=%E2%9C%93\u0026color=24C28A\u0026labelColor=444444)\n![SPM](https://img.shields.io/static/v1?label=SPM\u0026message=%E2%9C%93\u0026color=24C28A\u0026labelColor=444444)\n![tests](https://github.com/pjechris/AnnotationInject/actions/workflows/testing.yml/badge.svg)\n[![twitter](https://img.shields.io/badge/twitter-pjechris-1DA1F2?logo=twitter\u0026logoColor=white)](https://twitter.com/pjechris)\n\nGenerate your dependency injections. Aimed for safety.\n\n|                     | AnnotationInject\n|---------------------|--------\n| :statue_of_liberty: | Free you from manually registering your dependencies.\n| ⚡                   | Spend **less time to configure** and more time to code!\n| 🛡                  | **No more runtime crash** because dependency is not up-to-date. Everything is checked at **compile-time**.\n| 👐                  | Based on open source tools you like as [Sourcery](https://github.com/krzysztofzablocki/Sourcery) and [Swinject](https://github.com/Swinject/Swinject).\n| :book:              | 100% open source under the MIT license\n\n- [What's the issue with injection?](#whats-the-issue-with-injection)\n- [Usage](#usage)\n- [Available annotations](#available-annotations)\n- [Caveats](#caveats)\n\n\u003e 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).\n\n## What's the issue with injection?\n### Without annotations\nUsing a dependency injection library (say, Swinject) you need to **remember** to register your dependencies:\n\n```swift\ncontainer.register(CoffeeMaker.self) { r in\n  return CoffeeMaker(heater: r.resolve()!) // Trouble ahead, not sure Heater is in fact registered!\n}\n\n/// later in your code\nlet coffeeMaker = container.resolve(CoffeeMaker.self) // crash, missing Heater dependency!\n```\n\nRunning this code we'll get a crash **at runtime**: we didn't register any `heater`, resulting in CoffeeMaker resolver to crash.\n\n### With annotations\n\nAnnotations will generate your dependencies and make sure everything resolves at **compile time**.\n\n```swift\n/// sourcery: inject\nclass CoffeeMaker {\n    init(heater: Heater) {\n\n    }\n}\n```\n\nThis time we'll get a compile time error because we forgot to declare a `Heater` dependency. Houray!\n\n## Usage\n\n### 1. Annotate your dependencies\n```\n/// sourcery: inject\nclass CoffeeMaker {\n  init(heater: Heater) { }\n}\n\n/// sourcery: inject\nclass Heater {\n    init() { }\n}\n```\n\n### 2. Add a build phase to generate dependencies\nSee [Installation](#installation) for more details.\n\nIf not all dependencies can be resolved, the build phase will fail, preventing your code from compiling succesfully.\n\n### 3. Add generated files and use generated code\n\n```\nlet resolver = Assembler([AnnotationAssembly()]).resolver\n\n// `registeredService` is generated code. It is completely safe at compile time.\nlet coffeeMaker = resolver.registeredService() as CoffeeMaker\nlet heater = resolver.registeredService() as Heater\n```\n\n## Installation\n\u003e Note: AnnotationInject depends/relies on Sourcery for annotations declaration, and Swinject as dependency injecter.\n\n- Swift Package Manager\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/pjechris/AnnotationInject.git\", from: \"0.6.0\")\n]\n```\nThen add a `Build phases` to your project:\n\n```shell\nswift run annotationinject-cli --sources \u003cpath to your sources\u003e --output \u003cpath to output generated code\u003e (--args imports=\u003cMyLib1\u003e -args imports=\u003cMyLib2\u003e\u003e)\n```\n\n- Swift Package Manager (Xcode)\n\nAdd AnnotationInject as dependency in Xcode then add this `Build phase` to your project:\n\n```shell\nSPM_CHECKOUT_DIR=${BUILD_DIR%Build/*}SourcePackages/checkouts/AnnotationInject\ncd $SPM_CHECKOUT_DIR\n/usr/bin/xcrun --sdk macosx swift run annotationinject-cli ...\n```\n\n- CocoaPods\n\nAdd `pod AnnotationInject` to your `Podfile` and a new `Build phases` to your project:\n```shell\n\"$(PODS_ROOT)\"/AnnotationInject/Scripts/annotationinject --sources \u003cpath to your sources\u003e --output \u003cpath to output generated code\u003e (--args imports=\u003cMyLib1\u003e -args imports=\u003cMyLib2\u003e\u003e)\n```\n\n\u003e Note: You can pass all `sourcery` command line options to `annotationinject` script.\n\n- Manually\n\n 1. Install [Swinject](https://github.com/Swinject/Swinject) and [Sourcery](https://github.com/krzysztofzablocki/Sourcery).\n\n 2. Copy-paste Sources and Templates folders inside and add a new `Build phases` to your project:\n```shell\nsourcery --templates \u003cpath to copied templates\u003e --sources \u003cpath to your sources\u003e --output \u003cpath to output generated code\u003e (--args imports=\u003cMyLib1\u003e -args imports=\u003cMyLib2\u003e\u003e)\n```\n\n## Available annotations\n\n### `inject`\nRegisters a class into the dependency container.\n\n```swift\n/// sourcery: inject\nclass CoffeeMaker { }\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eGenerated code\u003c/summary\u003e\n  \u003cp\u003e\n\n  ```swift\n  container.register(CoffeeMaker.self) {\n    return CoffeeMaker()\n  }\n\n  extension SafeDependencyResolver {\n    func registeredService() -\u003e CoffeeMaker {\n      return resolve(CoffeeMaker.self)!\n    }\n  }\n  ```\n\n  \u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eOptions\u003c/summary\u003e\n  \u003cp\u003e\n    \u003cdl\u003e\n        \u003cdt\u003ename\u003c/dt\u003e\n        \u003cdd\u003eDefine a name for the service. Generated method will use that name.\u003c/dd\u003e\n        \u003cdt\u003escope\u003c/dt\u003e\n        \u003cdd\u003eSee \u003ca href=\"https://github.com/Swinject/Swinject/blob/master/Documentation/ObjectScopes.md\"\u003eSwinject Object Scopes\u003c/a\u003e\n        \u003c/dd\u003e\n        \u003cdt\u003etype\u003c/dt\u003e\n        \u003cdd\u003eDefines the type on which the class is registered. Use it when you want to resolve against a protocol.\n        \u003c/dd\u003e\n    \u003c/dl\u003e\n\n  \u003c/p\u003e\n\n  ```swift\n  /// sourcery:inject: scope = \"weak\", type = \"Maker\", name = \"Arabica\"\n  class CoffeeMaker: Maker { }\n  ```\n\u003c/details\u003e\n\n### `inject` (init)\nRegisters a specific init for injection. If annotation is not provided, first found is used.\n\n\u003e Note: Class still needs to be `inject` annotated.\n\n```swift\n// sourcery: inject\nclass CoffeeMaker {\n  init(heater: Heater) { }\n\n  // sourcery: inject\n  convenience init() {\n    self.init(heater: CoffeHeater())\n  }\n}\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eGenerated code\u003c/summary\u003e\n  \u003cp\u003e\n\n  ```swift\n  container.register(CoffeeMaker.self) {\n    return CoffeeMaker()\n  }\n\n  extension SafeDependencyResolver {\n    func registeredService() -\u003e CoffeeMaker {\n      return resolve(CoffeeMaker.self)!\n    }\n  }\n  ```\n\n  \u003c/p\u003e\n\u003c/details\u003e\n\n### `inject` (attribute)\nInjects an attribute after init. Attribute requires to be marked as Optional (`?` or `!`).\n\n \u003e Note: Class still needs to be `inject` annotated.\n\n```swift\n// sourcery: inject\nclass CoffeeMaker {\n  /// sourcery: inject\n  var heater: Heater!\n\n  init() { }\n}\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eGenerated code\u003c/summary\u003e\n  \u003cp\u003e\n\n  ```swift\n  container.register(CoffeeMaker.self) {\n    return CoffeeMaker()\n  }\n  .initCompleted { service, resolver in\n    service.heater = resolver.registeredService()\n  }\n  ```\n\n  \u003c/p\u003e\n\u003c/details\u003e\n\n### `provider`\nUses a custom function to register your dependency. It is the same as implementing `container.register` manually while keeping safety.\nNote that provided method **must** be called `instantiate`.\n\n\u003e 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.\n\n```swift\nclass CoffeeMaker {\n  init(heater: Heater) { }\n}\n\n// sourcery: provider\nclass AppProvider {\n  static func instantiate(resolver: SafeDependencyResolver) -\u003e CoffeeMaker {\n    return CoffeeMaker(heater: CoffeHeater())\n  }\n}\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eGenerated code\u003c/summary\u003e\n  \u003cp\u003e\n\n  ```swift\n  container.register(CoffeeMaker, factory: AppProvider.instantiate(resolver:))\n\n  extension SafeDependencyResolver {\n    func registeredService() -\u003e CoffeeMaker {\n      return resolve(CoffeeMaker.self)!\n    }\n  }\n  ```\n\n  \u003c/p\u003e\n\u003c/details\u003e\n\n### `provided` (no longer needed with 0.5.0)\nDeclares a parameter as argument to define into the resolver method. Work on init and provider methods.\n\n\n## Caveats\n_**Generated code does not compile because of missing imports**_\n\nSet `--args imports=\u003cMyLib1\u003e -args imports=\u003cMyLib2\u003e\u003e` so that generated code includes 3rd party libraries.\n\n_**Foundation types (URLSession, NSNotificationCenter, ...) are empty (.self) in generated code**_\n\nSourcery 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.\n\n_**Build phase is failing with no error reported**_\n\nThis 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.\n\n_**Pods/Sourcery/bin/Sourcery.app/Contents/MacOS/Sourcery: No such file or directory**_\n\nYou'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.\n\n## License\nThis project is released under the MIT License. Please see the LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpjechris%2Fannotationinject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpjechris%2Fannotationinject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpjechris%2Fannotationinject/lists"}