Reputation: 2811
I have a funtion like this in many places in my project
class ClubsViewModel(): BaseViewModel() {
private var _mClubs = MutableLiveData<ArrayList<ClubFSEntity>>()
...
private fun listenToFireStoreClubs() {
mFirestore.collection("Clubs").addSnapshotListener { snapshot, e ->
// if there is an exception we want to skip.
if (e != null) {
return@addSnapshotListener
}
// if we are here, we did not encounter an exception
if (snapshot != null) {
val allClubs = ArrayList<ClubFSEntity>()
snapshot.documents.forEach {
it.toObject(ClubFSEntity::class.java)?.let {
allClubs.add(it)
}
}
_mClubs.value = allClubs
}
}
}
}
...
...
class ProjectsViewModel(): BaseViewModel() {
private var _mProjects = MutableLiveData<ArrayList<ProjectsFSEntity>>()
private fun listenToFireStoreProjects() {
//Code Here is Similar to the Code in the similar function in ClubsViewModel.
}
}
...
...
open class BaseViewModel() : ViewModel() {
protected val mFirestore = Firebase.firestore
protected var mStorageReferenence: StorageReference
protected val _networkState = MutableLiveData<NetworkState>()
protected val networkState: LiveData<NetworkState> = _networkState
//I would like to make this function private to access the above variables.
inline fun listenToFireStoreCollection[TO MOVE HERE SO THAT ALL MY OTHER VIEW MODELS CAN CALL IT]
}
...
...
@Parcelize
data class ClubFSEntity(val title: String="",
val description: String="",
val startDate: String?=null,
var clubId : String=""): Parcelable
It's easy to modularize out the "Clubs"
parameter. The Firebase data from that document list goes to an arraylist of ClubFSEntity
data objects. When adding the Firebase snapshot data to the allClubs
array, we convert using ClubFSEntity::class.java
.
I have many places with this same code, but only three things change ("Clubs"
, ClubFSEntity
and ClubFSEntity::class.java
). I would like to create a function with those three items as a variables.
I don't even know what to search for. I have tried to use generics but can't get it so far.
I would like to also use a generic class if possible.
I would prefer to initialize the ViewModel like so:
class ClubsFragment : Fragment() {
private val mClubsViewModel: ClubsViewModel<ClubsFSEntity> by viewModels()
}
And, to change the ViewModel definition to something like:
class ClubsViewModel<T> () : BaseViewModel() {
private var _mClubs = MutableLiveData<List<T>>()
...
}
Each view model would call listenToFirestoreCollection
with its own parameter(s).
Upvotes: 0
Views: 1006
Reputation: 2811
I finished like this:
class ClubsViewModel<T> (clazz: Class<T>) : BaseViewModel<T>(clazz) { //<< THIS IS WHERE THE CURRENT PROBLEM WAS.
private var _mClubs = MutableLiveData<List<T>>()
listenToFireStoreCollection("Clubs", _mClubs)
...
}
class BViewModel<T> (clazz: Class<T>) : BaseViewModel<T>(clazz) { //<< THIS IS WHERE THE CURRENT PROBLEM WAS.
private var _mBs = MutableLiveData<List<T>>()
listenToFireStoreCollection("Bname", _mBs)
...
}
class BaseViewModel<T>(val clazz: Class<T>) {
protected val mFirestore = Firebase.firestore
protected fun listenToFireStoreCollection(val collectionName: String, liveData: MutableLiveData<List<T>>)
mFirestore.collection(collectionName).addSnapshotListener { snapshot, e ->
// if there is an exception we want to skip.
if (e != null) {
return@addSnapshotListener
}
// if we are here, we did not encounter an exception
if (snapshot != null) {
liveData.value = snapshot.documents.mapNotNull { it.toObject(clazz) }
}
}
}
}
//FRAGMENT EXAMPLES.
class ClubsFragment : Fragment() {
private val mClubsViewModel: ClubsViewModel<ClubsFSEntity> by viewModels()
...
}
class BsFragment : Fragment() {
private val mBsViewModel: BsViewModel<BsFSEntity> by viewModels()
...
}
Upvotes: 0
Reputation: 37660
Let's first simplify your initial function:
private var _mClubs = MutableLiveData<List<ClubFSEntity>>()
private fun listenToFireStoreClubs() {
mFirestore.collection("Clubs").addSnapshotListener { snapshot, e ->
// if there is an exception we want to skip.
if (e != null) {
return@addSnapshotListener
}
// if we are here, we did not encounter an exception
if (snapshot != null) {
_mClubs.value = snapshot.documents.mapNotNull {
it.toObject(ClubFSEntity::class.java)
}
}
}
}
You could make it more generic like this:
private fun <T> listenToFireStoreCollection(
collectionName: String,
liveData: MutableLiveData<List<T>>,
clazz: Class<T>,
) {
mFirestore.collection(collectionName).addSnapshotListener { snapshot, e ->
// if there is an exception we want to skip.
if (e != null) {
return@addSnapshotListener
}
// if we are here, we did not encounter an exception
if (snapshot != null) {
liveData.value = snapshot.documents.mapNotNull { it.toObject(clazz) }
}
}
}
You can also go one step further and additionally define a reified version of this, so you don't have to pass in the Class<T>
explicitly each time:
private inline fun <reified T> listenToFireStoreCollection(
collectionName: String,
liveData: MutableLiveData<List<T>>,
) = listenToFireStoreCollection(collectionName, liveData, T::class.java)
If the live data and the function are in the same class, you can also make the class generic itself like the following:
class FireStoreLiveData<T>(
val collectionName: String,
val clazz: Class<T>,
) {
private var _mClubs = MutableLiveData<List<ClubFSEntity>>()
private fun listenToFireStoreCollection() {
mFirestore.collection(collectionName).addSnapshotListener { snapshot, e ->
// if there is an exception we want to skip.
if (e != null) {
return@addSnapshotListener
}
// if we are here, we did not encounter an exception
if (snapshot != null) {
liveData.value = snapshot.documents.mapNotNull { it.toObject(clazz) }
}
}
}
}
But it's hard to tell how to design the API of this class without more info about how you intend to use it from outside (everything is private right now, so not much use). For instance, who starts the listener? Is there a way to stop it? etc.
Upvotes: 2