I'm using MVVM with jetpack compose, and I need to...
# compose
p
I'm using MVVM with jetpack compose, and I need to make a call to Geocoder in my viewmodel. But now, the Geocoder class returns the response on a Listenner. It sounds rare to use a listener like in the old days of android views, but inside a viewmodel of compose. Is correct to do it? I'm attaching the sample in the thread
Copy code
fun transformStringLocationIntoLatLng(context: Context, address: String, addressType: AddressType) {
    viewModelScope.launch(Dispatchers.IO) {
        _uiState.update { currentState -> currentState.copy(loading = true) }

        val geocoder = Geocoder(context)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            geocoder.getFromLocationName(address, 1, object : Geocoder.GeocodeListener {
                override fun onGeocode(addresses: List<Address>) {
                    if (addresses.isNotEmpty()) {
                        val location = addresses[0]
                        val latLng = LatLng(location.latitude, location.longitude)

                        when (addressType) {
                            AddressType.ORIGIN ->
                                _uiState.update { currentState -> currentState.copy(loading = false, originLocation = latLng)}
                            AddressType.DESTINATION ->
                                _uiState.update { currentState -> currentState.copy(loading = false, destinationLocation = latLng)}
                        }
                    }
                }
                override fun onError(errorMessage: String?) {
                    Log.d("XXXX", "GeocodeListener ERROR: $errorMessage")
                }
            })
        } else {
            val addresses = geocoder.getFromLocationName(address, 1)
            if (addresses?.isNotEmpty() == true) {
                val location = addresses[0]
                val latLng = LatLng(location.latitude, location.longitude)

                when (addressType) {
                    AddressType.ORIGIN ->
                        _uiState.update { currentState -> currentState.copy(loading = false, originLocation = latLng)}
                    AddressType.DESTINATION ->
                        _uiState.update { currentState -> currentState.copy(loading = false, destinationLocation = latLng)}
                }
            }
        }
    }
}
f
You can convert your listener in coroutine, it’s not difficult;
p
you mean doing this?
Copy code
suspendCancellableCoroutine { continuation ->
        val geocoder = Geocoder(context, Locale.getDefault())
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                geocoder.getFromLocationName(address, 1, object : Geocoder.GeocodeListener {
                    override fun onGeocode(addresses: List<Address>) {
                        if (addresses.isNotEmpty()) {
                            val location = addresses[0]
                            continuation.resume(LatLng(location.latitude, location.longitude))
                        } else {
                            continuation.resume(null)
                        }
                    }

                    override fun onError(errorMessage: String?) {
                        continuation.resumeWithException(Exception(errorMessage))
                    }
                })
I still see that code using a listener.... so no changes, it is still using a listener inside mvvm and compose
f
Your listener is behind a Coroutine and make your viewmodel cleaner. Move your new code outside your viewmodel
p
can you clarify what you mean please¿?
f
If you’re API can only use listener, it’s fine. • Wrap it inside a coroutine like you show • Make a reusable function of it.
p
Or maybe
Flow.callbackFlow
p
François but I can do more or less the same without using the corutine and I still have the listener wrapping it into a corutine so I feel that no changes
Pablichenko how to achieve it with that
maybe doing that the listenner dissapear?
or maybe simply is correct to use a listener inside a MVVM compose viewmodel?
p
Ask any llm for an example of it
Or any direct text matcher like Google search
p
well, for this case I prefeer human interaction
👍 1
knowing if is a correct approach to use a listener inside a MVVM compose viewmodel is not a simple google search
p
There is nothing wrong with that. Just ensure you unsubscribe from the event source(in this case the location provider) when the ViewModel is cleared, onClear()
p
ummm
I don't think it's possible to unsubscribe it
or at least don't see a method for that
finally I migrated it to suspendCancellableCoroutine. Because then I can move it to a domain UseCase and simply calling it as a suspend function and update uistate with the result
but now I wonder what will happen if the vm is closed, maybe it will magically unsubscribe the listener?
p
Humm but is your listener receiving only one event or multiple events? In the case of one event this is fine. I thought it received multiple calls hince my suggestion to use callbackFlow. But for a one event operation you don't need to unsubscribe or anything like that. This is enough.
If you use viewModelScope to execute the coroutine it will cancel in onClear, you are good
p
great
thanks