Kotlin Flow - Generic function for Retrofit with Result sealed class - retrofit

I am recently working with Flow in my retrofit's repository.
Sealed class for Result
enum class ApiStatus{
SUCCESS,
ERROR,
LOADING
}
sealed class ApiResult <out T> (val status: ApiStatus, val data: T?, val message:String?) {
data class Success<out R>(val _data: R?): ApiResult<R>(
status = ApiStatus.SUCCESS,
data = _data,
message = null
)
data class Error(val exception: String): ApiResult<Nothing>(
status = ApiStatus.ERROR,
data = null,
message = exception
)
data class Loading<out R>(val _data: R?, val isLoading: Boolean): ApiResult<R>(
status = ApiStatus.LOADING,
data = _data,
message = null
)
}
Example repository call for 3 state - Loading, Error, Success
fun googleDisconnect() = flow {
emit(ApiResult.Loading(null, true))
val call = userDataSource.self("v4").googleDisconnect()
if(call.isSuccessful) {
emit(ApiResult.Success(call.body()))
} else {
emit(ApiResult.Error("Google Disconnect Failed"))
}
}
However, I have multiple network call with different function in my repository. Is there any idea to write a generic function for these flow so that these flow can be emitted to the flow builder?
My attempt but problem is How can I pass suspend function into the function?

Finally I got myself the answer. I wonder if this will helps but I will post out my answer.
fun <T> toResultFlow(call: suspend () -> Response<T>?) : Flow<ApiResult<T>?> {
return flow {
emit(ApiResult.Loading())
val c = call() <-- have to initialize the call method first
c?.let {
try{
if(c.isSuccessful) {
c.body()?.let {
emit(ApiResult.Success(it))
}
} else {
c.errorBody()?.let {
val error = it.string()
it.close()
emit(ApiResult.Error(error))
}
}
}catch (e: Exception) {
emit(ApiResult.Error(e.toString()))
}
}
}.flowOn(Dispatchers.IO)
}
Then, pass in your suspend function as lambda
fun googleDisconnect() = toResultFlow {
userDataSource.self("v4").googleDisconnect()
}
Finally, the toResultFlow will be return Flow<ApiResult> and T is your preferred datatype! Volla!

Related

How do i make my code in kotlin read data from the firebase firestore

I am trying to make an app that reads data from the firebase firestore and then shows in a screen that same data
The problem the data only appears when shown in log but i want the in text(string)
Can anyone help me understand how to do it
I already tried many videos explaining but none of then work so my last option is really ask in here for help.
Here is the code
#SuppressLint("UnrememberedMutableState")
#Composable
fun DB () {
val db = Firebase.firestore
val collectionReference = db.collection("Inventário")
.document("Bloco E")
.collection("Sala E0.05")
val data = mutableStateOf(mapOf<String, Any>())
val job = remember { Job() }
remember {
GlobalScope.launch(Dispatchers.Main) {
val documentSnapshot = collectionReference.document("Computador").get().await()
try {
data.value = documentSnapshot.data ?: mapOf()
}catch (e: Exception){
Log.e("Firestore", "Error retrieving data", e)
}
}
}
Column() {
data.value.forEach { (key, value) ->
Text("$key: $value")}
}
}
And here is the database structure:
It seems like you forgot to use remember on your MutableState (and also suppressed the lint warning with (#SuppressLint("UnrememberedMutableState"))):
val (data, setData) = remember { mutableStateOf(mapOf<String, Any>()) }
Also, using GlobalScope in Android is not recommended. Consider using a LaunchedEffect instead:
#Composable
fun DB () {
val db = Firebase.firestore
val collectionReference = db.collection("Inventário")
.document("Bloco E")
.collection("Sala E0.05")
val (data, setData) = remember { mutableStateOf(mapOf<String, Any>()) }
LaunchedEffect(collectionReference) {
try {
val documentSnapshot = collectionReference.document("Computador").get().await()
setData(documentSnapshot.data ?: mapOf())
} catch (e: Exception) {
Log.e("Firestore", "Error retrieving data", e)
}
}
Column() {
data.value.forEach { (key, value) ->
Text("$key: $value")}
}
}

Issue with coroutines fold function and Firestore

thank you for taking your time to read my problem.
Im currently using Firebase Firestore to retrieve a list of objects that I which to display to the UI, im trying to use a suspend function to fold the accumulative values of a sequence of calls from the Firestore server, but at the moment im unable to pass the result value outside the scope of the coroutine.
This is my fold function:
suspend fun getFormattedList(): FirestoreState {
return foldFunctions(FirestoreModel(""), ::getMatchesFromBackend, ...., ....)
}
This is my custom fold function:
suspend fun foldFunctions(model: FirestoreModel,
vararg functions: suspend (FirestoreModel, SuccessData) -> FirestoreState): FirestoreState {
val successData: SuccessData = functions.fold(SuccessData()) { updatedSuccessData, function ->
val status = function(model, updatedSuccessData)
if (status !is FirestoreState.Continue) {
return status
}
updatedSuccessData <--- I managed to retrieve the list of values correctly here
}
val successModel = SuccessData()
successData.matchList?.let { successModel.matchList = it }
successData.usermatchList?.let { successModel.usermatchList = it }
successData.formattedList?.let { successModel.formattedList = it }
return FirestoreState.Success(successModel) <--- I cant event get to this line with debugger on
}
This is my first function (which is working fine)
suspend fun getMatchesFromBackend(model: FirestoreModel, successData: SuccessData): FirestoreState {
return try {
val querySnapshot: QuerySnapshot? = db.collection("matches").get().await()
querySnapshot?.toObjects(Match::class.java).let { list ->
val matchList = mutableListOf<Match>()
list?.let {
for (document in it) {
matchList.add(Match(document.away_score,
document.away_team,
document.date,
document.home_score,
document.home_team,
document.match_id,
document.matchpoints,
document.played,
document.round,
document.tournament))
}
successData.matchList = matchList <--- where list gets stored
}
}
FirestoreState.Continue
} catch (e : Exception){
when (e) {
is RuntimeException -> FirestoreState.MatchesFailure
is ConnectException -> FirestoreState.MatchesFailure
is CancellationException -> FirestoreState.MatchesFailure
else -> FirestoreState.MatchesFailure
}
}
}
My hypothesis is that the suspen fun get cancelled and the continuation of the scope gets blocked, I have tried to use runBlocking { } without vail. If someone has an idea of how to circumvent this issue I'd be very gratefull.

Calendar TypeConverter for Room (Kotlin)

I am trying to persist a timestamp in my room database using the following TypeConverter:
class Converters {
#TypeConverter
fun fromTimestamp(value: Long?): Calendar? {
if(value == null) return null
val cal = GregorianCalendar()
cal.timeInMillis = value
return cal
}
#TypeConverter
fun toTimestamp(timestamp: Calendar?): Long? {
if(timestamp == null) return null
return timestamp.timeInMillis
}
}
Two of my Entities include the following column
:
#ColumnInfo(name = "timestamp")
val timestamp: Calendar?,
But I get a compilation error upon trying to build the project - I had no issues when using the Date TypeConverter example from the developer reference guide.
I am unable to see what the actual error is as I just get a bunch of databinding 'cannot find symbol' errors if there is something wrong with the code related to Room.
Use:
object Converters {
#TypeConverter
#JvmStatic
fun fromTimestamp(value: Long?): Calendar? = value?.let { value ->
GregorianCalendar().also { calendar ->
calendar.timeInMillis = value
}
}
#TypeConverter
#JvmStatic
fun toTimestamp(timestamp: Calendar?): Long? = timestamp?.timeInMillis
}
And
#TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {

why the coroutine exception handler double the original exception?

I implements my own async, I can't process the exception in a right way. why?
val expected = IllegalStateException();
val it = async<Any> {
throw expected;
};
assert.that({ it.get() }, throws(equalTo(expected)));
// ^--- but it throws a IllegalStateException(cause = expected)
Source Code
interface Response<in T> {
suspend fun yield(value: T);
}
interface Request<out T> {
fun get(): T;
fun <R> then(mapping: (T) -> R): Request<R>;
}
private val executor: ExecutorService = ForkJoinPool(20);
fun <T> async(block: suspend Response<T>.() -> Unit): Request<T> {
return object : Request<T>, Response<T> {
#Volatile var value: T? = null;
var request: Continuation<Unit>? = block.createCoroutine(this, delegate {}).let {
var task: Future<*>? = executor.submit { it.resume(Unit); };
return#let delegate {
try {
val current = task!!;
task = null;
current.get();
} catch(e: ExecutionException) {
throw e.cause ?: e;
}
};
};
override fun <R> then(mapping: (T) -> R): Request<R> = async<R> {
yield(mapping(get()));
};
override fun get(): T {
return value ?: wait();
}
private fun wait(): T {
val it = request!!;
request = null;
it.resume(Unit);
return value!!;
}
suspend override fun yield(value: T) {
this.value = value;
}
};
}
inline fun <T> delegate(noinline exceptional: (Throwable) -> Unit = { throw it; }, crossinline resume: (T) -> Unit): Continuation<T> {
return object : Continuation<T> {
override val context: CoroutineContext = EmptyCoroutineContext;
override fun resumeWithException(exception: Throwable) {
exceptional(exception);
}
override fun resume(value: T) {
resume(value);
}
}
}
the strange behavior is comes from java. the ForkJoinTask#getThrowableException will rethrow exception for the given task:
Returns a rethrowable exception for the given task, if
available. To provide accurate stack traces, if the exception
was not thrown by the current thread, we try to create a new
exception of the same type as the one thrown, but with the
recorded exception as its cause. If there is no such
constructor, we instead try to use a no-arg constructor,
followed by initCause, to the same effect. If none of these
apply, or any fail due to other exceptions, we return the
recorded exception, which is still correct, although it may
contain a misleading stack trace.
which implies that if you don't want to rethrow exception for the give task you can make the exception constructor non-publicly, for example:
val exception = object: IllegalStateException(){/**/};
// ^--- its constructor only available in its scope

How can I get the name of a Kotlin property?

I have the following function to access a property's delegate. It uses Kotlin reflection to get a property's name and Java reflection to get the field.
fun Any.getDelegate<T>(prop: KProperty<T>): Any {
return javaClass.getDeclaredField("${prop.name}\$delegate").let {
it.setAccessible(true)
it.get(this)
}
}
The method is used like this:
val delegate = a.getDelegate(A::b)
However, I would prefer to use it like this:
val delegate = a.b.delegate
The problem with the code above is getting the property name of a.b and getting the instance a from a.b. From what I know about Kotlin, this is probably not possible, however I'd like to see if I can clean up my function at all.
To give a bigger picture of what I'm trying do here's my complete code. I want an observable delegate to which I can add and remove observers using the delegate reference and without creating addition variables.
fun Any.addObservable<T>(prop: KProperty<T>, observer: (T) -> Unit) {
getObservableProperty(prop).observers.add(observer)
}
fun Any.getObservableProperty<T>(prop: KProperty<T>): ObservableProperty<T> {
return getDelegate(prop) as ObservableProperty<T>
}
fun Any.getDelegate<T>(prop: KProperty<T>): Any {
return javaClass.getDeclaredField("${prop.name}\$delegate").let {
it.setAccessible(true)
it.get(this)
}
}
class ObservableProperty<T>(
initialValue: T,
initialObservers: Array<(T) -> Unit> = emptyArray()) : ReadWriteProperty<Any?, T> {
private var value = initialValue
public val observers: MutableSet<(T) -> Unit> = initialObservers.toHashSet()
public override fun get(thisRef: Any?, desc: PropertyMetadata): T {
return value
}
public override fun set(thisRef: Any?, desc: PropertyMetadata, value: T) {
this.value = value
observers.forEach { it(value) }
}
}
class A() {
var b by ObservableProperty(0)
}
fun main(args: Array<String>) {
val a = A()
a.addObservable(A::b) {
println("b is now $it")
}
a.b = 1
a.b = 2
a.b = 3
}
Edit:
I just realized that the function also isn't strict because the property delegate field name is referenced by KProperty name, which doesn't require a strong reference to the enclosing class. Here's an example to demonstrate the problem:
class A() {
var foo by ObservableProperty(0)
}
class B() {
var foo by ObservableProperty(0)
}
fun main(args: Array<String>) {
val a = A()
a.addObservable(B::foo) {
println("b is now $it")
}
a.foo = 1
a.foo = 2
a.foo = 3
}
This compiles and runs without error because A::foo and B::foo both result in a field string of "foo$delegate.
Right now reflection is all we can do to get to the delegate object. We are designing a language feature to have direct access to delegate instance, but it's long way to go.
This is how you get the name of a Kotlin Property (although only with an instance of the class). This part will be useful to anyone arriving at this question purely based off its title.
class Stuff(val thing: String)
val stuff = Stuff("cool stuff")
val thingFieldName = "${stuff.thing}\$delegate"
// value of thingFieldName is now "thing"
In terms of getting the delegate itself easier, they say you can now do this:
class Foo {
var bar: String by ReactiveProperty<String>()
}
val foo = Foo()
val bar = foo.bar
val barDelegate = ... // foo.bar$delegate
See ticket.

Resources