GithubHelp home page GithubHelp logo

ivianuu / injekt Goto Github PK

View Code? Open in Web Editor NEW
110.0 4.0 2.0 15.21 MB

Next gen dependency injection library for Kotlin [WIP]

Kotlin 100.00%
dependency-injection kotlin compile-time-dependency-injection compiler-plugin kotlin-compiler-plugin

injekt's Introduction

Injekt

Next gen dependency injection library for Kotlin.

@Provide fun jsonParser() = JsonParser()

interface Http

@Provide class RealHttp : Http

@Provide class Api(private val http: Http, private val jsonParser: JsonParser)

@Provide class Repository(private val api: Api)

val repo = inject<Repository>()

Setup

plugins {
  id("com.ivianuu.injekt") version latest_version
}

repositories {
  mavenCentral()
}

dependencies {
  // core runtime
  implementation("com.ivianuu.injekt:core:${latest_version}")
  // optional - common utilities
  implementation("com.ivianuu.injekt:common:${latest_version}")
}

Provide injectables

You can provide dependencies by annotating them with @Provide:

// classes and objects
@Provide class MyApi(baseUrl: BaseUrl)

// constructors
class MyService @Provide constructor(logger: Logger) {
  @Provide constructor()
}

// functions
@Provide fun okHttp(authenticator: Authenticator): OkHttpClient = ...

// properties and local variables
@Provide val apiKey: ApiKey = ...

// value parameters
fun run(@Provide config: Config) {
}

Multi injection

You can inject all injectables of a given type by injecting a List<T>

@Provide fun singleElement(): String = "a"
@Provide fun multipleElements(): Collection<String> = listOf("b", "c")

fun main() {
  inject<List<String>>() == listOf("a", "b", "c") // true
}

All elements which match E or Collection<E> will be included in the resulting list.

Scoping

The core of Injekt doesn't know anything about scoping, but there is a api in the common module. You have to annotate your class or the return type of a function or a property with @Scoped tag.

@Provide @Scoped<UiScope> class Db

Then you have to provide a Scope instance.

// use a object as name for the scope
object UiScope

Then you can inject your class.

@Provide val uiScope = Scope<UiScope>()

fun onCreate() {
  // use ui scoped dependency
  val db = inject<Db>()
}

Modules

There is no @Module annotation in Injekt instead a module is just a provided class which contains more @Provide declarations

// object module which is marked with @Provide
// can be used to organize injectables
@Provide object DatabaseModule {
  @Provide fun databaseFile(): File = ...
}

// module with parameters which can be provided later
class NetworkModule(val apiKey: String) {
  @Provide fun api(): Api = ...
}

fun main() {
  @Provide val networkModule = NetworkModule(if (isDebug) ... else ...)
  inject<Api>()
}

Components

There is also no @Component annotation in Injekt instead a component can be declared like this

@Provide class ActivityComponent(
  val api: Api,
  val createFragmentComponent: (Fragment, Scope<FragmentScope>) -> FragmentComponent
)

Function injection

Sometimes you want to delay the creation, need multiple instances, want to provide additional parameters, or to break circular dependencies. You can do this by injecting a function.

// inject a function to create multiple Tokens
fun run(tokenFactory: () -> Token = inject) {
  val tokenA = tokenFactory()
  val tokenB = tokenFactory()
}

// inject a function to create a MyViewModel with the additional String parameter
@Composable fun MyScreen(viewModelFactory: (String) -> MyViewModel = inject) {
  val viewModel = remember { viewModelFactory("user_id") }
}

// break circular dependency
@Provide class Foo(val bar: Bar)
@Provide class Bar(foo: (Bar) -> Foo) {
   val foo = foo(this)
}

// inject functions in a inline function to create a conditional Logger with zero overhead
@Provide inline fun logger(isDebug: IsDebug, loggerImpl: () -> LoggerImpl, noOpLogger: () -> NoOpLogger): Logger =
  if (isDebug) loggerImpl() else noOpLogger()

Distinguish between types

Sometimes you have multiple injectables of the same type Injekt will need help to keep them apart here are two strategies:

Value classes:

@JvmInline value class PlaylistId(val value: String)
@JvmInline value class TrackId(val value: String)

fun loadPlaylistTracks(playlistId: PlaylistId = inject, trackId: TrackId = inject): List<Track> = ...

Tags:

@Tag 
@Target(AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.TYPE)
annotation class PlaylistId
@Tag
@Target(AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.TYPE)
annotation class TrackId

fun loadPlaylistTracks(playlistId: @PlaylistId String = inject, trackId: @TrackId String = inject): List<Track> = ...

Optionally you can add a typealias for your tag to make it easier to use

@Tag @Target(AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.TYPE)
annotation class PlaylistIdTag
typealias PlaylistId = @PlaylistIdTag String
@Tag @Target(AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.TYPE)
annotation class TrackIdTag
typealias TrackId = @TrackIdTag String

fun loadPlaylistTracks(playlistId: PlaylistId = inject, trackId: TrackId = inject): List<Track> = ...

More complex uses can be found in my essentials project(base project for my apps)

injekt's People

Contributors

ivianuu avatar shikasd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

injekt's Issues

Better dsl

Chaining binding contexts is not the best solution

Add more annotations BindIntoMap, BindIntoSet, BindTypes, BindNames

annotation class BindIntoMap<T : MapKeyProvider<*>>

annotation class BindIntoSet(val setNames: Array)

annotation class BindTypes(val types: Array<KClass<*>>)
annotation class BindNames(val names: Array)

interface MapKeyProvider {
fun getMapKeyBindings(binding: Binding): Collection
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.