Impose is a simple dependency injection library for Swift

Overview

Impose

Impose is a simple dependency injection library for Swift

codebeat badge build test SwiftPM Compatible Version License Platform

Requirements

  • Swift 5.0 or higher (or 5.3 when using Swift Package Manager)
  • iOS 9.3 or higher (or 10 when using Swift Package Manager)

Only Swift Package Manager

  • macOS 10.10 or higher
  • tvOS 10 or higher

Installation

Cocoapods

Impose is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'Impose', '~> 1.2'

Swift Package Manager from XCode

  • Add it using XCode menu File > Swift Package > Add Package Dependency
  • Add https://github.com/hainayanda/Impose.git as Swift Package URL
  • Set rules at version, with Up to Next Major option and put 1.2.6 as its version
  • Click next and wait

Swift Package Manager from Package.swift

Add as your target dependency in Package.swift

dependencies: [
    .package(url: "https://github.com/hainayanda/Impose.git", .upToNextMajor(from: "1.2.6"))
]

Use it in your target as Impose

 .target(
    name: "MyModule",
    dependencies: ["Impose"]
)

Author

Nayanda Haberty, [email protected]

License

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

Basic Usage

Impose is very easy to use and straightforward, all you need to do is provide some provider for dependency:

Imposer.impose(for: Dependency.self, SomeDependency())

and then use it in some of your classes using property wrapper or using global function

class InjectedByPropertyWrapper {
    @Injected var dependency: Dependency
    
    ...
    ...
}

class InjectedByInit {
    var dependency: Dependency
    
    init(dependency: Dependency = inject()) {
        self.dependency = dependency
    }
}

the provider is autoClosure type, so you can do something like this:

Imposer.impose(for: Dependency.self) {
    dependency: SomeDependency = .init()
    dependency.doSomeSetup()
    return dependency
}

the provider automatically just create one instance only from calling closure and reused the instance, so the closure only called once. If you want the provider to call closure for every injection, you can just pass the option:

Imposer.impose(for: Dependency.self, option: .closureBased, SomeDependency())

or if you want to set it to a single instance explicitly:

Imposer.impose(for: Dependency.self, option: .singleInstance, SomeDependency())

Don't forget that it will throw an uncatchable Error if the provider is not registered yet. If you want to catch the error manually, just use tryInject instead:

class InjectedByInit {
    var dependency: Dependency
    
    init(dependency: Dependency? = nil) {
        do {
            self.dependency = dependency ?? try tryInject()
        } catch {
            self.dependency = DefaultDependency()
        }
    }
}

Optional Inject

Sometimes you just don't want your app to be throwing errors just because it's failing in dependency injection. In those cases, just use @UnforceInjected attribute or unforceInject function. It will return nil if injection fail:

class InjectedByPropertyWrapper {
    @UnforceInjected var dependency: Dependency?
    
    ...
    ...
}

class InjectedByInit {
    var dependency: Dependency
    
    init(dependency: Dependency? = unforceInject()) {
        self.dependency = dependency
    }
}

No Match Rules

If the Imposer did not found the exact type registered but multiple compatible types, it will use the nearest one to the requested type. Like in this example:

protocol Dependency {
    ...
    ...
}

class NearestToDependency: Dependency {
    ...
    ...
}

class MidwayToDependency: NearestToDependency {
    ...
    ...
}

class FurthestToDependency: MidwayToDependency {
    ...
    ...
}

so if you provide dependency like this:

Imposer.impose(for: NearestToDependency.self, NearestToDependency())
Imposer.impose(for: MidwayToDependency.self, MidwayToDependency())
Imposer.impose(for: FurthestToDependency.self, FurthestToDependency())

and you try to inject Dependency protocol which Imposer already have three candidates for that, by default Imposer will return NearestToDependency since its the nearest one to Dependency:

class MyClass {
    // this will be NearestToDependency
    @Injected var dependency: Dependency
}

but if you want to get another dependency, you could pass InjectionRules:

  • nearest which will return the nearest one found
  • furthest which will return the furthest one found
  • nearestAndCastable same like nearest, but will be using type casting too when searching dependency
  • furthestAndCastable same as furthest, but will be using type casting too when searching dependency
class MyClass {
    // this will be NearestToDependency
    @Injected var dependency: Dependency
    
    // this will be FurthestToDependency
    @Injected(ifNoMatchUse: .furthest) var furthestDependency: Dependency
}

it can apply to the inject function too:

// this will be NearestToDependency
var dependency: Dependency = inject()

// this will be FurthestToDependency
var furthestDependency: Dependency = inject(ifNoMatchUse: .furthest)

Keep in mind, using nearestAndCastable and furthestAndCastable will create/using the dependency instance and cast it to Dependency needed, so if the instance injected using one or more Dependencies that circular with itself, it will be raising a stack overflow, so it's better to avoid it unless you really need it and make sure the dependency is safe.

Multiple Imposer

You could have multiple Imposer to provide different dependencies for the same type by using ImposerType. ImposerType is an enumeration to mark the Imposer:

  • primary which is the default Imposer
  • secondary which is the secondary Imposer where the Imposer will search if dependency is not present in the primary
  • custom(AnyHashable) which is the optional Imposer where the Imposer will search if dependency is not present in the primary or secondary

To use ImposerType other than primary, use static method imposer(for:). It will search the Imposer for given type and create new if the Imposer did not found:

let secondaryImposer = Imposer.imposer(for: .secondary)
secondaryImposer.impose(for: Dependency.self, SomeDependency())

Then pass the type to propertyWrapper or global function as the first parameter:

class InjectedByPropertyWrapper {
    @Injected(from: .secondary) var dependency: Dependency
    
    ...
    ...
}

class InjectedByInit {
    var dependency: Dependency
    
    init(dependency: Dependency = inject(from: .secondary)) {
        self.dependency = dependency
    }
}

It will search the dependency from the Imposer for the given type and if the dependency is not found, it will try to search from the other available Imposer started from primary

Module Injector

If you have a modular project and want the individual module to inject everything manually by itself. You can use ModuleInjector protocol, and use it as a provider in the main module:

// this is in MyModule
class MyModuleInjector: ModuleInjector {
    var type: ImposerType { .primary }
    
    func provide(for imposer: Imposer) {
        imposer.impose(for: Dependency.self, SomeDependency())
    }
}

then let's say in your AppDelegate:

import Impose
import MyModule

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(
            _ application: UIApplication,
            didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        provideDependencies()
        // do something
        return true
    }
    
    func provideDependencies() {
        Imposer.provide(using: MyModuleInjector())
    }
}

It will call provide(using:) with primary Imposer. type of imposer is optional, the default value is primary. You can add as many ModuleInjector as you need, but if the Module provides the same Dependency for the same type of Imposer, it will override the previous one with the new one.

Contribute

You know how, just clone and do pull request

You might also like...
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

Swinject is a lightweight dependency injection framework for Swift.
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

Dependency Injection framework for Swift (iOS/macOS/Linux)
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

Swift Ultralight Dependency Injection / Service Locator framework
Swift Ultralight Dependency Injection / Service Locator framework

Swift Ultralight Dependency Injection / Service Locator framework

Needle - Compile-time safe Swift dependency injection framework
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

CarbonGraph - A Swift dependency injection / lookup framework for iOS

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

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

A new approach to Container-Based Dependency Injection for Swift and SwiftUI. Why do something new? Resolver was my first Dependency Injection system.

Container is a lightweight dependency injection framework for Swift.
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

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

Comments
  • Remove test dependency from distribution

    Remove test dependency from distribution

    Since some projects will have different Quick and Nimble versions, and it shouldn't include in the distribution, it is now removed from dependency in Swift Package Manager version.

    opened by hainayanda 0
  • Minor fixes

    Minor fixes

    • Fix weak Injector injecting scoped instead of itself
    • Improve resolver potential search algorithm
    • Add reset and remove for resolver
    • Minor refactoring
    opened by hainayanda 0
  • New Improved Impose

    New Improved Impose

    • Simplify and improve structure and algorithm
    • Make a more explicit method instead of using enumeration for selecting the resolving type (transient, singleton and scoped)
    • Add new scoped provider that can be released by using ImposeContext
    • Deprecate the old Imposer
    • Refactor
    opened by hainayanda 0
Releases(3.1.4)
Owner
Nayanda Haberty
Programmer. What else?
Nayanda Haberty
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
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
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
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
A simple way to handle dependency injection using property wrappers

Injektion Introduction A simple way to handle dependency injection using propert

Andrew McGee 2 May 31, 2022
StoryboardBuilder - Simple dependency injection for generating views from storyboard.

StoryboardBuilder Simple dependency injection for generating views from storyboard. Description StoryboardBuilder is framework to help simply and easi

null 5 Jun 13, 2019
DIContainer Swift is an ultra-light dependency injection container made to help developers to handle dependencies easily. It works with Swift 5.1 or above.

?? DIContainer Swift It is an ultra-light dependency injection container made to help developers to handle dependencies easily. We know that handle wi

Victor Carvalho Tavernari 10 Nov 23, 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
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