{"id":13535157,"url":"https://github.com/sebastianpixel/DependencyInjection","last_synced_at":"2025-04-02T00:32:39.597Z","repository":{"id":56908440,"uuid":"251217650","full_name":"sebastianpixel/DependencyInjection","owner":"sebastianpixel","description":"Microframework for dependency injection in Swift based on PropertyWrappers.","archived":false,"fork":false,"pushed_at":"2021-02-04T21:17:24.000Z","size":59,"stargazers_count":43,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-12T23:06:10.067Z","etag":null,"topics":["dependency-injection-framework","inversion-of-control","ios","macos","property-wrappers","swift"],"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/sebastianpixel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-03-30T06:12:22.000Z","updated_at":"2024-11-05T20:34:51.000Z","dependencies_parsed_at":"2022-08-21T03:20:59.013Z","dependency_job_id":null,"html_url":"https://github.com/sebastianpixel/DependencyInjection","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebastianpixel%2FDependencyInjection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebastianpixel%2FDependencyInjection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebastianpixel%2FDependencyInjection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebastianpixel%2FDependencyInjection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sebastianpixel","download_url":"https://codeload.github.com/sebastianpixel/DependencyInjection/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246735354,"owners_count":20825221,"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":["dependency-injection-framework","inversion-of-control","ios","macos","property-wrappers","swift"],"created_at":"2024-08-01T08:00:50.515Z","updated_at":"2025-04-02T00:32:39.245Z","avatar_url":"https://github.com/sebastianpixel.png","language":"Swift","funding_links":[],"categories":["Dependency Injection"],"sub_categories":[],"readme":"# DependencyInjection\n\nA microframework for dependency injection based on the service locator pattern utilizing Swift's property wrappers.\n\n## Usage\nOn App start register dependencies by overriding AppDelegate's initializer:\n```Swift\nimport DependencyInjection\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n    @LazyInject private var config: AppConfiguration\n    @LazyInject private var router: Router\n\n    override init() {\n        super.init()\n\n        DIContainer.register {\n            Shared(AppConfigurationImpl() as AppConfiguration)\n            Shared(RouterImpl() as Router)\n        }\n    }\n\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -\u003e Bool {\n\n        // use `config` and `router`\n\n        return true\n    }\n}\n```\n### Lazy vs eager evaluation\nAs the initializers of the injected properties the example above would be called before the AppDelegate's initializer, it's necessary to separate usage and initialization of the injected property. `@LazyInject` will only resolve when its property is first accessed. To resolve properties eagerly use `@Inject` instead.\n\n### Mutable vs immutable injected properties\nProperties injected via `@Inject` and `@LazyInject` are immutable which is enforced by the compiler. To resolve mutable properties use `@MutableInject` and `@MutableLazyInject`.\n\n### Optional dependencies\nDependencies that under certain circumstances cannot be resolved can simply be marked as `Optional`s.\n```Swift\n@Inject var player: MediaPlayer?\n```\nIf no `MediaPlayer` instance is registered, `player` resolves to `nil`.\nNote: If an Optional would be registered `player` also resolves to `nil` as the registered type is not the expected `MediaPlayer` but `Optional\u003cMediaPlayer\u003e` in this case.\n\n### Shared vs new instances\nRegistering a dependency as `Shared` will always resolve to the same (identical) instance. To get a new instance in each property use `New`:\n```Swift\nDIContainer.register(New(MockRouter() as Router))\n```\nBy doing so registrations made in production code could for example be overridden by mock objects in tests that are not shared across objects.\n\n### Aliases\nInstances can also be registered with multiple alias protocols that each only expose certain parts of their functionality:\n```Swift\nDIContainer.register(Shared(RouterImpl.init, as: Router.self, DeeplinkHandler.self))\n```\n\n### Registering dependencies that have dependencies themselves\nIn case the registered dependencies depend on other dependencies themselves that should be passed via initializer injection there are overloads for registering `Shared` and `New` instances that pass a `Resolver` object in a closure:\n```Swift\nDIContainer.register {\n    Shared { resolve in RouterImpl(config: resolve()) }\n}\n```\n\n### Modules\nTo group dependencies or to avoid exposing concrete types outside a Swift module it's an option to use DI Modules. Those are convenience wrappers around registrations and can either be defined in different parts of the code base and then registered themselves e.g. in AppDelegate:\n```Swift\n// Feature 1\n\nstruct FeatureOneDependencyInjection {\n    static let module = Module {\n        Shared(FeatureOneImplementation() as FeatureOne)\n        New(FeatureOneViewModelImplementation() as FeatureOneViewModel)\n    }\n}\n\n// Feature 2\n\nstruct FeatureTwoDependencyInjection {\n    static let module = Module(Shared(FeatureTwoImplementation() as FeatureTwo))\n}\n\n// AppDelegate\n\noverride init() {\n    super.init()\n\n    DIContainer.register {\n        FeatureOneDependencyInjection.module\n        FeatureTwoDependencyInjection.module\n    }\n}\n```\n\nAlternatively Modules could also be used inline in a central location:\n```Swift\nDIContainer.register {\n    Module {\n        Shared(FeatureOneImplementation() as FeatureOne)\n        New(FeatureOneViewModelImplementation() as FeatureOneViewModel)\n    }\n    Module {\n        Shared(FeatureTwoImplementation() as FeatureTwo)\n    }\n}\n```\n\n### Alternative registering outside AppDelegate\nDependencies can be registered by conforming `DIContainer` to the `DependencyRegistering` protocol and implementing the `registerDependencies` method. Dependencies will then be registered once the first dependency is resolved.\n```Swift\nextension DIContainer: DependencyRegistering {\n    public static func registerDependencies() {\n        register(Shared(RouterImpl() as Router))\n    }\n}\n```\n\n### Usage without property wrappers\nAs property wrappers can currently not be used inside function bodies, dependencies can be resolved \"manually\":\n```Swift\nfunc foo() {\n    DIContainer.resolve(Router.self)\n}\n```\n\nor if the compiler can infer the type to resolve:\n```Swift\nfunc foo() {\n    bar(router: DIContainer.resolve())\n}\n\nfunc bar(router: Router) {\n    // …\n}\n```\n\n### Parameterized resolution\nIf inversion of control should also be applied to types where some or all arguments are provided at a later point it's possible to register a closure that receives arguments and returns the desired object. In this case it makes most sense to register a `New` instance, meaning every time the dependency is resolved a new object is created. If a `Shared` instance would be registered the resolved instances would always be the one that was first resolved for the respective type and arguments would be ignored.\n```Swift\nfunc register() {\n    DIContainer.register {\n        New({ resolver, id in ConcreteViewModel(id: id) }, as: ViewModelProtocol.self)\n\n        // alternatively:\n        New { _, id in ConcreteViewModel(id: id) as ViewModelProtocol }\n    }\n}\n```\n\nThe `id` argument needs to be provided to use an instance implementing `ViewModelProtocol`. The first argument `resolver` can be used or ignored to resolve further arguments via dependency injection. Providing the argument is done in a closure:\n```Swift\nfunc resolve() -\u003e ViewModelProtocol {\n    DIContainer.resolve(ViewModelProtocol.self, arguments: { \"id_goes_here\" })\n}\n\nfunc resolve() -\u003e PresenterProtocol {\n    // multiple arguments are provided as tuple:\n    DIContainer.resolve(PresenterProtocol.self) { (\"argument 1\", 23, \"argument 3\") }\n}\n```\n\nAs initializers of properties (and Property Wrappers for that matter) are called before `self` is available in Swift only hard coded arguments could be provided in initalizers of `@Inject` and `@LazyInject`. Parameterized resolution is therefore currently limited to the `resolve` method of `DIContainer` as shown above.\n\n### Thread safety\nRegistering as well as resolving dependencies is handled on a dedicated synchronous and reentrant queue.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebastianpixel%2FDependencyInjection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsebastianpixel%2FDependencyInjection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebastianpixel%2FDependencyInjection/lists"}