{"id":32147077,"url":"https://github.com/andrey-shavelev/juice","last_synced_at":"2026-02-19T02:02:16.594Z","repository":{"id":63903694,"uuid":"214248193","full_name":"andrey-shavelev/Juice","owner":"andrey-shavelev","description":"Lightweight dependency injection container with simple fluent interface.","archived":false,"fork":false,"pushed_at":"2022-01-31T07:39:20.000Z","size":344,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-25T18:21:56.969Z","etag":null,"topics":["dependency-injection","di-container","fluent-interface","inversion-of-control","property-wrappers","swift","swift5"],"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/andrey-shavelev.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":"2019-10-10T17:45:41.000Z","updated_at":"2021-11-27T22:01:26.000Z","dependencies_parsed_at":"2023-01-14T13:01:13.540Z","dependency_job_id":null,"html_url":"https://github.com/andrey-shavelev/Juice","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/andrey-shavelev/Juice","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrey-shavelev%2FJuice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrey-shavelev%2FJuice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrey-shavelev%2FJuice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrey-shavelev%2FJuice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andrey-shavelev","download_url":"https://codeload.github.com/andrey-shavelev/Juice/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrey-shavelev%2FJuice/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29600845,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T00:59:38.239Z","status":"online","status_checked_at":"2026-02-19T02:00:07.702Z","response_time":117,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","di-container","fluent-interface","inversion-of-control","property-wrappers","swift","swift5"],"created_at":"2025-10-21T08:44:46.315Z","updated_at":"2026-02-19T02:02:16.587Z","avatar_url":"https://github.com/andrey-shavelev.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![license-MIT](https://img.shields.io/badge/license-MIT-green)](https://opensource.org/licenses/MIT)\n[![Swift-5.2](https://img.shields.io/badge/Swift-5.2-orange)](https://swift.org)\n![Tests](https://github.com/andrey-shavelev/Juice/actions/workflows/tests.yml/badge.svg)\n\n\n# Juice\nLightweight dependency injection container with simple fluent interface.\n\n## Installing\n\nWith a swift package manager:\n\n```swift\ndependencies: [\n        .package(url: \"https://github.com/andrey-shavelev/Juice\", from: \"0.1.0\")\n//...\n    ]\n```\n\n## Quick Start\n###  Creating container and registering services\n\n```swift\nlet container = try Container { builder in\n\n// register a type that conforms to Injectable protocol\n\n    builder.register(injectable: FreshJuice.self)\n            .instancePerDependency()\n            .as(Juice.self)\n\n// register by initializer\n\n    builder.register(initializer: Apple.init(color:))\n        .instancePerDependency()\n         .asSelf()\n\n// register a custom factory\n\n    builder.register { scope -\u003e TeaBlend in\n           let orange = try scope.resolve(Orange.self)\n        let blackTea = try scope.resolve(BlackTea.self)\n    \n        return TeaBlend(fruit: orange, tea: blackTea)\n    }\n    .singleInstance()\n    .asSelf()\n}\n```\n\n### Resolving a Service\n\n```swift\n// required\n\nlet orangeJuice = try container.resolve(Juice.self)\n\n// optional\n\nlet compot = try container.resolveOptional(Compot.self)\nlet tea = try container.resolve(Tea?.self)\n\n// supplying arguments\n\nlet appleJuice = try container.resolve(\n    Juice.self, \n    withArguments: Apple())\n\n// or when you need to specify type directly\n\nlet appleJuice = try container.resolve(\n    Juice.self, \n    withArguments: Argument\u003cFruit\u003e(Apple()))\n\n```\n\nArguments passed to `resolve` method are matched with initializer parameters by their type and take precedence over services registered in container.\n\n### Dependency Injection\n\n#### Initializer\nFor services that conform to one of `Injectable` protocols or are registered by their `init` method, parameters of the initializer are filled in by `Container`.\n\n```swift\nclass IcyLemonade: InjectableWithFiveParameters {\n    let fruitJuice: Juice\n    let lemon: Lemon\n    let optionalSweetener: Sweetener?\n    let water: Water\n    let ice: Ice\n\n    // All parameters will be filled by Container from resolution scope.\n    required init(_ fruitJuice: Juice,\n                  _ lemon: Lemon,\n                  _ optionalSweetener: Sweetener?,\n                  _ water: Water,\n                  _ ice: Ice) {\n        self.fruitJuice = fruitJuice\n        self.lemon = lemon\n        optionalSweetener = optionalSweetener\n        self.water = water\n        self.ice = ice\n    }\n}\n\nlet container = try Container { builder in\n    builder.register(injectable: IcyLemonade.self)\n            .singleInstance()\n            .asSelf()\n    ///...\n}\n\n```\n\n### Property\nAlternatively, dependencies could be injected into properties.\n\n```swift\n// Using Inject property wrapper\n\nclass Jam {\n    @Inject var fruit: Fruit\n    @Inject var spice: Spice?\n\n    init() {\n    }\n}\n\n// Or without wrapper\n\nstruct TeaBlend {\n    var fruit: Fruit!\n    var spice: Spice?\n\n       init() {\n    }\n}\n\n// In this case you need to specify properties for injection when registering a service\n\nlet container = try Container { builder in\n    builder.register(injectable: Jam.self)\n            .singleInstance()\n            .asSelf()\n            .injectDependency(into: \\.fruit)\n            .injectDependency(into: \\.spice)\n}\n```\n\n### Lazy dependencies\n\n```swift\nclass Egg {\n    unowned var chicken: Chicken\n    \n    required init(_ chicken: Chicken) {\n        self.chicken = chicken\n    }\n}\n\nclass Chicken {\n    var egg: Lazy\u003cEgg\u003e\n    \n    required init(_ egg: Lazy\u003cEgg\u003e) {\n        self.egg = egg\n    }\n}\n\n/* \n * Lazy\u003cT\u003e - is a wrapper that delays actual resolution \n * until its value is requested. \n * For this purpose it keeps a strong reference to a resolution \n * context of the owning instance, \n * including all arguments (if any) that were passed to it.\n*/\n```\n\n### Modules\n\n```swift\nlet container = try Container { builder in\n    builder.register(module: FruitModule())\n}\n\n// Module allows to group registrations\nstruct FruitModule : Module {\n    func registerServices(into builder: ContainerBuilder) {\n        builder.register(injectable: Apple.self)\n            .instancePerDependency()\n            .asSelf()\n        builder.register(injectable: Orange.self)\n            .instancePerDependency()\n            .as(Fruit.self)\n    }\n}\n```\n\n### Child Containers\n\n```swift\nlet container = try Container { builer in\n    // Some types are registered here\n}\n\nlet childContainer = try container.createChildContainer { builer in\n    // Additional types are be registered here\n    // Registrations from the parent container could be overriden  \n}\n```\n\n### Thread Safety\n\nCurrent version of Juice does not support resolving services from concurrent threads. \n\n## Registration options\n\n```swift\nlet container = try Container { builder in\n\n// Instance per depenedency\n    builder.register(injectable: Banana.self)\n      .singleInstance()\n      .asSelf()\n// Each time Banana is resolved, container will return the same instance. Container will keep strong reference to it.\n\n// Single instance\n    builder.register(initializer: Apple.init(color:))\n      .instancePerDependency()\n         .asSelf()\n// Each time Apples is resolved, container will create a new instance. Container will not keep reference to any of it.\n\n// External singletons\nlet someExternalSingleton = SingletonService.instance\n        \nbuilder.register(instance: someExternalSingleton)\n  .ownedExternally()\n  .asSelf()\n// For instances registered as ownedExternally() container will keep an unowned reference.\n\nlet anotherExternalSingleton = AnotherSingletonService.instance\n        \nbuilder.register(instance: anotherExternalSingleton)\n  .ownedByContainer()\n  .asSelf()\n// For instances registered as ownedByContainer() container will keep a strong reference.\n}\n\n```\n\n## License\n\nThis project is licensed under MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrey-shavelev%2Fjuice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandrey-shavelev%2Fjuice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrey-shavelev%2Fjuice/lists"}