tinmegali
7/14/2017 - 5:08 PM

Injecting ViewModel with Dagger2 on Android using Kotlin

Injecting ViewModel with Dagger2 on Android using Kotlin

class App : Application(), HasActivityInjector {

    @Inject lateinit var activityInjector: DispatchingAndroidInjector<Activity>

    override fun activityInjector(): AndroidInjector<Activity> {
        return activityInjector
    }

    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.builder()
                .build()
                .inject(this)
    }
}
package com.tinmegali.daggerwithkotlin.dagger

import com.tinmegali.daggerwithkotlin.App
import com.tinmegali.daggerwithkotlin.dagger.activities.ActivitiesModule
import com.tinmegali.daggerwithkotlin.dagger.fragments.FragmentsModule
import com.tinmegali.daggerwithkotlin.dagger.viewModels.ViewModelModule
import dagger.Component
import dagger.android.AndroidInjectionModule
import javax.inject.Singleton

@Singleton
@Component(modules = arrayOf(
        AndroidInjectionModule::class,
        ViewModelModule::class
))
interface AppComponent {
    fun inject(app: App)
}
class MainActivity : LifecycleActivity() {
  //...
  @Inject lateinit var viewModelFactory: ViewModelProvider.Factory
  var mainViewModel: MainActivityModel? = null
  
  override fun onCreate(savedInstanceState: Bundle?) {
    AndroidInjection.inject(this) // Dagger
    super.onCreate(savedInstanceState)
    //..
    mainViewModel = ViewModelProviders.of(this, viewModelFactory)
      .get(MainActivityModel::class.java)
  }
}
class MainActivityModel
    @Inject constructor(
            private var noteDAO: NoteDAO,
            private var userDAO: UserDAO
    ): ViewModel(), AnkoLogger {

    private var notesData: LiveData<List<Note>>? = null

    fun getNotes( ): LiveData<List<Note>>? {
        return notesData
    }

    fun subscribeToNotesDBChanges( callback: OnSync ) {
        doAsync {
            notesData = noteDAO.findAllNotedObservable()
            info("Notes received.")
            callback.notesReceived()

        }
    }

    public interface OnSync {
        fun notesReceived( )
    }

}
package com.tinmegali.daggerwithkotlin.dagger.viewModels

import com.tinmegali.daggerwithkotlin.MainActivityModel
import dagger.Component

@Component( modules = arrayOf(
        ViewModelModule::class
))
interface ViewModelComponent {
    // inject your view models
    fun inject( mainViewModel: MainActivityModel )

}
package com.tinmegali.oauth2restclient.dagger

import android.arch.lifecycle.ViewModel
import android.arch.lifecycle.ViewModelProvider

import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Singleton

@Suppress("UNCHECKED_CAST")
@Singleton
class ViewModelFactory @Inject
constructor(
        private val creators: Map<Class<out ViewModel>,
                @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        var creator: Provider<out ViewModel>? = creators[modelClass]
        if (creator == null) {
            for ((key, value) in creators) {
                if (modelClass.isAssignableFrom(key)) {
                    creator = value
                    break
                }
            }
        }
        if (creator == null) {
            throw IllegalArgumentException("unknown model class " + modelClass)
        }
        try {
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }

    }
}
package com.tinmegali.daggerwithkotlin.dagger.viewModels
import android.arch.lifecycle.ViewModel

import java.lang.annotation.Documented
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy

import dagger.MapKey
import kotlin.reflect.KClass

@MustBeDocumented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
package com.tinmegali.daggerwithkotlin.dagger.viewModels

import android.arch.lifecycle.ViewModel
import android.arch.lifecycle.ViewModelProvider
import com.tinmegali.daggerwithkotlin.MainActivityModel
import com.tinmegali.oauth2restclient.dagger.ViewModelFactory
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap

@Module
abstract class ViewModelModule {


    @Binds
    @IntoMap
    @ViewModelKey( MainActivityModel::class )
    // Bind your View Model here
    abstract fun bindMainViewModel( mainViewModel: MainActivityModel ): ViewModel

    @Binds
    abstract fun bindViewModelFactory( factory: ViewModelFactory):
            ViewModelProvider.Factory

}