Pusher Chatkit Connect Callback in Java - java

I am trying to implement the Pusher ChatKit from the SDK site: https://docs.pusher.com/chatkit. I prefer to keep my code entirely in Java.
In the examples, there is this code block:
chatManager.connect { result ->
when (result) {
is Result.Success -> {
// We have connected!
currentUser = result.value // CurrentUser
}
is Result.Failure -> {
// Failure
handleConnectionError(result.error)
}
}
}
However, the above code is written in Kotlin. In my own code, mCurrentUser is null even after the chatManager.connect method runs.
I upgraded my Android Studio to run Java 8 and I'm trying to use the following code:
chatManager.connect(result -> {
if (result instanceof Result.Success) {
mCurrentUser = ((Result.Success<CurrentUser, Error>) result).getValue();
}
if (result instanceof Result.Failure) {
System.out.print(result);
}
return Unit.INSTANCE;
});
When I step through using the debugger, the entire block is skipped. The connect methods logs a successful connection, but result is always null. Can someone either show me how to write the same callback using a listener? Or, show me the issue with my lambda expressions.
By the way, chatManager.connect takes Function1<? super Result<CurrentUser, Error> Unit> callback as a parameter. Thanks in advance. I've been stuck on this for 2 days.

Related

Is there any special method that adding document of firebase into arrayList(Kotlin)? [duplicate]

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())
})

Kotlin multi platform observable pattern

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.

rxjava2 - if else on Maybe

I am looking for what is the recommended practice in rxjava2 to handle a case where one flowable leads to conditional behaviors.
More concretely, I have a Maybe<String> for which I want to Update the String on the database if the String exists or, if it doesn't exists I want to create a new String and save it on the database.
I thought of the below but obviously it is not what I am looking for:
Maybe<String> source = Maybe.just(new String("foo")); //oversimplified source
source.switchIfEmpty(Maybe.just(new String("bar"))).subscribe(result ->
System.out.println("save to database "+result));
source.subscribe(result -> System.out.println("update result "+result));
The above obviously produces
save to database foo
update result foo
I tried also the below which gives the expected result but still feel it's... weird.
Maybe<String> source = Maybe.just(new String("foo")); //oversimplified source
source.switchIfEmpty(Maybe.just(new String("bar")).doOnSuccess(result ->
System.out.println("save to database "+result))).subscribe();
source.doOnSuccess(result -> System.out.println("update result "+result)).subscribe();
How can I have an action for when the result exists and when it doesn't exists? How is that use case supposed to be handled in rxjava2?
Update 01
I tried the below and it looks cleaner than what I came up with above. Note sure it is recommended rxjava2 practice however...
Maybe.just(new String("foo"))
.map(value -> Optional.of(value))
.defaultIfEmpty(Optional.empty())
.subscribe(result -> {
if(result.isPresent()) {
System.out.println("update result "+result);
}
else {
System.out.println("save to database "+"bar");
}
});
You have the isEmpty() operator that will return you Boolean if the Maybe source is empty or not, and then you can flatMap it and write a if else statement depending on that Boolean
This is a common pattern in our code as well, though in our case the choices are themselves async. You can't get quite the right semantic by simply composing flatMapX and switchIfEmpty (in either order), so I am curious why this isn't part of the API.
Here's what we're doing for now (this example for when the 2 options are both Completables, we have similar things for the other types as well):
public static <T> Completable flatMapCompletable(Maybe<T> target,
#ClosureParams(FirstParam.FirstGenericType.class)
Closure<? extends CompletableSource> completableSupplier,
Supplier<CompletableSource> emptySupplier) {
Maybe<T> result = target.cache();
return result.isEmpty().flatMapCompletable(empty -> {
if (empty) {
return emptySupplier.get();
} else {
return result.flatMapCompletable(completableSupplier::call);
}
});
}
We're using Groovy, so we package these up as extension methods. I'm not thrilled with the need to use cache() so I'm wondering if there is a better alternative. From looking at the code, an operator which basically combines flatMapX and switch looks like it wouldn't be too hard (but I feel like I'm missing something).
Try something like this. checkDB can return a Maybe or Single or whatever which emits either an optional or a wrapper Object.
checkDB(String)
.flatMap(s -> {
if (s.isPresent()) {
return updateDB(s.get());
} else {
return insertDB("new String");
}
})
There is an solution using the flatMap call with 3 params
fun addOrUpdate(message: LocalMessage): Single<LocalMessage> {
return getById(message.id) // returns Maybe
.flatMap(
Function {
update(message) // onSuccess update call returns Single
},
Function {
Single.error(it) // onError
},
Callable {
add(message) // onComplete add call returns Single
}
)
}
}
Or shorter version
fun addOrUpdate(message: LocalMessage): Single<LocalMessage> {
return getById(message.id) // returns Maybe
.flatMap(
{
update(message) // onSuccess update call returns Single
},
{
Single.error(it) // onError
},
{
add(message) // onComplete add call returns Single
}
)
}
}

Invoke a a lambda function from a lambda function written in Java

Is there any good documentation how to invoke a lambda function from a lambda function written in java?
Is there a difference between invoking a lambda function from a lambda function or invoke from a normal java application?
The only thing that I found is the normal AWS Java SDK doc.
http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/index.html
Would be happy if someone could help me.
Yes, you can call lambdas normally from lambdas, just as you would if your code was executing on a host somewhere.
You'll have an additional step of ensuring that the lambda executing the call to another lambda has permissions to execute other lambda functions (the permission is called "lambda:InvokeFunction").
The rest of the steps are the same as regular java using the AWS SDK, you instantiate an AWSLambdaClient object, set your credentials and settings (region, etc.) and then create an InvokeRequest object which is the sub-lambda to be called with the appropriate payload.
Here's some sample java code which does exactly this, but it's also part of the standard sdk docs.
And also keep in mind that you'll still be subject to the original timeout for the first lambda, otherwise execution will be stopped.
Here's the relevant code snippets that you should be able to work from, I've made a change to how the second lamdba is called in terms of credentials - your can pass the credentials used to invoke the first lambda implicitly into the second one, which is probably a bit easier and more maintainable - you just need to ensure that the first lambda call gets the credentials and the rest will inherit them.
Region region;
AWSLambdaClient lambdaClient;
lambdaClient = new AWSLambdaClient(new DefaultAWSCredentialsProviderChain());
region = Region.getRegion(Regions.fromName(regionName));
lambdaClient.setRegion(region);
InvokeRequest invokeRequest = new InvokeRequest();
invokeRequest.setFunctionName(FunctionName);
invokeRequest.setPayload(ipInput);
returnDetails = byteBufferToString(
lambdaClient.invoke(invokeRequest).getPayload(),
Charset.forName("UTF-8"),logger);
edit: I should also point out that depending on your architecture, there's probably much cleaner options like using SQS, or depending on how simple your nested lambdas are, inlining them directly inside each other to avoid the extra call.
A lot of the used API functions are deprecated and the AWS docu is ... I share a new implemented example. The Lambda function "updateS3Chart" calls another Lambda function "AsyncUpdate" asynchronous:
public class LambdaInvoker {
static final Logger logger = LogManager.getLogger(LambdaInvoker.class);
static final String LambdaFunctionName = "AsyncUpdate";
private class AsyncLambdaHandler implements AsyncHandler<InvokeRequest, InvokeResult>
{
public void onSuccess(InvokeRequest req, InvokeResult res) {
logger.debug("\nLambda function returned:");
ByteBuffer response_payload = res.getPayload();
logger.debug(new String(response_payload.array()));
}
public void onError(Exception e) {
logger.debug(e.getMessage());
}
}
public void updateS3Chart(UpdateS3ChartRequest updateS3ChartRequest) {
Gson gson = new Gson();
try {
//issue: aws region is not set to debug-time. solution for eclipse:
//environment variable is set by lambda container or eclipse ide environment variables
//use instead for eclipse debugging: project -> Run as -> Run Configurations -> Environment -> Add variable: "AWS_REGION": "eu-central-1"
AWSLambdaAsync lambda = AWSLambdaAsyncClientBuilder.defaultClient(); //Default async client using the DefaultAWSCredentialsProviderChain and DefaultAwsRegionProviderChain chain
InvokeRequest req = new InvokeRequest()
.withFunctionName(LambdaFunctionName)
.withPayload(gson.toJson(updateS3ChartRequest));
Future<InvokeResult> future_res = lambda.invokeAsync(req, new AsyncLambdaHandler());
logger.debug("Waiting for async callback");
while (!future_res.isDone() && !future_res.isCancelled()) {
// perform some other tasks...
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
logger.debug("Thread.sleep() was interrupted!");
}
System.out.print(".");
}
} catch (Exception e) {
logger.fatal("Execute async lambda function: " + LambdaFunctionName + " failed: " + e.getMessage());
}
}
}
You have to set your AWS region as system property in your IDE for debugging (see comment in source code for Eclipse). UpdateS3ChartRequest is simple POJO with property set/get.

RXJava flow equivalent for Login + Webservicecall

i'm digging into rxJAva and i'm not sure i'm using the right logic.
What 'd need to achieve is the following. Consider (for now
LoginResult l;
if (hasCachedLoginResult()) {
l = getCachedLoginResult();
if ( l.timePassed > 20mins) {
l = null;
}
}
if (l == null) { // no cache or cache expired.
if (canPerformLogin() /* user has entered credentials */ ) {
l = performLogin();
} else {
showMessageToUserAskingCredentials();
return;
}
}
if (! l.isLoggedIn() ) {
showErrorToUser(l.errorMessage());
} else {
proceedWithNextWebserviceCall();
}
This is because i'd like to always pre-execute this "routine" on every webservice call that requires the user authentication.
I've already written the "performLogin" and "proceedWithNextWebserviceCall" as Observables but i don't know how to chain them together.
I saw there is a 'cache' operator but proceedWithNextWebserviceCall might throw an error when the user is not authenticated ( User have changed password and we're using the cached loginresult with the old cookies ) so i'd also need to invalidate the cached loginResult and wasn't able to find any documentation on this.
Thank you
I am also digging into RxJava, but your code doesn't seems rx at all for me. For chaining action, map method is really useful. So using rx and lambda, i would write it like this.
Observable.from(hasCachedLoginResult())
.map(hasCache -> hasCache ? getCachedLoginResult() : null)
.map(loginResult -> loginResult != null ? loginResult : performLogin())
.subscribe(loginResult -> proceedWithNextWebserviceCall(loginResult), errorLogin -> showError(errorLogin));
In short, you can use map method to chain action together.

Categories

Resources