Skip to content

Latest commit

History

127 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

DIKit - Dependency Injection Kit

CI

Swift library that allows you to use a dependency injection pattern in your project by creating a container that holds all the dependencies in one place.

Create container

Most recommended way to create a container is to use assemblies. Assemblies are classes that conform to Assembly protocol and are responsible for registering dependencies in the container.

Container(assemblies:[FoundationAssembly(),APIAssembli(),DataBaseAssembly(),ThemeAssembly()])

SwiftUI

If you want to use DIKit in SwiftUI you can create a container in the App struct like this:

@mainstructMyApp:App{letcontainer=Container(assemblies:[...])varbody:someScene{WindowGroup{ContentView().environmentObject(container.toObservable())}}}
// somewhere in the code
structContentView:View{@DIObservedObjectvarapi:APIvarbody:someView{Button("Make request"){
api.request()}}}

Shared container

If you want to use the container in property wrappers (InjectLazy, InjectProvider..) you can create a shared container like this:

letcontainer=Container(assemblies:[...])
container.makeShared() // <-- make container shared
// somewhere in the code
finalclassSomeManager{@InjectLazyvarapi:APIfunc makeRequest(){
api.request()}}

Create assembly

Most basic assembly should look like this:

finalclassApplicationAssembly:Assembly{
// list of assemblies that this assembly depends on
vardependencies:[Assembly]{return[AnalyticsAssembly(),RouterAssembly(),StoragesAssembly(),ThemeAssembly(),UIComponentsAssembly()]}func assemble(with registrator:Registrator){
registrator.register(UserDefaults.self){returnUserDefaults.standard
}
registrator.register(NotificationCenter.self){returnNotificationCenter.default
}
registrator.register(UIApplication.self, options:.transient){returnUIApplication.shared
}
registrator.register(BuildMode.self, entity:BuildMode.init)
registrator.register(UserManager.self, options:.container, entity:UserManager.init)}}

How to resolve dependencies

At any place where you have access to container you can resolve dependencies like this:

letapi:API= container.resolve()letdataBase:DataBase= container.resolve()

or if container is shared you can use:

@InjectLazyvarapi:API@InjectProvidervardataBase:DataBase

of SwiftUI:

@DIObservedObjectvarapi:API@DIStateObjectvarviewState:ReCreatedState@DIProvidervardataBase:DataBase

Registration

Most basic registration looks like this:

registrator.register(BuildMode.self, entity:BuildMode.init)

options

  • container - creates a single instance and stores it in the container (like a singleton)
  • weak - resolve weak reference and if it was deallocated it will be resolved again
  • transient - resolve new instance every time, never store it in the container

'Named' option

You can register multiple instances of the same type with different names and resolve them by name.

registrator.register(Theme.self, name:"light"){returnLightTheme()}
registrator.register(Theme.self, name:"dark"){returnDarkTheme()}

and resolve it like this:

letlightTheme:Theme= container.resolve(name:"light")letdarkTheme:Theme= container.resolve(name:"dark")@InjectLazy(named:"light")varlightTheme:Theme@InjectLazy(named:"dark")vardarkTheme:Theme

'.implements'

Multiple implementations of different protocols in one class

registrator.register(UserDefaults.self){returnUserDefaults.standard
}.implements(DefaultsStorage.self)

Custom entity key (EntityKeyProviding)

The container derives the storage key for each registration from the type's runtime metadata (ObjectIdentifier). For most types this is unique and stable.

There is one Swift-level exception: parameterized existentials (any P<X>). For such types String(reflecting:) returns the literal string "<<< invalid type >>>", and _mangledTypeName can return nil when the generic parameter crosses a module boundary. DIKit itself now relies on ObjectIdentifier instead, which works for the common cases — but if you hit a crash like this:

DIKit/Container.swift:?: Fatal error: <<< invalid type >>> is already registered with <<< invalid type >>>

two distinct registrations have collapsed onto the same key. Conform the type to EntityKeyProviding to pin an explicit key:

import DIKit
extensionFeatureFlagManaging:EntityKeyProviding{publicstaticvarentityKey:String{"FeatureFlagManaging<FeatureFlag>"}}

The same key is used for both registration and resolution, so every call site for that type sees the same storage bucket. Two unrelated types may deliberately share the same entityKey to alias one onto the other.

Arguments

You can pass arguments to the registration block

when you don’t know the index of the argument, but you are sure that there is only one type in the arguments:

registrator.register(BlaBla.self, options:.transient){ _, args inreturnBlaBla(name: args.first())<-- by type
}

when you know index of argument:

registrator.register(BlaBla.self, options:.transient){ _, args inreturnBlaBla(name:args[1])<-- by index
}

About

Swift library that allows you to use a dependency injection pattern in your project by creating a container that holds all the dependencies in one place

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Contributors

Languages