I am new to koin and kotlin and I have just started using koin in my project and It is working quite good. I have two viewmodel classes, SubscritpionViewModel and LoginViewModel. Is there a way I can get instance of LoginViewModel inside SubscriptionViewModel. I don't know if it is right or not but it will be handy for me if I can access the other viewmodel.
val viewModule = module {
viewModel { SubscriptionViewModel(get(), get()) }
viewModel { LoginViewModel(get()) }
}
SubscriptionViewModel
class SubscriptionViewModel(val api: ServiceApi, var user: LoginViewModel) : BaseViewModel() {
...
}
I have also created a separate module for this, but I don't know what is the right way to initialize it.
val userModule = module {
single( definition = {
get<LoginViewModel>() })
}
I think it's a bad design. I think what you should do is to create a common object between LoginViewModel and SubscriptionViewModel and inject it via constructor to both LoginViewModel and SubscriptionViewModel. Maybe Repository pattern would be good? Please describe the functionality you want to implement so we can get the idea of why you need one ViewModel inside another. With repository you can do something like this:
class UserRepository(private val serviceApi: ServiceApi) {
}
class SubscriptionViewModel(val userRepository: UserRepository) : BaseViewModel() {
...
}
class LoginViewModel(val userRepository: UserRepository) : BaseViewModel() {
...
}
and in Koin module:
module {
single { UserRepository(get()) }
viewModel { SubscriptionViewModel(get()) }
viewModel { LoginViewModel(get()) }
}
Related
I have three class needing to share a dependency. The latter is initialisated by one of them.The SettingsViewModel contains the data to initialize the dependency and it need to be deleted at the end of the activity. NetworkViewModel and TimeViewModel use it as an interface since the dependancy is an interface with the logic to handle Bluetooth.
SettingsViewModel -->(initialize) SingletonDependency.
NetworkViewModel --> (use).
TimeViewModel --> (use).
How can I make Hilt (or manual) injection to use the same interface? If I understand well I can't use singleton here since I need to iniatilize the dependency when the activity start.
If we consider that your class name is SomeClass you can provide a live data of this class like this:
#Module
#InstallIn(SingletonComponent::class)
object SingeltonModule {
#Provides
#Singleton
fun provideSomeClassLiveData(): MutableLiveData<SomeClass> {
return MutableLiveData<SomeClass>()
}
}
in your SettingsViewModel do this:
#HiltViewModel
class SettingsViewModel #Inject constructor(
val SomeClassLiveData: MutableLiveData<SomeClass>
) : ViewModel() {
init{
someClassLiveData.value = SomeClass()
}
}
and in other view models you can inject this to contractors and observe it:
#HiltViewModel
class NetworkViewModel #Inject constructor(
val SomeClassLiveData: MutableLiveData<SomeClass>
) : ViewModel() {
init{
someClassLiveData.observeForEver{
//do what you want with value
}
}
}
Is there any alternative to javax.inject.Provider in koin?
To react to actions, I am injecting Commands to my activity.
Command is a single-run object, for example WriteToFile.
In dagger I could make it like this:
class MainPresenter : Presenter() {
#Inject
lateinit var writeFile: Provider<WriteFileCommand>
fun onSaveClicked() {
writeFile.get().run()
}
}
in koin, when I try to use:
class MainPresenter : Presenter() {
lateinit var writeFile: Provider<WriteFileCommand> by inject()
fun onSaveClicked() {
writeFile.get().run()
}
}
My koin module:
val appModule = module {
factory { WriteFileCommand(get(), get()) }
factory { FileProvider() }
single { DataStore() }
}
Than I got error saying:
Can't create definition for 'Factory [name='WriteFileCommand',class='com.test.WriteFileCommand']' due to error :
No compatible definition found. Check your module definition
I understand that I can call:
var command: WriteFileCommand = StandAloneContext.getKoin().koinContext.get()
command.run()
But It looks so cumbersome
There's nothing like a provider directly. If you use inject, you'll use a lazy delegate. If you use get, you'll create a new instance you declared the dependency with a factory. So get is what you need in your case. Just let your MainPresenter implement KoinComponent and you'll be able to use get directly:
class MainPresenter : Presenter(), KoinCompontent {
fun onSaveClicked() = get<WriteFileCommand>().run()
}
I'm new at Koin. I have set all the stuff and is working. But I'm getting some problems when I'm trying to inject interactor and presenter at the same time. That not sure it is possible.
This is my Module
val applicationModule = module(override = true) {
factory{VoucherImpl(get())}
factory<VoucherContract.Presenter> { (view: VoucherContract.View) -> VoucherPresenter(view, get()) }
}
This is my Activity where inject the presenter
private val presenter: VoucherContract.Presenter by inject { parametersOf(this)}
This is my Presenter
class VoucherPresenter (private var view: VoucherContract.View?, private var mCodeRechargeInteract : VoucherImpl) : VoucherContract.Presenter, VoucherContract.Callback, KoinComponent {
override fun create() {
view?.initView()
view?.showProgress()
mCodeRechargeInteract.run()
}
.
.
.
Interactor class
class VoucherImpl(private var mCallback: VoucherContract.Callback?) : AbstractInteractor() {
.
.
.
contract
interface VoucherContract {
interface Presenter {
fun create()
fun destroy()
fun checkIfShoppingCartHaveItems()
fun addVoucherToShoppingCart(voucherProduct: Product)
fun onItemClick(product: Product)
}
interface Callback {
fun onResponseVouchers(vouchers: List<Product>?)
fun onError()
}
}
With this code I get
No definition found for 'xxx.voucher.VoucherContract$Callback' has been found. Check your module definitions.
Then, I try to put it in the module and I can't do it because I get: a Type mismatch. Required VoucherContract.Callback Found VoucherImpl
factory<VoucherContract.Callback> { (callBack: VoucherContract.Callback) -> VoucherImpl(callBack) }
You have a circular dependency that's why this doesn't work.
VoucherImpl(VoucherContract.Callback) and VoucherPresenter(View, VoucherImpl):VoucherContract.Callback
There are multiple ways out of this predicament.
I would recommend the following changes:
The VoucherImpl should not have the constructor parameter VoucherContract.Callback. This callback should be the parameter of a method something like this:
class VoucherImpl : AbstractInteractor(){
fun listen(VoucherContract.Callback){...}
}
This way the dependency becomes one way and you can inject them.
How do we inject ViewModel with dependency using Koin?
So For Example I have a ViewModel thats like this:
class SomeViewModel(val someDependency: SomeDependency, val anotherDependency: AnotherDependency): ViewModel()
Now the official docs here, states that to provide a ViewModel we could do something like:
val myModule : Module = applicationContext {
// ViewModel instance of MyViewModel
// get() will resolve Repository instance
viewModel { SomeViewModel(get(), get()) }
// Single instance of SomeDependency
single<SomeDependency> { SomeDependency() }
// Single instance of AnotherDependency
single<AnotherDependency> { AnotherDependency() }
}
Then to inject it, we can do something like:
class MyActivity : AppCompatActivity(){
// Lazy inject SomeViewModel
val model : SomeViewModel by viewModel()
override fun onCreate() {
super.onCreate()
// or also direct retrieve instance
val model : SomeViewModel= getViewModel()
}
}
The confusing part for me is that, normally you will need a ViewModelFactory to provide the ViewModel with Dependencies. Where is the ViewModelFactory here? is it no longer needed?
Hello viewmodel() it's a Domain Specific Language (DSL) keywords that help creating a ViewModel instance.
At this [link][1] of official documentation you can find more info
The viewModel keyword helps declaring a factory instance of ViewModel.
This instance will be handled by internal ViewModelFactory and
reattach ViewModel instance if needed.
this example of koin version 2.0 [1]: https://insert-koin.io/docs/2.0/documentation/koin-android/index.html#_viewmodel_dsl
// Given some classes
class Controller(val service : BusinessService)
class BusinessService()
// just declare it
val myModule = module {
single { Controller(get()) }
single { BusinessService() }
}
I want my application to have the following architecture :
-di // dependancy injection package
-model
--datasources // implementations of repositories, using dao to return data
--dao // access to the database (room)
--repositories // interfaces
-ui // activities, fragments...
-viewmodels // viewmodels of each fragment / activity which will return data to the views in the ui package
On Android, people seem to usually have the dependancy injection in the activities, but I want to inject my repositories in view models.
I have one particular viewmodel that uses a manually created LiveData
class NavigationViewModel(application: Application) : AndroidViewModel(application) {
init {
DaggerAppComponent.builder()
.appModule(AppModule(getApplication()))
.roomModule(RoomModule(getApplication()))
.build()
.injectNavigationViewModel(this)
}
#Inject
lateinit var displayScreenRepository: DisplayScreenRepository
fun setScreenToDisplay(screen: DisplayScreen) {
displayScreenRepository.setScreenToDisplay(screen)
}
}
In my RoomModule, I have this provider :
#Singleton
#Provides
internal fun displayScreenRepository(): DisplayScreenRepository {
return DisplayScreenDataSource()
}
The class DisplayScreenDataSource is very simple :
class DisplayScreenDataSource : DisplayScreenRepository {
private val screenToDisplay = MutableLiveData<DisplayScreen>()
override fun getScreenToDisplay(): LiveData<DisplayScreen> {
return screenToDisplay
}
override fun setScreenToDisplay(screen: DisplayScreen) {
if (Looper.myLooper() == Looper.getMainLooper()) {
screenToDisplay.value = screen
} else {
screenToDisplay.postValue(screen)
}
}
}
I want this singleton to be available to an other viewmodel, MainActivityViewModel, so I did this :
class MainActivityViewModel(application: Application) : AndroidViewModel(application) {
#Inject
lateinit var displayScreenRepository: DisplayScreenRepository
init {
DaggerAppComponent.builder()
.appModule(AppModule(application))
.roomModule(RoomModule(application))
.build()
.injectMainViewModel(this)
}
}
But when I run my application, the repositories instantiated don't have the same reference and when I update the value of one LiveData in one of my ViewModel, if I observe the LiveData of an other ViewModel, it is not the same LiveData so is is not updated.
My guess is that I am not correctly instantiating the DaggerAppComponent : it is supposed to be created only once and eventually injected several times.
Is that right?
Where should I be supposed to store the instance of the DaggerAppComponent? In the Application class?
I could have something like that :
class MainActivityViewModel(application: Application) : AndroidViewModel(application) {
#Inject
lateinit var displayScreenRepository: DisplayScreenRepository
init {
(application as MyApplication).daggerAppComponent.injectMainViewModel(this)
}
}
Is this the recommended way?