How to inject data class using Koin? - android

When my application starts I create object with some data and I want to share the same instance of object between services/viewModels.
Is it possible to inject the same instance of data class to viewModel using Koin?
Edit:
I create user object in MainViewModel when app loaded data from firebase at start.
#IgnoreExtraProperties
#Keep
data class User(
val id: String = "",
val name: String? = null,
val surname: String? = null,
val email: String? = null,
val avatarUrl: String? = null
)

If your view model inherits from KoinComponent, you can access getKoin method to declare your user object.
class MainViewModel : ViewModel(), KoinComponent {
The user object will be available to rest of your application after declaration.
// user created from data from firebase ...
fun insertKoinFor(user: User) {
// declare koin the user of type User
getKoin().declare<User>(user)
// or declare with a named qualifier
getKoin().declare(user, named("myUser"))
}
Hope, it helps.

I would create a holder object, say UserManager, to hold an optional User instance. This holder is something you can provide in your koin graph as single, and whatever component responsible setting up the User instance (for example your MainViewModel) can update the instance inside the singleton holder.

Related

How to implement login with Room database in Kotlin

I need to implement some easy login using Room Database with Kotlin. What I need is to check empty TextViews and to get whether user exist, if so I need to store to SharePreferences the value of id of user and value of zebra. I know it is very easy but I cannot find some really easy example. Can I ask for source code also?
I have a class
#Entity(tableName = "cis06zebras")
data class User(
#PrimaryKey val id: Int,
val login: String? = null,
val password: String? = null,
val surname: String? = null,
val zebra: Int? = null
)
ViewModel
#HiltViewModel
class LoginViewModel #Inject constructor(
repository: SybaseRepository
):ViewModel() {
val users= repository.getUser().asLiveData()
DAO
#Query("SELECT * FROM cis06zebras")
fun getAllUsers(): Flow<List<User>>
I know this is not the correct the way of answering your question but i guess this repo i did can help you out with your use case: An android app that uses Room as the main database for authentication

Type cast / make genric viewModel to be passed as parameter

I am working on a custom dialog fragment, that is being used/ called from two different views having different viewModels. Instead of passing two separate viewModels in the constructor parameter of Dialog class as,
class CustomeDialog(var viewModel1: ViewModelA ?= null, var viewModel2 : ViewModelB ?= null) : DialogFragment()
I need to ask/ figure out a way where I could just set < T> kind of parameter to dialog so I could just type caste any viewModel to it, I want.
something like this,
class CustomDialog<T:ViewModel> : DialogFragment()
and in code, it would be something like
val mdialog1: CustomeDialog by lazy { CustomeDialog(viewModel as ViewModelA) }
and also
val mdialog2: CustomeDialog by lazy { CustomeDialog(viewMode2 as ViewModelB) }
You can create a secondary constructor in the generic class that takes in a generic ViewModel parameter:
class CustomeDialog<T : ViewModel>() : DialogFragment() {
constructor(viewmodel: T) : this()
}
And the usage the same as you did:
lateinit var viewModel: ViewModel
val mdialog1: CustomeDialog<ViewModelA> by lazy { CustomeDialog(viewModel as ViewModelA) }
lateinit var viewModel2: ViewModelA
val mdialog2: CustomeDialog<ViewModelA> by lazy { CustomeDialog(viewModel2) }
UPDATE:
how to initialize viewModel in dialog based on the type. eg. if VM1 is passed in constructor, then var dialogViewModel = WHAT??,
It's requested to have a Dialog with a generic ViewModel, so its type is Generic as it's unknown till it's instantiated.
yeah i need a local var dialogViewModel which is generic, as i mentioned, whole logic is dependent on this dvm
You can initialize it in the secondary constructor:
class CustomDialog<T : ViewModel>() : DialogFragment() {
lateinit var dialogViewModel: T
constructor(viewmodel: T) : this() {
dialogViewModel = viewmodel
}
}
This strategy cannot work. The OS recreates your Fragment using reflection and its empty constructor. It can restore state to the replacement Fragment using Bundle values, but class types are not a valid type of data for a Bundle.
Closest I can come up with is to make it an abstract class, and then create simple subclasses that have concrete types.
abstract class CustomDialog<T: ViewModel>(viewModelType: KClass<out T>): DialogFragment() {
val viewModel: T by createViewModelLazy(viewModelType, { viewModelStore })
}
class CustomDialogA: CustomDialog<ViewModelA>(ViewModelA::class)
class CustomDialogB: CustomDialog<ViewModelB>(ViewModelB::class)

Android ViewModelProvider() parameter error

I am trying to get a value from the SharedViewModel class but the ViewModelProvider() is giving a parameter error when i am passing requireActivity() although the same initilization and assignment works in my fragments.
It is requiring "ViewModelStoreOwner" to be passed.
class CourseRepository(val app: Application) {
private var viewModel: SharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
val courseData = MutableLiveData<List<Course>>()
init {
CoroutineScope(Dispatchers.IO).launch {
callWebService()
}
}
#WorkerThread
suspend fun callWebService() {
if (Utility.networkAvailable(app)) {
val retrofit = Retrofit.Builder().baseUrl(WEB_SERVICE_URL).addConverterFactory(MoshiConverterFactory.create()).build()
val service = retrofit.create(CourseService::class.java)
val serviceData = service.getCourseData(viewModel.pathName).body() ?: emptyList()
courseData.postValue(serviceData)
}
}
}
The purpose of the ViewModel here is because i am storing the Id of the selected RecyclerView item in order to send it to a server
ViewModel instances are scoped to Fragments or Activities (or anything with a similar lifecycle), which is why you need to pass in a ViewModelStoreOwner to the provider to get a ViewModel from it. The point of ViewModels is that they will exist until the store they belong to is destroyed.
The requireActivity method doesn't work here, because you're not inside a Fragment.
Some things to consider here:
Do you really need ViewModel in this use case? Could you perhaps use just a regular class that you can create by calling its constructor?
Could you call this Repository from your ViewModel, and pass in any parameters you need from there?

Accessing variables inside a model passed to live data

in User repository, I am fetching the data from the database and I cast into the model class User.
How do I access two variables user and email of the model class inside the live data?
ViewModel
class MyViewModel (private val repository: UsrRepository) : ViewModel() {
private var cUser : LiveData<User> = repository.getCacheUser()
val user: LiveData<String> = cUser.user
val email: LiveData<String> = cUser.email
}
Xml
<TextView
android:text="#{viewmodel.user}"
<TextView
android:text="#{viewmodel.email}"
Since you are not doing any transformations on the data provided by cUser, you can just make it public in your ViewModel:
val cUser: LiveData<User> = repository.getCacheUser()
Then access the user properties from the LiveData directly in the layout:
android:text="#{viewmodel.cUser.user}"
android:text="#{viewmodel.cUser.email}"
If you did not want to use the raw values, and instead some other computed values, you would instead expose a transformation of cUser, creating a new LiveData that has values modified from the original.

Kotlin object (singleton) with an Android Application Context [duplicate]

This question already has answers here:
Singleton with parameter in Kotlin
(14 answers)
Closed 2 years ago.
The Kotlin reference says that I can create a singleton using the object keyword like so:
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
//
}
}
However, I would like to pass an argument to that object. For example an ApplicationContext in an Android project.
Is there a way to do this?
Since objects do not have constructors what I have done the following to inject the values on an initial setup. You can call the function whatever you want and it can be called at any time to modify the value (or reconstruct the singleton based on your needs).
object Singleton {
private var myData: String = ""
fun init(data: String) {
myData = data
}
fun singletonDemo() {
System.out.println("Singleton Data: ${myData}")
}
}
Kotlin has a feature called Operator overloading, letting you pass arguments directly to an object.
object DataProviderManager {
fun registerDataProvider(provider: String) {
//
}
operator fun invoke(context: ApplicationContext): DataProviderManager {
//...
return this
}
}
//...
val myManager: DataProviderManager = DataProviderManager(someContext)
With most of the existing answers it's possible to access the class members without having initialized the singleton first. Here's a thread-safe sample that ensures that a single instance is created before accessing any of its members.
class MySingleton private constructor(private val param: String) {
companion object {
#Volatile
private var INSTANCE: MySingleton? = null
#Synchronized
fun getInstance(param: String): MySingleton = INSTANCE ?: MySingleton(param).also { INSTANCE = it }
}
fun printParam() {
print("Param: $param")
}
}
Usage:
MySingleton.getInstance("something").printParam()
There are also two native Kotlin injection libraries that are quite easy to use, and have other forms of singletons including per thread, key based, etc. Not sure if is in context of your question, but here are links to both:
Injekt (mine, I'm the author): https://github.com/kohesive/injekt
Kodein (similar to Injekt): https://github.com/SalomonBrys/Kodein
Typically in Android people are using a library like this, or Dagger, et al to accomplish parameterizing singletons, scoping them, etc.
I recommend that you use this form to pass arguments in a singleton in Kotlin debit that the object your constructor is deprived and blocked:
object Singleton {
fun instance(context: Context): Singleton {
return this
}
fun SaveData() {}
}
and you call it this way in the activity
Singleton.instance(this).SaveData()
If you looking for a base SingletonHolder class with more than one argument. I had created the SingletonHolder generic class, which supports to create only one instance of the singleton class with one argument, two arguments, and three arguments.
link Github of the base class here
Non-argument (default of Kotlin):
object AppRepository
One argument (from an example code in the above link):
class AppRepository private constructor(private val db: Database) {
companion object : SingleArgSingletonHolder<AppRepository, Database>(::AppRepository)
}
// Use
val appRepository = AppRepository.getInstance(db)
Two arguments:
class AppRepository private constructor(private val db: Database, private val apiService: ApiService) {
companion object : PairArgsSingletonHolder<AppRepository, Database, ApiService>(::AppRepository)
}
// Use
val appRepository = AppRepository.getInstance(db, apiService)
Three arguments:
class AppRepository private constructor(
private val db: Database,
private val apiService: ApiService,
private val storage : Storage
) {
companion object : TripleArgsSingletonHolder<AppRepository, Database, ApiService, Storage>(::AppRepository)
}
// Use
val appRepository = AppRepository.getInstance(db, apiService, storage)
More than 3 arguments:
To implement this case, I suggest creating a config object to pass to the singleton constructor.

Categories

Resources