A new approach to Container-Based Dependency Injection for Swift and SwiftUI.

Overview

A new approach to Container-Based Dependency Injection for Swift and SwiftUI.

Why do something new?

Resolver was my first Dependency Injection system. This open source project, while quite powerful and still in use in many applications, suffers from a few drawbacks.

  1. Resolver requires pre-registration of all service factories up front.
  2. Resolver uses type inference to dynamically find and return registered services in a container.

While the first issue could lead to a performance hit on application launch, in practice the registration process is usually quick and not normally noticable. No, it's the second item that's somewhat more problematic.

Failure to find a matching type could lead to an application crash if we attempt to resolve a given type and if a matching registration is not found. In real life that isn't really a problem as such a thing tends to be noticed and fixed rather quickly the very first time you run a unit test or when you run the application to see if your newest feature works.

But... could we do better? That question lead me on a quest for compile-time type safety. Several other systems have attempted to solve this, but I didn't want to have to add a source code scanning and generation step to my build process, nor did I want to give up a lot of the control and flexibility inherent in a run-time-based system.

Could I have my cake and eat it too?

Features

Factory is strongly influenced by SwiftUI, and in my opinion is highly suited for use in that environment. Factory is...

  • Safe: Factory is compile-time safe; a factory for a given type must exist or the code simply will not compile.
  • Flexible: It's easy to override dependencies at runtime and for use in SwiftUI Previews.
  • Powerful: Like Resolver, Factory supports application, cached, shared, and custom scopes, customer containers, arguments, decorators, and more.
  • Lightweight: With all of that Factory is slim and trim, coming in at about 200 lines of code.
  • Performant: Little to no setup time is needed for the vast majority of your services, resolutions are extremely fast, and no compile-time scripts or build phases are needed.
  • Concise: Defining a registration usually takes just a single line of code.

Sound too good to be true? Let's take a look.

A simple example

Most container-based dependency injection systems require you to define in some way that a given service type is available for injection and many reqire some sort of factory or mechanism that will provide a new instance of the service when needed.

Factory is no exception. Here's a simple dependency registraion.

extension Container {
    static let myService = Factory<MyServiceType> { MyService() }
}

Unlike Resolver which often requires defining a plethora of registration functions, or SwiftUI, where defining a new environment variable requires creating a new EnvironmentKey and adding additional getters and setters, here we simply add a new Factory to the default container. When called, the factory closure is evaluated and returns an instance of our dependency. That's it.

Injecting and using the service where needed is equally straightforward. Here's one way to do it.

class ContentViewModel: ObservableObject {
    @Injected(Container.myService) private var myService
    ...
}

Here our view model uses an @Injected property wrapper to request the desired dependency. Similar to @EnvironmentObject in SwiftUI, we provide the property wrapper with a reference to a factory of the desired type and it handles the rest.

And that's the core mechanism. In order to use the property wrapper you must define a factory. That factory must return the desired type when asked. Fail to do either one and the code will simply not compile. As such, Factory is compile-time safe.

Factory

A Factory is a lightweight struct that manages a given dependency. And due to the lazy nature of static variables, a factory isn't instantiated until it's referenced for the first time.

When a factory is evaluated it provides an instance of the desired dependency. As such, it's also possible to bypass the property wrapper and call the factory directly.

class ContentViewModel: ObservableObject {
    // dependencies
    private let myService = Container.myService()
    private let eventLogger = Container.eventLogger()
    ...
}

You can use the container directly or the property wrapper if you prefer, but either way I'd suggest grouping all of a given object's dependencies in a single place and marking them as private.

Mocking and Testing

Examining the above code, one might wonder why we've gone to all of this trouble? Why not simply say let myService = MyService() and be done with it?

Or keep the container idea, but write something similar to this…

extension Container {
    static var myService: MyServiceType { MyService() }
}

Well, the primary benefit one gains from using a container-based dependency injection system is that we're able to change the behavior of the system as needed. Consider the following code:

struct ContentView: View {
    @StateObject var model = ContentViewModel1()
    var body: some View {
        Text(model.text())
            .padding()
    }
}

Our ContentView uses our view model, which is assigned to a StateObject. Great. But now we want to preview our code. How do we change the behavior of ContentViewModel so that its MyService dependency isn't making live API calls during development?

It's easy. Just replace MyService with a mock.

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let _ = Container.myService.register { MockService2() }
        ContentView()
    }
}

Note the line in our preview code where we're gone back to our container and registered a new factory closure. One that provides a mock service that also conforms to MyServiceType.

Now when our preview is displayed ContentView creates a ContentViewModel which in turn depends on myService using the Injected property wrapper. But when the factory is asked for an instance of MyServiceType it now returns a MockService2 instance instead of the MyService instance originally defined.

We can do this because we originally defined the result of the myService factory to be the protocol MyServiceType

extension Container {
    static let myService = Factory<MyServiceType> { MyService() }
}

If not specialized, the type of the factory is inferred to be the type returned by the factory closure. You could also get the same result from a cast. Both are equivalent.

extension Container {
    static let myService = Factory { MyService() as MyServiceType }
}

If we have several mocks that we use all of the time, we can also add a setup function to the container to make this easier.

extension Container {
    static func setupMocks() {
        myService.register { MockServiceN(4) }
        sharedService.register { MockService2() }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let _ = Container.setupMocks()
        ContentView()
    }
}

This is a powerful concept that lets us reach deep into a chain of dependencies and alter the behavior of a system as needed.

But Factory has a few more tricks up it's sleeve.

Scope

If you've used Resolver or some other dependency injection system before then you've probably experienced the benefits and power of scopes.

And if not, the concept is easy to understand: Just how long should an instance of an object live?

You've no doubt created a singleton in your apps at some point in your career. This is an example of a scope. A single instance is created and used and shared by all of methods and functions in the app.

This can be done in Factory simply by adding a scope attribute.

extension Container {
    static let myService = Factory<MyServiceType>(scope: .singleton) { MyService() }
}

Now whenever someone requests an instance of myService they'll get the same instance of the object as everyone else.

Unless altered, the default scope is unique; every time the factory is asked for an instance of an object it will get a new instance of that object.

Other common scopes are cached and shared. Cached items are saved until the cache is reset, while shared items persist just as long as someone holds a strong reference to them. When the last reference goes away, the weakly held shared reference also goes away.

You can also add your own special purpose scopes to the mix.

extension Container.Scope {
    static var session = Cached()
}

extension Container {
    static let authentication = Factory(scope: .session) { Authentication() }
}

Once created, a single instance of Authentication will be provided to anyone that needs one... up until the point where the session scope is reset, perhaps by a user logging out.

func logout() {
    Container.Scope.session.reset()
    ...
}

Scopes are powerful tools to have in your arsenal. Use them.

Constructor Injection

At times we might prefer (or need) to use a technique known as constructor injection where dependencies are provided to an object upon initialization.

That's easy to do in Factory. Here we have a service that depends on an instance of MyServiceType, which we defined earlier.

extension Container {
    static let constructedService = Factory { MyConstructedService(service: myService()) }
}

All of the factories in a container are visible to other factories in a container. Just call the needed factory as a function and the dependency will be provided.

Custom Containers

In a large project you might want to segregate factories into additional, smaller containers.

class OrderContainer: SharedContainer {
    static let optionalService = Factory<SimpleService?> { nil }
    static let constructedService = Factory { MyConstructedService(service: myServiceType()) }
    static let additionalService = Factory(scope: .session) { SimpleService() }
}

Just define a new container derived from SharedContainer and add your factories there. You can have as many as you wish, and even derive other containers from your own.

While a container tree makes dependency resolutions easier, don't forget that if need be you can reach across containers simply by specifying the full container.factory path.

class PaymentsContainer: SharedContainer {
    static let anotherService = Factory { AnotherService(OrderContainer.optionalService()) }
}

SharedContainer

Note that you can also add your own factories to SharedContainer. Anything added there will be visible on every container in the system.

extension SharedContainer {
    static let api = Factory<APIServiceType> { APIService() }
}

Unit Tests

Factory also has some provisions added to make unit testing eaiser. In your unit test setUp function you can push the current state of the registration system and then register and test anything you want.

Then in your tearDown function simply pop your changes to restore everything back to the way it was prior to running that test suite.

final class FactoryCoreTests: XCTestCase {

    override func setUp() {
        super.setUp()
        Container.Registrations.push()
     }

    override func tearDown() {
        super.tearDown()
        Container.Registrations.pop()
    }
    
    ...
}

Installation

Factory is available as a Swift Package. Just add it to your projects.

License

Factory is available under the MIT license. See the LICENSE file for more info.

Author

Factory was designed, implemented, documented, and maintained by Michael Long, a Senior Lead iOS engineer at CRi Solutions. CRi is a leader in developing cutting edge iOS, Android, and mobile web applications and solutions for our corporate and financial clients.

He was also one of Google's Open Source Peer Reward winners in 2021 for his work on Resolver.

Additional Resources

Comments
  • Support for Async/Await Concurrency in Factory

    Support for Async/Await Concurrency in Factory

    Hey @hmlongco,

    First of all would like to say well done on creating both Resolver and Factory. I have been using Resolver in production for over a year now and really happy on how much it helped us clean up our code in regards to dependency injection.

    A pain point that we had with Resolver was when we migrated our code to the the new Concurrency model when using Mainactor or other types of Actors in general.

    Now that Factory seems to be a full re-write of Resolver with new features in mind, I wonder if now is a good time for you (us?) to think a bit more on how to natively support concurrency out of the box. Looking at the code, the actor model should be able to help you a lot when it comes to thread safety.

    Is this something that you're open to implement/discuss for Factory?

    opened by TheCoordinator 17
  • [Question] How to use with a VIPER architecture?

    [Question] How to use with a VIPER architecture?

    I love the simplicity of this DI package, but I'm finding the documentation a little confusing. My adoption of the package would depend on how it deals with a 'Clean Architecture' used in a professional workspace. Right now I'm struggling to understand how to use this package with a VIPER architecture. We use a similar structure to here: https://github.com/theswiftdev/tutorials/blob/master/VIPER/VIPERAndSwiftUI/VIPERAndSwiftUI/Sources/VIPER.swift

    I began with this (below) but it doesn't compile (not even sure this is the correct approach):

    import Foundation
    import SwiftUI
    import Factory
    
    // MARK: - Router
    typealias ListViewRouterInterface = any RouterInterface & ListViewRouterPresenterInterface
    
    protocol ListViewRouterPresenterInterface: RouterPresenterInterface {
        func showDetails(for animal: Animal) -> any View
    }
    
    // MARK: - Presenter
    
    typealias ListViewPresenterInterface = any PresenterInterface & ListViewPresenterRouterInterface & ListViewPresenterInteractorInterface & ListViewPresenterViewInterface
    
    protocol ListViewPresenterRouterInterface: PresenterRouterInterface {
    
    }
    
    protocol ListViewPresenterInteractorInterface: PresenterInteractorInterface {
        func didLoad(animals: [Animal])
    }
    
    protocol ListViewPresenterViewInterface: PresenterViewInterface {
        var viewModel: ListViewModel? { get set }
        func onAppear()
        func onBtnPress(animal: String)
    }
    
    // MARK: - Interactor
    typealias ListViewInteractorInterface = any InteractorInterface & ListViewInteractorPresenterInterface
    
    protocol ListViewInteractorPresenterInterface: InteractorPresenterInterface {
        func fetchItems()
    }
    
    extension Container {
        static let apiService = Factory<APIServiceProtocol>(scope: .shared) { APIService() }
    }
    
    class ListViewContainer: SharedContainer {
        static let viewModel = Factory<ListViewModel>(scope: .shared) { ListViewModel() }
        static let presenter = Factory<ListViewPresenterInterface>(scope: .shared) { ListViewPresenter() }
        static let interactor = Factory<ListViewInteractorInterface>(scope: .shared) { ListViewInteractor() }
        static let router = Factory<ListViewRouterInterface>(scope: .shared) { ListViewRouter() }
    }
    
    final class ListViewModule {
        @Injected(ListViewContainer.viewModel) private var viewModel
        @Injected(ListViewContainer.presenter) private var presenter
        @Injected(ListViewContainer.interactor) private var interactor
        @Injected(ListViewContainer.router) private var router
    
        func build() -> some View {
            presenter.viewModel = self.viewModel
            let view = ListView(presenter: presenter)
            presenter.interactor = self.interactor
            presenter.router = self.router
            interactor.presenter = self.presenter
            router.presenter = self.presenter
            return view
        }
    

    Do you have any advice on how to do this in a VIPER architecture? I think this would be a great example to add to your samples folder. The fact that VIPER has various Input and Output contracts make it difficult.

    opened by elprl 14
  • Crash when using singleton

    Crash when using singleton

    I have found a SIGABRT error occurs under a specific scenario. In this case, I am trying to provide a protocol for UIApplication, and I am setting this as a singleton (this is so that I can provide a mock during testing). The issue appears to be caused when setting the scope as singleton, and the factory itself is a singleton. I am providing the code here which causes the issue. All you need to do is run the test. FactoryDemo.zip

    opened by totidev 11
  • [Question] - Migration from Resolver

    [Question] - Migration from Resolver

    Hi Michael,

    great to see another dependency injection library from you. I am a long time Resolver user and now questioning myself if it is possible to migrate from Resolver to Factory and how. I hope it is fine to ask some question about this in this format.

    Currently Resolver is used in a MicroFeature architecture. So I have a lot of distinct features, all with an interface target and an implementation target. These features then are getting packed together in the final application target.

    Each feature implementation only knows the interface of other features AND the Resolver framework. The application target in the end knows each features interface and each features implementation and registers appropriate implementations to the interfaces for other features to consume.

    Some Code examples below. Feature A

    // FeatureA interface
    
    protocol FeatureAInterface {
        func fetchResourceA() async -> ResourceA
    }
    
    // FeatureA implementation
    
    protocol FeatureAImplementation: FeatureAInterface {
        func fetchResourceA() async -> ResourceA {
            return ResourceA()
        }
    }
    

    Feature B

    // FeatureB interface
    
    protocol FeatureBInterface {
        func fetchResourceB() async -> ResourceB
    }
    
    // FeatureB implementation
    
    final class FeatureBImplementation: FeatureBInterface {
        
        @Injected private var featureA: FeatureAInterface   
        
        func fetchResourceB() async -> ResourceA {
            let a = await featureA.fetchResourceA() 
            return ResourceB()
        }
    }
    

    App

    import FeatureA
    import FeatureAInterface
    
    import FeatureB
    import FeatureBInterface
    
    register { FeatureAImplementation() }
        .implements(FeatureAInterface.self)
    
    register { FeatureBImplementation() }
        .implements(FeatureBInterface.self)
    

    When I now look at Factory I saw that it is now required to create an extension on Container to make services explicitly type safe available to consumers. Maybe I misunderstood the new library a little bit, but do you have some suggestion of how the behaviour I described will be working with Factory ? What I want to avoid is to expose the implementation of features to other features. Exposing implementations inside the place where everything is registered is fine though.

    Maybe another question - will it also be possible to have a @OptionalInjected functionality again ? Maybe I oversaw it.

    Thanks a lot in advance, and again thanks a lot for all your work so far !

    opened by alexanderwe 9
  • SharedContainer Resolve

    SharedContainer Resolve

    Hey @hmlongco! I was thinking in a way to resolve a Factory passing a type something like that:

    open class SharedContainer {
    
        public static func resolve<T>(scope: Scope) -> T? {
            resolveRegistration() ?? resolveScope(scope: scope)
        }
        
        static func resolveRegistration<T>() -> T? {
            let factories: [T] = Container.Registrations.registrations.compactMap { key, factory in
                guard let currentFactory: T = (SharedContainer.Registrations.factory(for: key) as? TypedFactory<Void, T>)?.factory(()) else {
                    return nil
                }
                return currentFactory
            }
            return factories.first
        }
        
        static func resolveScope<T>(scope: Scope) -> T? {
            scope.cache.compactMap { id, factory in
                factory.instance as? T
            }.first
        }
     }
    

    Do you think that this could be a way to do that? Or im not thinking in something?

    opened by joaoGMPereira 7
  • [Question] What's the best practice to use Factory in a multi-modules project?

    [Question] What's the best practice to use Factory in a multi-modules project?

    First, thanks for releasing awesome DI library. đź‘Ť I have one question. I thought as below, but this way seems to have some disadvantages of always having to specify the type as optional since it cannot give an initial value. If you have feature modules, what do you think is the best way to inject dependencies into each module?

    In a feature module,

    class AFeatureContainer: SharedContainer {
        static let optionalService = Factory<ServiceType?> { nil }
    }
    

    In a main module,

    AFeatureContainer.optionalService.register { Service() }
    
    opened by DevYeom 7
  • Graph Scope

    Graph Scope

    First of all, I would like to thank for this library. It's exactly what we were looking for.

    I was wondering why Factory does not have built-in GraphScope. I know that Resolver had that kind of functionality so I am wondering about reasons why it's not included here. I think it's possible implement it in this way:

    func mapInGraph<T>(_ resolve: (GraphIdentifier) -> T) -> T {
                // Will create new graph id if it is the root
                // Otherwise it will use graphID from parent graph
                let isRoot: Bool = Self.graphID == nil
                let graphID = Self.graphID ?? UUID()
    
                if isRoot {
                    Self.graphID = graphID
                }
    
                // Resolve instance
                let instance = resolve(graphID)
    
                if isRoot {
                    // reset graph ID when exiting the graph
                    Self.graphID = nil
                }
                return instance
            }
    

    cache would be then per graph:

    private var cache: [GraphIdentifier: [Identifier: any Box]] = .init(minimumCapacity: 64)
    

    and then resolve function has to be changed:

    func resolve<T>(id: Identifier, factory: () -> T) -> T {
                defer { lock.unlock() }
                lock.lock()
    
                return mapInGraph { graphID in
                    /// retrieve from cache based on graphID
                    /// ...
    
                    /// if not find in cache create new instance and store in cache based on grapID
                    /// ...
                }
            }
    

    There's some trickery that needs to be implemented because of multithreading but this is the basic concept. Do you see any problem with it?

    opened by tkohout 6
  • Add @InjectedObject property for ObservableObject compatibility

    Add @InjectedObject property for ObservableObject compatibility

    Hello, I've used Resolver for a while and just switched over to Factory. Both libraries help to circumvent common issues with EnvironmentObject, but both libraries also run into the same issue wherein you lose the ability to trigger SwiftUI views to update when a property on the object changes by default. This usually requires a bunch of boilerplate code and a required ViewModel, so there's easy way to make lightweight views the way one might with EnvironmentObject, which creates an inverse problem to the one presented by SwiftUI's EnvironmentObject.

    Curious what the expectation is for working around this, or if there are plans for something like an @InjectedObject (as in Resolver) property to help account for this? I saw issue #1, but it doesn't really go into detail, and the provided snippet doesn't actually support re-rendering views (see example below), so it's not really a valid workaround for achieving the same behavior by itself.

    This library seems like an extremely easy-to-use and easy-to-onboard dependency injection tool, but I would argue it's not really SwiftUI-compatible (or at least not SwiftUI-optimized) without functionality like this in place, and/or some comprehensive documentation in the README for what the expectations/workarounds are for dealing with this.

    Problem

    Say we want to pull AppSettings into a view, which is just a struct that has an environment property. Ideally, this would be all it takes to achieve that:

    struct MyView: View {
        @Injected(Container.appSettings) var appSettings
    
        var body: some View {
            Text(self.appSettings.environment)
        }
    }
    

    However, when appSettings.environment is updated, the view does not reload -- which is a known limitation of this library.

    If I take the snippet provided in issue #1, it doesn't resolve the problem -- the view still doesn't reload.

    struct MyView: View {
        @StateObject var viewModel = MyViewModel()
    
        var body: some View {
            Text(self.viewModel.appSettings.environment)
        }
    }
    
    final class MyViewModel: ObservableObject {
        @Injected(Container.appSettings) var appSettings
    }
    

    Instead, I have to do something like this:

    final class MyViewModel: ObservableObject {
        @Injected(Container.appSettings) var appSettings
    
        private var subscriptions = Set<AnyCancellable>()
        
        init() {
            // without this block, MyView will not trigger a state refresh.
            self.appSettings.objectWillChange.sink { _ in
                self.objectWillChange.send()
            }
            .store(in: &self.subscriptions)
        }
    }
    

    or this

    final class MyViewModel: ObservableObject {
        @Injected(Container.appSettings) var appSettings
        @Published var environment: Environment
    
        private var subscriptions = Set<AnyCancellable>()
        
        init() {
            // no different than the example above unless I optimize further
            self.appSettings.$environment
                .assign(to: &self.$environment)
        }
    }
    

    Proposed Solution

    A reimplementation of Resolver.InjectedObject to be included in this library:

    @propertyWrapper public struct InjectedObject<T>: DynamicProperty where T: ObservableObject {
        @ObservedObject private var dependency: T
        
        public init(_ factory: Factory<T>) {
            self.dependency = factory()
        }
        
        public var wrappedValue: T {
            get { return dependency }
            mutating set { dependency = newValue }
        }
        
        public var projectedValue: ObservedObject<T>.Wrapper {
            return self.$dependency
        }
    }
    

    I don't think we need to provide an implementation for StateObject explicitly since we're not worried about a view being the owner of the StateObject -- similar to how there is no EnvironmentStateObject and EnvironmentObservedObject, for example.

    That said, if we needed to (or it would help optimize or anything), we could also add InjectedStateObject for explicit conformance.

    opened by bdrelling 6
  • TestUnit: I can't register

    TestUnit: I can't register

    Hello,

    I read the other ticket about the register issue but it didn't help me and mine is a little bit different.

    The register method doesn't work ONLY during the TestUnit

    Here my situation:

    ---- Container ----

    class CustomContainer: SharedContainer {
            static let poc = Factory<String>(scope: .singleton) {
                "Initial project"
            }
    }
    

    ---- ViewModel ----

    final class ViewModelPoc {
        @LazyInjected(CustomContainer.poc) private var poc
    
        func unitTestMethod() {
            print("Result: \(poc)")
        }
    }
    

    ---- Test Unit ----

    class SimpleTests: XCTestCase {
    
        override func setUp() {
            super.setUp()
            CustomContainer.poc.register { "Test project" }
        }
    
        func testMethod() {
            ViewModelPoc().unitTestMethod()
        }
    }
    

    And when I run the test the consol display "Result: Initial project"

    If you have any suggestions ? Thanks

    opened by paul-lavoine 5
  • Question about Dynamic Registration example

    Question about Dynamic Registration example

    func authenticated(with user: User) {
        ...
        Container.userProviding.register { UserProvider(user: user) }
        ...
    }
    

    In the above example that you provide, what was the way that userProviding was initially registered? Should this be created as a nilable, a UserProvider that takes no user, or even a separate type conforming to UserProviding protocol?

    opened by totidev 4
  • Workspace and clean architecture setup

    Workspace and clean architecture setup

    Hi, I am not able to wrap my head around how to best use Factory in a multi-project setup, formed around a clean architecture pattern. This was easy with Resolver, but in Factory it seems to lead to circular references - unless you accept a "doubling" of references.

    Consider the following architecture in an XCWorkspace:

    App (contains the SwiftUI project with views and viewmodels) Core (framework that contains the protocols and models) Services (framework that contains the services, for example external api integrations) Bootstrap (framework that bootstraps Services and Core for dependency injection/ioc)

    In Factory, registrations made in Bootstrap will not be visible to App or Services.

    In a clean architecture using xcworkspace, separation is the key benefit. App should not know about the concrete implementations of the services, it will only operate on the protocols. Therefore, App does not have a reference to Services, only to Core where the protocols live. And because of that, a separate project needs to do the bootstrapping, ie Bootstrap.

    My Bootstrap project contains only this, and embeds the Core and Services projects to make this work:

    extension SharedContainer { static let httpClient = Factory(scope: .singleton) { HttpClient() as HttpClientProtocol } static let linksService = Factory(scope: .singleton) { LinksService() as LinksServiceProtocol } }

    My App project embeds the Bootstrap project and the Core project

    My Services project embeds Core.

    Services needs an instance of HttpClientProtocol: @Injected(Container.httpClient) private var httpClient: HttpClientProtocol

    But that will not work, since the registration is done in Bootstrap. Services has no notion of Bootstrap at all.

    Is there a way with Factory? If not, the Inversion of Control part of Dependency Injection is missing in this framework, and it will only work for monolith projects.

    opened by HaakonL 3
Releases(1.3.1)
Owner
Michael Long
I'm a technology consultant and specialist in mobile application development, web development, software engineering, and design.
Michael Long
Kraken - Simple Dependency Injection container for Swift. Use protocols to resolve dependencies with easy-to-use syntax!

Kraken Photo courtesy of www.krakenstudios.blogspot.com Introduction Kraken is a simple Dependency Injection Container. It's aimed to be as simple as

Syed Sabir Salman-Al-Musawi 1 Oct 9, 2020
ViperServices - Simple dependency injection container for services written for iOS in swift supporting boot order

ViperServices Introduction ViperServices is dependency injection container for iOS applications written in Swift. It is more lightweight and simple in

Siarhei Ladzeika 5 Dec 8, 2022
Container is a lightweight dependency injection framework for Swift.

Container Container is a lightweight dependency injection framework for Swift. Installation is available in the Swift Package Manager. Swift Package M

Aleksei Artemev 17 Oct 13, 2022
Deli is an easy-to-use Dependency Injection Container that creates DI containers

Deli is an easy-to-use Dependency Injection Container that creates DI containers with all required registrations and corresponding factories.

Jungwon An 134 Aug 10, 2022
Dip is a simple Dependency Injection Container.

Dip is a simple Dependency Injection Container. It's aimed to be as simple as possible yet p

Olivier Halligon 949 Jan 3, 2023
Injection - Dependency injection using property wrappers

Dependency injection using property wrappers. Registering types: // injecting a

Alejandro Ramirez 3 Mar 14, 2022
Pilgrim - Dependency injection for Swift (iOS, OSX, Linux). Strongly typed, pure Swift successor to Typhoon.

pilgrim.ph Pilgrim is a dependency injection library for Swift with the following features: Minimal runtime-only library that works with pure Swift (s

AppsQuick.ly 60 Oct 24, 2022
Injector - A Swift package for simple dependency injection that also supports Swift UI previews

A Swift package for simple dependency injection that also supports Swift UI prev

null 6 Aug 9, 2022
Typhoon Powerful dependency injection for Cocoa and CocoaTouch.

Typhoon Powerful dependency injection for Cocoa and CocoaTouch. Lightweight, yet full-featured and super-easy to use. Pilgrim is a pure Swift successo

AppsQuick.ly 2.7k Dec 14, 2022
Cleanse is a dependency injection framework for Swift.

Cleanse - Swift Dependency Injection Cleanse is a dependency injection framework for Swift. It is designed from the ground-up with developer experienc

Square 1.7k Dec 16, 2022
DIKit Dependency Injection Framework for Swift, inspired by KOIN.

DIKit Dependency Injection Framework for Swift, inspired by KOIN. Basically an implementation of service-locator pattern, living within the applicatio

null 95 Dec 22, 2022
Tranquillity is a lightweight but powerful dependency injection library for swift.

DITranquillity Tranquillity is a lightweight but powerful dependency injection library for swift. The name "Tranquillity" laid the foundation in the b

Ivlev Alexander 393 Dec 24, 2022
Swinject is a lightweight dependency injection framework for Swift.

Swinject Swinject is a lightweight dependency injection framework for Swift. Dependency injection (DI) is a software design pattern that implements In

null 5.6k Dec 31, 2022
Dependency Injection framework for Swift (iOS/macOS/Linux)

Declarative, easy-to-use and safe Dependency Injection framework for Swift (iOS/macOS/Linux) Features Dependency declaration via property wrappers or

Scribd 684 Dec 12, 2022
Swift Ultralight Dependency Injection / Service Locator framework

Swift Ultralight Dependency Injection / Service Locator framework

Michael Long 1.9k Jan 6, 2023
Needle - Compile-time safe Swift dependency injection framework

Needle is a dependency injection (DI) system for Swift. Unlike other DI frameworks, such as Cleanse, Swinject, Needle encourages hierarchical DI struc

Uber Open Source 1.4k Jan 3, 2023
CarbonGraph - A Swift dependency injection / lookup framework for iOS

CarbonGraph is a Swift dependency injection / lookup framework for iOS. You can

Baidu 244 Jan 4, 2023
Toledo - a dependency injection library for Swift that statically generates resolvers at compile-time.

Toledo Toledo is a dependency injection library for Swift that statically generates resolvers at compile-time. Index Features Installation Usage Licen

Valentin Radu 18 Nov 25, 2022
Effortless modular dependency injection for Swift.

Inject Effortless modular dependency injection for Swift. Sometimes during the app development process we need to replace instances of classes or acto

Maxim Bazarov 40 Dec 6, 2022