I have 2 apps on android and ios side. Both of apps using apollo graphql library. So i decided to use it on kotlin multi library project. Everything works well with this type of usage
suspend fun addFeedback(request: AddFeedBackRequest): BaseResponseModel<Any> {
val feedBackType = if (request.type == AddFeedBackType.Bug) {
FeedbackType.BUG
} else {
FeedbackType.FEEDBACK
}
val input = AddFeedbackInput(request.note, Optional.presentIfNotNull(feedBackType))
val mutation = AddFeedbackMutation(input)
val response = executeMutation(mutation)
if (response.isFailed()) return onError()
return onSuccess()
}
and when i want to use this method on android application i can use it like this
lifecycleScope.launch {
HelpRepository().addFeedback(AddFeedBackRequest(AddFeedBackType.Bug, ""))
}
I've been searching about observable pattern i found [https://github.com/badoo/Reaktive1
library for observable rx kotlin usages
For example i want to use addFeedBack method on android side like this
HelpRepository()
.addFeedback()
.onSuccess { response ->
}
.onLoading { state ->
}
.onError { err ->
}
Last two days i searched about this structure but i couldn't much more anything. Any re-search subjects or example projects that you can help please ?
Thanks for that
If you want to use Reaktive, you can do the following:
Add the following dependencies: com.badoo.reaktive:reaktive:<version>, com.badoo.reaktive:coroutines-interop:<version>
Change you method signature to the following: fun addFeedback(request: AddFeedBackRequest): Single<T>
Inside this method use singleFromCoroutine {} function to wrap the suspend call executeMutation into Single<T>
Use it as follows:
val disposable = HelpRepository()
.addFeedback()
.doOnBeforeSubscribe { /* Loading started */ }
.subscribe(
onSuccess = { response -> },
onError = { e -> }
)
Since you are also sharing the code with iOS, please read the following docs: for the old memory model, for the new memory model.
Related
I'm building an app for a friend and I use Firestore. What I want is to display a list of favorite places but for some reason, the list is always empty.
I cannot get the data from Firestore. This is my code:
fun getListOfPlaces() : List<String> {
val places = ArrayList<String>()
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
for (document in task.result) {
val name = document.data["name"].toString()
places.add(name)
}
}
}
return list;
}
If I try to print, let's say the size of the list in onCreate function, the size is always 0.
Log.d("TAG", getListOfPlaces().size().toString()); // Is 0 !!!
I can confirm Firebase is successfully installed.
What am I missing?
This is a classic issue with asynchronous web APIs. You cannot return something now, that hasn't been loaded yet. With other words, you cannot simply return the places list as a result of a method because it will always be empty due the asynchronous behavior of the onComplete function. Depending on your connection speed and the state, it may take from a few hundred milliseconds to a few seconds before that data is available.
But not only Cloud Firestore loads data asynchronously, almost all of modern other web APIs do, since it may take some time to get the data. But let's take an quick example, by placing a few log statements in the code, to see more clearly what I'm talking about.
fun getListOfPlaces() : List<String> {
Log.d("TAG", "Before attaching the listener!");
val places = ArrayList<String>()
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d("TAG", "Inside onComplete function!");
for (document in task.result) {
val name = document.data["name"].toString()
places.add(name)
}
}
}
Log.d("TAG", "After attaching the listener!");
return list;
}
If we run this code will, the output in your logcat will be:
Before attaching the listener!
After attaching the listener!
Inside onComplete function!
This is probably not what you expected, but it explains precisely why your places list is empty when returning it.
The initial response for most developers is to try and "fix" this asynchronous behavior, which I personally recommend against it. Here is an excelent article written by Doug Stevenson that I'll highly recommend you to read.
A quick solve for this problem would be to use the places list only inside the onComplete function:
fun readData() {
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val list = ArrayList<String>()
for (document in task.result) {
val name = document.data["name"].toString()
list.add(name)
}
//Do what you need to do with your list
}
}
}
If you want to use the list outside, there is another approach. You need to create your own callback to wait for Firestore to return you the data. To achieve this, first you need to create an interface like this:
interface MyCallback {
fun onCallback(value: List<String>)
}
Then you need to create a function that is actually getting the data from the database. This method should look like this:
fun readData(myCallback : MyCallback) {
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val list = ArrayList<String>()
for (document in task.result) {
val name = document.data["name"].toString()
list.add(name)
}
myCallback.onCallback(list)
}
}
}
See, we don't have any return type anymore. In the end just simply call readData() function in your onCreate function and pass an instance of the MyCallback interface as an argument like this:
readData(object: MyCallback {
override fun onCallback(value: List<String>) {
Log.d("TAG", list.size.toString())
}
})
If you are using Kotlin, please check the other answer.
Nowadays, Kotlin provides a simpler way to achieve the same result as in the case of using a callback. This answer is going to explain how to use Kotlin Coroutines. In order to make it work, we need to add the following dependency in our build.gradle file:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.2.1"
This library that we use is called Module kotlinx-coroutines-play-services and is used for the exact same purpose. As we already know, there is no way we can return a list of objects as a result of a method because get() returns immediately, while the callback from the Task it returns will be called sometime later. That's the reason why we should wait until the data is available.
When calling "get()" on the Task object that is returned, we can attach a listener so we can get the result of our query. What we need to do now is to convert this into something that is working with Kotlin Coroutines. For that, we need to create a suspend function that looks like this:
private suspend fun getListOfPlaces(): List<DocumentSnapshot> {
val snapshot = placesRef.get().await()
return snapshot.documents
}
As you can see, we have now an extension function called await() that will interrupt the Coroutine until the data from the database is available and then return it. Now we can simply call it from another suspend method like in the following lines of code:
private suspend fun getDataFromFirestore() {
try {
val listOfPlaces = getListOfPlaces()
} catch (e: Exception) {
Log.d(TAG, e.getMessage()) //Don't ignore potential errors!
}
}
The reason for having a empty list got perfectly answered by Alex Mamo above.
I just like to present the same thing without needing to add an extra interface.
In Kotlin you could just implement it like so:
fun readData(myCallback: (List<String>) -> Unit) {
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val list = ArrayList<String>()
for (document in task.result) {
val name = document.data["name"].toString()
list.add(name)
}
myCallback(list)
}
}
}
and then use it like so:
readData() {
Log.d("TAG", it.size.toString())
})
I'm Using Project Reactor library. Here is my scenario.
I want to call the blocking service inside my non blocking method.
I have a three different services, I called those three services from my springboot application. Here is my sample code
public Mono<Example> getValuesFromDifferentServices() {
Mono<Example1> mono1=service.getService1();
Mono<Example2> mono2=service.getService2();
mono1.zipwith(mono2)
.map(value-> {
// some logics then
if(value.getT1().getStatus().equals(value.getT2().getStatus())) {
Mono<Example3> mono3 = service.getService3(true);
mono3.map(f-> {
value.getT1().setSomething(f.getSomething);
return f;
}).subscribe();
}
return value.getT1();
})
}
Note: Above example is not the actual logic. But the implementation is similar to that
Even I tried to subscribe() it, I couldn't get the 3rd service value all the time (uncertainty values). I cannot block() the 3rd service since it is not allowed. How to achieve this?
Update: 3rd Service input would be decided after If condition either it should be true or not Mono<Example3> mono3 = service.getService3(true);
We should call the 3rd service if only the condition matches, otherwise calling the 3rd service is not required and which is not advisable., If condition doesn't match, we should not invoke 3rd service.
This example is a little wierd but as I understand, you want to call the first two services, each give you back a single value.
After that you want to call the third one if necessary and set a value from this into one of the first's field.
Anyway, there is a simple solution, but with more information maybe we can create nicer stream. This stream takes adventages of flatMap, which eagerly subscribes into the inner publisher.
[The example was written in Kotlin, it's very like Java. The only confusig thing here maybe the it variable, which is equals something like this: map(it -> it.sg )]
data class Example(
val name: String,
val status: String,
var value: String? = null
)
class ReactorTest {
#Test
fun test() {
val first = Mono.just(Example("first", "suspended"))
val second = Mono.just(Example("second", "suspended"))
val third = Mono.just(Example("third", "suspended", "thirdValue"))
val stream = first.zipWith(second)
.flatMap { tuple ->
Mono.just(tuple.t1)
.filter { it.status == tuple.t2.status }
.zipWith(third)
.doOnNext {
it.t1.value = it.t2.value
}
.map { it.t1 }
.switchIfEmpty(Mono.just(tuple.t1))
}
StepVerifier.create(stream)
.expectNext(Example("first", "suspended", "thirdValue"))
.verifyComplete()
}
#Test
fun test2() {
val first = Mono.just(Example("first", "suspended"))
val second = Mono.just(Example("second", "active"))
val third = Mono.just(Example("third", "suspended", "thirdValue"))
val stream = first.zipWith(second)
.flatMap { tuple ->
Mono.just(tuple.t1)
.filter { it.status == tuple.t2.status }
.zipWith(third)
.doOnNext {
it.t1.value = it.t2.value
}
.map { it.t1 }
.switchIfEmpty(Mono.just(tuple.t1))
}
StepVerifier.create(stream)
.expectNext(Example("first", "suspended"))
.verifyComplete()
}
}
Side note: if you're using blocking services in your reactive streams, those should be separated into dedicated threadpools. Like:
fun blockingService(): Mono<String> {
//real service use fromCallable
return Mono.just("fromCallableOnServiceCall")
//for real service it may use a dedicated pool
.subscribeOn(Schedulers.boundedElastic())
}
I have a suspend function from which I want to return the result of a Java 5 Future. The future object comes from another library Firebase Cloud Firestore- Admin SDK for Java and provides a blocking call get() to retrieve the result of the said future.
My function looks like this-
suspend fun getPrefix(messageCreateEvent: MessageCreateEvent): String {
val snapshot = db.collection("prefixes")
.document(messageCreateEvent.guildId.get().asString())
.get() //This returns a future
.get() //Retrieves the future's result (Blocks thread; IDE gives warning)
//Return the prefix
return if (snapshot.exists())
snapshot.getString("prefix") ?: DEFAULT_PREFIX
else DEFAULT_PREFIX
}
Solutions I have considered
The first thing that I considered was to look in kotlinx.coroutine for extensions to bridge the futures. While the extensions exist, they do only for CompletionStatge. So I decided to wrap the future into one ()-
val snapshot = CompleteableFuture.supplyAsync {
db.collection("prefixes")
.document(messageCreateEvent.guildId.get().asString())
.get() // This returns a future
.get() // Get the result
}.await()
I am quite inexperienced and not sure if this is was proper solution. I queried my question on a programming community, where a person recommended me to use a Deferred-
val deferred = CompletableDeferred<DocumentSnapshot>()
val future = db.collection("prefixes")
.document(messageCreateEvent.guildId.get().asString())
.get()
future.addListener(
Runnable { deferred.complete(future.get()) },
ForkJoinPool.commonPool()
)
val snapshot = deferred.await()
I've give it quite a time to search for a way to bridge futures to co-routines, there isn't even a similar question on SO. Through, I wouldn't be surprised if this question gets a duplicate mark.
The key to this problem is the suspendCoroutine function. The other non-obvious bit is that to add a callback to the ApiFuture you use a static method on ApiFutures.
Here's an extension function that implements await() on an ApiFuture.
/**
* Function to convert an ApiFuture into a coroutine return.
*/
suspend fun <F : Any?, R : Any?> ApiFuture<F>.await(
successHandler: (F) -> R,
): R {
return suspendCoroutine { cont ->
ApiFutures.addCallback(this, object : ApiFutureCallback<F> {
override fun onFailure(t: Throwable?) {
cont.resumeWithException(t ?: IOException("Unknown error"))
}
override fun onSuccess(result: F) {
cont.resume(successHandler(result))
}
}, Dispatchers.IO.asExecutor())
}
}
/**
* inline function to retrieve a document as a POJO from a DocumentReference
*/
suspend inline fun <reified T: Any>DocumentReference.toObject(): T? {
return get().await<DocumentSnapshot, T?> {
it.toObject(T::class.java)
}
}
Now you can write things like:
suspend fun getUser(id: String): User? {
return db.collection("users").document(id).toObject()
}
You can use kotlinx-coroutines-guava to do this
First convert ApiFuture to ListenableFuture using ApiFutureToListenableFuture then use .await to await completion of the listenableFuture without blocking a thread.
ApiFutureToListenableFuture(this).await()
I have a problem that has been challenging me for a few days with no resolution (more directly, no resolution I feel is correct). The issue is around callbacks, Java implementation vs Kotlin implementation
I have this Java method:
public void setOnSelectionChange(MapControlUpdate mapMenuControl) {
this.mapControlUpdate = mapMenuControl;
}
private MapControlUpdate mapControlUpdate;
public interface MapControlUpdate {
void onSelectionChange(MAP_TYPE mapType);
}
Using the above implementation I have what I want (below) in both Java and Kotlin.
Java (before):
widgetMapType.setOnSelectionChange(mapType -> {
Toast.makeText(getContext(), "clicked: " + mapType, Toast.LENGTH_LONG).show();
});
Kotlin (before):
widgetMapType.setOnSelectionChange {
Toast.makeText(context, "clicked: $it", Toast.LENGTH_LONG).show()
}
The new Kotlin code, after conversion is:
fun setOnSelectionChange(mapMenuControl: MapControlUpdate?) {
mapControlUpdate = mapMenuControl
}
private var mapControlUpdate: MapControlUpdate? = null
After the conversion to Kotlin the Java usage remains unchanged but I need to change the Kotlin code as follows or I get a syntax error:
Kotlin (after):
widgetMapType.setMapMenuControl(object: WidgetMapType.MapControlUpdate {
override fun onSelectionChange(mapType: WidgetMapType.MAP_TYPE?) {
Toast.makeText(context, "clicked: $mapType", Toast.LENGTH_LONG).show()
}
})
In order to get back to where I'd like to be I found that the only solution appear to be to implement 2 callbacks; 1 to allow Java to work with the original syntax and another to allow Kotlin syntax to remain the same.
This is the code I'm using (it works):
var onSelectionChange: MapControlUpdate? = null
private var onSelectionChangeListener: ((MapDisplayTypes?) -> Unit)? = null
fun setOnSelectionChange(listener: (MapDisplayTypes?) -> Unit){
onSelectionChangeListener = listener
}
and I fire both callbacks as appropriate
onSelectionChange?.onSelectionChange(it) // Java
onSelectionChangeListener?.invoke(it) // Kotlin
I really cannot believe that there isn't a more correct method but my searches (here and on the web) have returns tons of examples for Kotlin and Java but they all align with my above examples based on the code (also shown above). I suspect there maybe an annotation or something that I'm missing so finding no other solution I'm turning to the community here.
Thank you ahead of time!!
I suspect that you will need to keep just your interface MapControlUpdate definition in Java.
Instead of two callbacks you would keep just one callback and convert as appropriate:
var onSelectionChange: MapControlUpdate? = null
fun setOnSelectionChange(listener: (WidgetMapType.MAP_TYPE?) -> Unit){
onSelectionChangeListener = object : MapControlUpdate {
override fun onSelectionChange(mapType: WidgetMapType.MAP_TYPE?) {
listener(mapType)
}
}
}
Or write a helper function if MapControlUpdate is used more than once.
But the real solution is, as karmakaze says: keep the interface in Java until Kotlin 1.4 and then declare it as fun interface.
I've been working through the examples in the book Reactive Programming with RxJava, which is targeted at version 1 not 2. An introduction to infinite streams has the following example (and notes there are better ways to deal with the concurrency):
Observable<BigInteger> naturalNumbers = Observable.create(subscriber -> {
Runnabler = () -> {
BigInteger i = ZERO;
while (!subscriber.isUnsubscribed()) {
subscriber.onNext(i);
i = i.add(ONE);
}
};
new Thread(r).start();
});
...
Subscription subscription = naturalNumbers.subscribe(x -> log(x));
/* after some time... */
subscription.unsubscribe();
However, in RxJava 2, the lambda expression passed to the create() method is of type ObservableEmitter and this doesn't have an isUnsubscribed() method. I've had a look in What's Different in 2.0 and also performed a search of the repository but can't find any such method.
How would this same functionality be achieved in 2.0?
Edited to include solution as given below (n.b. using kotlin):
val naturalNumbers = Observable.create<BigInteger> { emitter ->
Thread({
var int: BigInteger = BigInteger.ZERO
while (!emitter.isDisposed) {
emitter.onNext(int)
int = int.add(BigInteger.ONE)
}
}).start()
}
val first = naturalNumbers.subscribe { log("First: $it") }
val second = naturalNumbers.subscribe { log("Second: $it") }
Thread.sleep(5)
first.dispose()
Thread.sleep(5)
second.dispose()
After you subscribe to Observable, Disposable is returned. You can save it to your local variable and check disposable.isDisposed() to see if it still subscribing or not.