Kotlin extension not recognized in adapter - android

I have basic extension util for picasso:
public val Context.picasso: Picasso
get() = Picasso.with(this)
public fun ImageView.load(path: String, request: (RequestCreator) -> RequestCreator) {
request(context.picasso.load(path)).into(this)
}
And when I try to call it from imageview context in adapter it's not recognized.
Same thing with itemView.context.picasso
Thanks!

I'm pretty sure your extensions are out of scope (in a different package).
Import your extensions like so:
import your.package.picasso
import your.package.load
Take a look at the docs for more info.

Related

Platform declaration clash kotlin

I am seeing the following error
Platform declaration clash: The following declarations have the same
JVM signature (getHosts()Landroidx/lifecycle/MutableLiveData;):
private final fun <get-hosts>(): MutableLiveData<List> defined
in com.example.xx.viewmodel.HostsViewModel public final fun
getHosts(): MutableLiveData<List> defined in
com.example.xx.viewmodel.HostsViewModel
What am I doing wrong?
class HostsViewModel : ViewModel() {
private val hostsService = HostsService()
private val hosts: MutableLiveData<List<Host>> by lazy {
MutableLiveData<List<Host>>().also {
loadHosts()
}
}
fun getHosts(): MutableLiveData<List<Host>> {
return hosts
}
private fun loadHosts(){
hosts.value = hostsService.getHosts().body()
}
}
For every class property (val), Kotlin generates a getter called getHosts() and for var also a setter called setHosts(MutableLiveData<List<Host>> value) as per Java's convention. It hides it from the Kotlin user as getters and setters are usually just boilerplate code without offering much value. As such, your own getHosts() method clashes with the generated method at compilation. You have multiple possibilities to solve this issue:
Rename private val hosts to something else, e.g. private val internalHosts
Annotate the getHosts method with #JvmName("getHosts2"). If you do that though, consider the possibility that someone might call your code from Java and in that case, the caller would need to call getHosts2() in Java code, which might not be such nice API-design.
Reconsider your api design. In your case, you could simply make val hosts public and remove your getHosts() entirely, as the compiler will auto-generate getHosts() for you.
In addition to that, you might want to consider not exposing MutableLiveData in general as mentioned in the comments.
Edit:
Also, I would recommend that you do this:
val hosts: MutableLiveData<List<Host>> by lazy {
MutableLiveData<List<Host>>().also {
it.value = hostsService.getHosts().body()
}
}
and remove loadHosts to make your code more concise.

Android MVVM multiple API Call

I'm on my way to make an MVVM example project without the complexity of injection dependency library and RX ( because I think it's better to understand how it works fundamentally for people without all this very efficient stuff ) but its harder to make :)
I'm in trouble, I use the CatApi here: https://thecatapi.com/
I'm trying to do a spinner that contains breeds name and also a picture of a cat to the left ( for each breed ) but in this API you can only get this result in two calls ( one for breeds, one for images for a breed ), I don't push the research on the API far because even if the API can solve my problem, I want to face the problem because it can happen later in my life :)
So there is my probleme i've made the following code :
BreedEntity :
package com.example.mvvm_kitty.data.local.entities
//Entity was used to be stored into a local DB so no use here
data class BreedEntity (
val adaptability: Int,
val affection_level: Int,
val description: String,
val id: String,
var name: String,
val life_span: String,
val origin: String,
var iconImage : BreedImageEntity?,
var images: List<BreedImageEntity>?
){
}
the call into the BreedActivity :
private fun subscribeToModel(breedsViewModel: BreedsViewModel) {
//Todo: Gerer les erreurs reseau
breedsViewModel.getBreeds().observe(this, Observer {
breedEntities ->
mBinding.catSelected = breedEntities[0]
breedSpinnerAdapter = BreedsSpinnerAdapter(this, breedEntities)
mBinding.breedSelector.adapter = breedSpinnerAdapter
breedEntities.forEach {breedEntity ->
breedsViewModel.getBreedImages(breedEntities.indexOf(breedEntity)).observe(this, Observer {
breedEntity.iconImage = it[0]
})
}
})
}
yeah I think made a foreach it's very dirty ( and also it doesn't work because don't run on the same time so when I set the images in the observer the "it" value is on the last item
there is my BreedsViewModel :
package com.example.mvvm_kitty.viewmodels
import android.app.Application
import android.util.Log
import android.view.animation.Transformation
import androidx.lifecycle.*
import com.example.mvvm_kitty.BasicApp
import com.example.mvvm_kitty.data.local.entities.BreedEntity
import com.example.mvvm_kitty.data.local.entities.BreedImageEntity
import com.example.mvvm_kitty.data.repositories.CatRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
class BreedsViewModel(application: Application, private val catRepository: CatRepository) : AndroidViewModel(application) {
private val mObservableBreeds: LiveData<List<BreedEntity>> = catRepository.getBreeds()
/**
* Expose the product to allow the UI to observe it
*/
fun getBreeds(): LiveData<List<BreedEntity>> {
return mObservableBreeds
}
fun getBreedImages(index : Int): LiveData<List<BreedImageEntity>> {
val breed = mObservableBreeds.value?.get(index)
return catRepository.getBreedImages(breed!!.id)
}
/**
* Factory is used to inject dynamically all dependency to the viewModel like reposiroty, or id
* or whatever
*/
class Factory(private val mApplication: Application) :
ViewModelProvider.NewInstanceFactory() {
private val mRepository: CatRepository = (mApplication as BasicApp).getCatRepository()
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return BreedsViewModel(mApplication, mRepository) as T
}
}
}
and to finish the CatRepository method to get the images :
private fun getBreedImagesFromApi(id: String) : LiveData<List<BreedImageEntity>>{
mObservableBreedImages.addSource(catService.getAllImages(id, 10)){
mObservableBreedImages.postValue(it.resource?.map { breedDto ->
breedDto.toEntity()})
}
return mObservableBreedImages
}
My problem is the following how can I get my images for each item in a clean way ( because I think my code is good but the foreach observer part is very dirty )
If someone can help me it would be very nice :D, Thanks in advance for your time.
Seems like instead of fetching the data separately, you should be fetching them at the same time and combining the result into one response.
Generally speaking:
Remove getBreedImagesFromApi.
Update your getBreedsFromApi (I
assume that exists and you're using coroutines) to fetch both pieces
of data in the one call you already have for getting breeds. You can
use async and await() for this to fire off two requests to the two different endpoints, have them run concurrently, and wait for both requests to finish.
Remove the "foreach" because the images will now exist by the time you get the list of breeds.
Hope that helps!
I guess the view should receive a completed object data from your view-model,
which mean you must do all this logic in view model.so
hitting both APIs & observe them in view-model.
create one more Live Data responsible for passing data to your View(Activity/Fragment)
update the ViewLiveData from both APIs observers.
and to do this you will need Transformation or MediatorLiveData in your view-model.
also you can use Rxjava, check fetch every item on the list

Difference between file and classes in kotlin

I am a bit confuse that where to use file or class in kotlin can some one please suggest when to use file,it is not clear for me due to less documents for both the case.
You got to use a kotlin file to declare extension methods , and import that file to the class or file you're working on
Eg: the kotlin file contains
package com.something.mediaplayersystem
public fun String.toLowerCaseByAnExtensionMethod(){
this.toLowerCase()
}
And I want to use that extension method on a class
package com.something.mediaplayersystem
import com.something.mediaplayersystem.toLowerCase
class Classa {
var word:String = "WELCOME"
public fun Method(){
var lowerCaseWord = word.toLowerCaseByAnExtensionMethod()
}
}
In the case of String you don't need to import the method but in mayor cases you have to.

How to verify that a specific lambda method has been passed as argument with mockito

I have a Kotlin class (The problem was simplified to have a basic example), in it there is a method testedMethod() I want to test interactions on.
I want to make sure the correct parameter is passed to anotherMethod(), my problem is that the parameter is not a value but a lambda function.
A simple solution in Kotlin is what I am looking for, it may or may not be based on this solution for java.
class TestedClass {
fun testedMethod() {
anotherMethod({ passedMethod() })
}
fun passedMethod() {}
fun anotherMethod(lambdaParameter: () -> Unit) {
// Does something with lambdaParameter
}
}
This is the solution I ended up using.
Basically you create a spy of the tested class to be able to verify its own method, and you use argumentCaptor to capture the passed lambda and be able to invoke what is in it.
import com.nhaarman.mockito_kotlin.argumentCaptor
import com.nhaarman.mockito_kotlin.spy
import com.nhaarman.mockito_kotlin.verify
#Test
fun lambdaParameterPassedIsCorrect() {
val testedClass = TestedClass()
val spyOfTestedClass = spy(testedClass)
val captor = argumentCaptor<() -> Unit>()
spyOfTestedClass.testedMethod()
verify(spyOfTestedClass).anotherMethod(captor.capture())
// Invoke the function passed as a parameter
// and then verify that the expected method was called
val function = captor.firstValue
function.invoke()
verify(spyOfTestedClass).passedMethod()
}
I would still be interested in a simpler solution.
You can use mockito_kotlin library, which have the function "verify".
verify(TestedClass, times(1)).anotherMethod(eq(passedMethod))
Which verify that the method "anotherMethod" of class "TestedClass" invoked once with the parameter equal to "passedMethod"

Is it possible to get a Kotlin subclass type from within a superclass?

I'm trying to figure out if something is possible. Generally, what I'm trying to do is get a class type of a subclass from within the companion object of the superclass ... In the snipped below, treat the __ as what I need
companion object
{
fun fromSnapshot(snapshot: DataSnapshot): __
{
val model = snapshot.getValue(__)
model.key = snapshot.key
// ...
return model
}
}
Some background ... DataSnapshot is from Firebase, and snapshot.getValue() takes a Class<T>. If I were trying to create an instance of, say, a TestModel, code would be as follows
companion object
{
fun fromSnapshot(snapshot: DataSnapshot): TestModel
{
val model = snapshot.getValue(TestModel::java.class)
model.key = snapshot.key
// ...
return model
}
}
I'm not really sure if what I'm asking is possible in Kotlin. I'm pretty sure it isn't in Java. I hate to mention it, but in Swift, this would be accomplished with what I call "big-S self," or Self, which is the class type of instance self. If you don't know Swift, self is equivalent to Java and Kotlin's this.
Any help would be hugely appreciated!
From your code, it seems to be a very generic function. It doesn't matter what T is and in which companion object this function lives, so I have another version:
inline fun <reified T : FirebaseModel> DataSnapshot.toModelOfType() =
getValue(T::class.java).also { it.key = this.key}
It can be used like this:
someSnapshot.toModelOfType<SomeFirebaseModel>()
instead of your
FirebaseModel.fromSnapshot<SomeFirebaseModel>(someSnapshot)
or with imports
fromSnapshot<SomeFirebaseModel>(someSnapshot)
I prefer mine because it's shorter than your version without imports and more fluent than your version with imports.
I personally suggest Prefer extension functions over Java style utility functions.
Even though I sat on this for days without posting a question, I figured it out less than an hour after posting this question. This can be accomplished with a reified generic type, which allows for usage of the generic type from within a function, however these can only be used as inline functions. Here's my solution
companion object
{
inline fun <reified T : FirebaseModel> fromSnapshot(snapshot: DataSnapshot): T
{
val model = snapshot.getValue(T::class.java)
model.key = snapshot.key
return model
}
}

Categories

Resources