{"id":15066902,"url":"https://github.com/lukas-simonson/hydrate","last_synced_at":"2026-01-03T10:06:21.232Z","repository":{"id":255417757,"uuid":"849554171","full_name":"Lukas-Simonson/Hydrate","owner":"Lukas-Simonson","description":"A Swift Dependency Injection Framework Made Simple","archived":false,"fork":false,"pushed_at":"2024-08-29T22:07:41.000Z","size":223,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-22T16:13:06.126Z","etag":null,"topics":["container","dependency-injection","ios","swift","swiftui","xcode"],"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/Lukas-Simonson.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-29T19:55:14.000Z","updated_at":"2024-08-29T22:16:43.000Z","dependencies_parsed_at":null,"dependency_job_id":"37f08d08-ff34-4c0c-9f0b-9b35840f7982","html_url":"https://github.com/Lukas-Simonson/Hydrate","commit_stats":null,"previous_names":["lukas-simonson/hydrate"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lukas-Simonson%2FHydrate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lukas-Simonson%2FHydrate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lukas-Simonson%2FHydrate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lukas-Simonson%2FHydrate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lukas-Simonson","download_url":"https://codeload.github.com/Lukas-Simonson/Hydrate/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243822311,"owners_count":20353496,"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":["container","dependency-injection","ios","swift","swiftui","xcode"],"created_at":"2024-09-25T01:13:44.692Z","updated_at":"2026-01-03T10:06:21.182Z","avatar_url":"https://github.com/Lukas-Simonson.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n    \u003cpicture\u003e\n        \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://github.com/Lukas-Simonson/Hydrate/blob/main/hydrateLogoDark.png?raw=true\"\u003e\n        \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://github.com/Lukas-Simonson/Hydrate/blob/main/hydrateLogoLight.png?raw=true\"\u003e\n        \u003cimg alt=\"Hydrate: a Dependency Injection Framework for Swift\" src=\"https://github.com/Lukas-Simonson/Hydrate/blob/main/hydrateLogoLight.png?raw=true\" width=200\u003e\n    \u003c/picture\u003e\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cstrong\u003eA Swift Dependency Injection Framework Made Simple\u003c/strong\u003e\u003cbr\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://developer.apple.com/swift/\"\u003e\u003cimg alt=\"Swift 5.10\" src=\"https://img.shields.io/badge/swift-5.10-orange.svg?style=flat\"\u003e\u003c/a\u003e\n    \u003ca href=\"LICENSE\"\u003e\u003cimg alt=\"license\" src=\"https://img.shields.io/badge/license-MIT-black.svg\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Overview\nHydrate is a Swift Dependency Injection (DI) framework designed with ease of use, and simplicity as its goal. Leveraging the power of DI, Hydrate allows you to decouple components, making your codebase more modular, testable, and maintainable. Hydrate provides a simple, and beautiful approach to dependency management, supporting a variety of injection patterns, including singleton instances, factory-generated dependencies, and the ability to build your own provider implementations. With thread-safe storage and a clean, intuitive API, Hydrate ensures that your dependencies are resolved efficiently and safely, even in complex, multi-threaded environments.\n\n## Quickstart Guide\n\n### Registering Dependencies\nHydrate makes it simple to register and manage dependencies within your application. There are several methods available for registering dependencies, depending on how you want them to be resolved. Below are examples of how to register dependencies using Hydrate's included API.\n\n#### Registering a Singleton Instance\nA singleton instance is a dependency that should only have one shared instance throughout the applications lifecycle. You can easily register a singleton instance using the `registerSingleton` method.\n\n```swift\nlet service = MovieAPI()\nContainer.registerSingleton(service)\n```\n\n#### Registering a Singleton Factory\nSometimes, you may want to ensure that a dependency is only created once; however, this dependency may rely on other injected dependencies. You can register a singleton factory to control how the initial instance of your singleton is created. The singleton factory uses a closure to build the instance of your Singleton the first time it is created. This closure takes a `Resolver` as a parameter that you can use to resolve other registered dependencies.\n\n```swift\nContainer.registerSingleton { resolver in\n    MovieRepository(api: resolver.resolve(MovieAPI.self)) \n}\n```\n\n- Here a factory closure is registered to create an instance of `MovieRepository`. The closure will be called once to create the instance, and that instance will be reused for all subsequent resolutions.\n\n#### Registering a Factory\nIf you need a fresh instance of a dependency each time it's requested, you can register a factory that creates a new instance every time:\n\n```swift\nContainer.register { resolver in\n    return FetchMovieUseCase(dependency: resolver.resolve(MovieRepository.self))\n}\n```\n\n- This example registers a factory that produces a new instance of `MyService` each time it is resolved. This is useful for dependencies that should not be shared or cached.\n\n#### Aliasing Types\nIt is common practice to write Protocols to define the functionality of a dependency, and then rely on that protocol in the rest of your application. This allows you to easily switch between different concrete versions of that protocol. You can easily alias a registered type to match that of a protocol, by using the `type` parameter of all the registration functions.\n\n```swift\nprotocol MovieDatabase {  }\nclass CoreDataMovieDatabase: MovieDatabase {  }\n\nContainer.registerSingleton(as: MovieDatabase.self) { _ in\n    CoreDataMovieDatabase()\n}\n```\n\n- This example shows how you can register a concrete type as a protocol type.\n\n#### Named Registrations\nIn Hydrate, you can register multiple instances of the same type by using names. This is useful when you have different configurations or variations of the same dependency.\n\n```swift\nContainer.registerSingleton(named: \"transientRepository\") { resolver in\n    MovieRepository(api: resolver.resolve(MovieAPI.self))\n}\n\nContainer.registerSingleton(as: MovieRepository.self, named: \"cachedRepository\") { resolver in\n    // CachedMovieRepository is a child class of MovieRepository, so it can be cast here.\n    CachedMovieRepository(api: resolver.resolve(MovieAPI.self), db: resolver.resolve(MovieDatabase.self))\n}\n```\n\n- This example shows two different versions of the same type being registered, by providing a name you can differentiate between the two versions.\n\n---\n\n### Resolving Dependencies\nOnce your dependencies are registered, resolving them in Hydrate is very straightforward. The framework provides several ways to access and inject your dependencies, ensuring they are available wherever you need them in your application.\n\n\u003e [!WARNING]\n\u003e Attempting to resolve a dependency with no matching provider registered will result in a runtime error.\n\n#### Resolving A Dependency\nThe most direct way to resolve a dependency is by using the `resolve` method on a `Container`:\n\n```swift\nlet repository = Container.resolve(MovieRepository.self)\n```\n\n- In this example, the `MovieRepository` type is resolved from the shared `Container`. The resolved instance is returned and assigned to the `repository` constant.\n\n#### Resolving a Named Dependency\nIf you registered a dependency with a specific name, you can resolve it by specifying the name during resolution:\n\n```swift\nlet transientRepository = Container.resolve(MovieRepository.self, named: \"transientRepository\")\nlet cachedRepository = Container.resolve(MovieRepository.self, named: \"cachedRepository\")\n```\n\n- This example demonstrates how to resolve two different instances of `MovieRepository` that were registered using names.\n\n#### Resolving with the `Hydrated` Property Wrapper\n\nTo simplify dependency injection, Hydrate provides the `Hydrated` property wrapper. This wrapper automatically resolved and injects dependencies when they are accessed:\n\n```swift\nclass MovieVM {\n    @Hydrated var service: MyService\n    \n    init() { }\n    \n    func useService() {\n        service.performTask()\n    }\n}\n```\n\n- In this example, the `@Hydrated` property wrapper automatically resolves `MyService` from the shared `Container` when the `service` property is accessed. This makes your code cleaner, and reduces boilerplate!\n\n#### Resolving Named Dependencies When Using The `Hydrated` Property Wrapper.\n\nThe `@Hydrated` property wrapper can take a name parameter to allow you to resolve named dependencies.\n\n```swift\nclass MovieVM {\n    @Hydrated(name: \"cachedRepository\") var repository: MovieRepository\n    \n    init() { }\n    \n    func getMovies() {\n        repository.getMovies()\n    }\n}\n```\n\n---\n\n### Using Multiple Containers\nHydrate provides a convenient shared `Container` for global dependency management; however, there are scenarios where you may want to seperate your logic into multiple containers. This approach allows you to isolate dependencies for different parts of your application, making it easier to manage configurations, testing, and modular development.\n\n#### Creating a Container\nYou can create a new `Container` instance whenever you need to manage dependencies separately from the shared container:\n\n```swift\nlet customContainer = Container()\n```\n\n- This example shows how to create a new `Container` instance, which operates independently of the shared container. You can register and resolve dependencies in this container without affecting the shared instance.\n\n#### Registering Dependencies in a Custom Container\nWhen registering dependencies on the shared `Container` Hydrate provides static functions to reduce boilerplate. Each of these functions, however, has an instance function match that can be used on your instance.\n\n```swift\n// Using the shared `Container`\nContainer.registerSingleton {\n    MyService()\n}\n\n// Using a custom `Container`\ncustomContainer.registerSingleton {\n    MyService()\n}\n```\n\n#### Resolving Dependencies from a Custom Container\nJust like with registering dependencies, each of the static `Container` functions that can be used with the shared `Container`, has an instance function match that can be used on your instance.\n\n```swift\n// Using the shared `Container`\nlet service = Container.resolve(MyService.self)\n\n// Using a custom `Container`\nlet service = customContainer.resolve(MyService.self)\n```\n\n#### Resolving Dependencies from a Custom Container Using the `Hydrated` property wrapper.\nWhen using the `@Hydrated` property wrapper, you can optionally provide a `Container` instance for the property wrapper to resolve its dependencies from, if no `Container` is provided, the shared one will be used.\n\n```swift\nclass MovieVM {\n    @Hydrated(container: customContainer) var service: MyService\n    \n    init() { }\n    \n    func useService() {\n        service.performTask()\n    }\n}\n```\n\n---\n\n### Custom Providers\nWhile Hydrate offers built-in mechanisms for managing dependencies through singleton and factory providers, there may be times when you need more control over how dependencies are created or managed. Custom providers give you this flexibility by allowing you to define your own logic for creating and providing dependencies.\n\n#### Implementing a Custom Provider\nTo create a custom provider, you need create a type that conforms to the `Provider` protocol. This protocol requires a `provide(with:)` method, which is responsible for generating or retrieving the dependency.\n\n```swift\nstruct ExpirableSingletonFactory\u003cValue\u003e: Provider {\n    \n    let duration: TimeInterval\n    let factory: (Resolver) -\u003e Value\n    \n    var expirationDate: Date = .now\n    var value: Value? = nil\n    \n    mutating func provide(with resolver: Resolver) -\u003e Value {\n        if let value, expirationDate \u003e .now {\n            return value\n        }\n        \n        self.value = factory(resolver)\n        self.expirationDate = .now.advanced(by: duration)\n        return self.value!\n    }\n}\n```\n\n- This example, `ExpirableSingletonFactory` conforms to the `Provider` protocol and provides a custom implementation for creating an instance of a generic `Value`. It allows the specification of a duration that the singleton should exist before being re-resolved. The `provid(with:)` method can include any logic necessary to configure or retrieve the service, such as resolving additional dependencies.\n\n#### Registering a Custom Provider\nOnce you've created a custom provider, you can register it with a container using the `registerProvider()` method. You can either use the static method to add it to the shared `Container` or use an instance method on any custom `Containers`\n\n```swift\nContainer.registerProvider(\n    // New Dependency Resolved Every 5 Minutes.\n    ExpirableSingletonFactory(duration: 300) { _ in\n        ExpirableDependecy()\n    }\n)\n```\n\n#### Resolving When Using A Custom Provider\nNothing changes about the resolution process when using a provider, you can do it all the same way, but your custom provider will be used when resolving the dependency attached to it.\n\n## Installation\n\n### Swift Package Manager\n\n[Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the Swift compiler.\n\nTo add [Hydrate](https://github.com/Lukas-Simonson/Hydrate) to your project do the following.\n- Open Xcode\n- Click on `File -\u003e Add Packages`\n- Use this repositories URL (https://github.com/Lukas-Simonson/Hydrate.git) in the top right of the window to download the package.\n- When prompted for a Version or a Branch, we suggest you use the branch: main\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukas-simonson%2Fhydrate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flukas-simonson%2Fhydrate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukas-simonson%2Fhydrate/lists"}