mapping custom data RxAndroid with Kotlin - android

I am trying to convert examples from this article from Java to Kotlin.
I get error from picture at Exmaple 5:
And I noticed, that without map() function I don't get this error
So, what the point of this error and how to write it right?

The return value of a lambda in Kotlin is always the last expression in the block.
So in this case the result of
.map { it.note = it.note.toUpperCase() }
is not returning a meaningful value.
What you should do instead is this
.map {
it.note = it.note.toUpperCase()
it
}
Which returns a type of Note instead of Unit.

Related

In jetpack what is setContent? [duplicate]

Passing a lambda to the last parameter
In Kotlin, there is a convention that if the last parameter of a
function accepts a function, a lambda expression that is passed as the
corresponding argument can be placed outside the parentheses:
val product = items.fold(1) { acc, e -> acc * e }
What is the purpose of this syntax?
This syntax gives Kotlin great DSL capabilities, it makes functions look like language constructions. For example:
with(car) {
startUp()
goToDestination()
}
Here with looks like it is language construction, whereas it is a simple function, receiving lambda as the last parameter.
And this leads to such elegant things like Kotlin HTML DSL

Compiler complains about wrong type, but it is clearly correct

I have the following issue with Kotlin, here is my code:
val dishesBitmaps: LiveData<List<Bitmap>> = liveData {
val data = getDishesBitmaps()
data?.let {
emit(data)
}
}
getDishesBitmaps() returns List<Bitmap>? however, the compiler shows me the following error
Type inference failed. Expected type mismatch: inferred type is LiveData?> but LiveData> was expected
but data clearly becomes List<Bitmap> after the null check. The strange thing is that it compiles fine if I explicitly cast it like this emit(data as List<Bitmap>) (but again IDE shows that the cast is not needed).
Is this a bug in the compiler or am I doing something wrong?
but data clearly becomes List<Bitmap> after the null check
The compiler could make a smart cast there, but I guess currently it doesn't. Doing the simpler
if (data != null) { emit(data) }
should work. The usual reason to use ?.let instead is if the value checked is a var, which doesn't apply here.
The reason emit(it) works is the type parameter inferred for let.
The strange thing is that it compiles fine if I explicitly cast it like this emit(data as List<Bitmap>) (but again IDE shows that the cast is not needed).
That's not strange by itself, by the cast you are telling the compiler "it doesn't matter what you think type of data is, I know better". It would compile even if you had data: Any?.
The IDE showing the cast is unnecessary is the strange part and something I'd consider a bug (which could well be fixed by making the cast actually unnecessary).
Just place a ? after the LiveData<List<Bitmap>>. Looks like it's nullable. Or you can leave it as it is and change the data? to data!!
Yes as you are adding the null safety on data
data?.let {
emit(data)
}
But your function return type is not null so you need to add ? with the function. So it can be a null value
val dishesBitmaps: LiveData<List<Bitmap>>? = liveData {
val data = getDishesBitmaps()
data?.let {
emit(data)
}
}
data?.let {
emit(data)
}
This block of code translates to
if (date != null) emit(data) else null
If you want return null safety object use elvis
data?.let {
emit(data)
} ?: /**code if data is null**/

flatMap() in kotlin

If i make a Single invocation using Retrofit as example in Kotlin, i want to check the resulted answer and keep going with a Single or an error. As example
model.doRequest()
.flatMap { t: Response ->
if(!h.hasError) {
return#flatMap model.doAnotherRequest()
} else {
return#flatmap Single.error<Throwable>(Throwable("error))
}
}
If i make another flatMap(), subscribe() or any other things, RxJava won't know that I want to continue with response given by the doAnowhtRequest(), instead will return Any!. How i can get data given by second request?
In Java, Single.error isn't interpreted so RxJava will continue to give me the response in next invocations.
Assuming you want to return the same thing as doAnotherRequest() the problem is with Single.error<Throwable>(Throwable("error)). You're hitting the compiler that you're returning a Single<Throwable>, but you want to return a Single<whatever doAnotherRequest returns>.
Say your doAnotherRequest returns Single<Foo>, then you'd want to return Single.error<Foo>(Throwable("error)), which will return a single that will emit an error with the given exception.
Kotlin tries to infer the type you want to return, but because you're returning 2 types that the "most common" type is Any, kotlin can only infer it's that you want to return.

RxJava2 Single.Concat for repository pattern

I am using Room with RxJava2 to implement my data layer via Repository Pattern principles.
I have the following simple code which decides where to pick data from.
#Override
public Single<Team> getTeamById(int teamId) {
return Single.
concat(local.getTeamById(teamId),
remote.getTeamById(teamId)).
filter(team -> team != null).
firstOrError();
}
The problem here is that instead of going to the remote source , it returns an error from the first source (local) if the data was not available.
android.arch.persistence.room.EmptyResultSetException: Query returned empty result set: select * from teams where id = ?
How should I instruct the concat to forgo any error that is received and continue its concatenation?
Aslong you're not sure if you can receive at least one Team from you data provider, you should probably think of using Maybe instead of Single.
You can lookup the definition here:
Single as it states:
it always either emits one value or an error notification
Use Maybe instead:
Maybe
there could be 0 or 1 item or an error signalled by some reactive
source
As your error already states there seems to be a problem while extracting results from your query.
Handle your result extraction correctly, so that you check if there are results before trying extracting any. Therefor the Maybe would either return 0 or 1 item, and not throw any error at all when no Team was found.
You cannot pass null in RxJava2. So whenever your local repo is empty you just can't return null in your single. There was a question o stack about handling null objects: Handle null in RxJava2
Also here you can find an article showing you preferred implementation of repository pattern using RxJava2:
https://android.jlelse.eu/rxjava-2-single-concat-sample-for-repository-pattern-1873c456227a
So simplifying - instead of returning null from both local and remote repo pass some sort of "empty" object. That will be useful also in your business logic allowing you to recognize empty set of data.
If you want to continue when the first source errors (instead of completing as empty), you can use onErrorResumeNext instead of concat (I assume both get calls return Observable, adjust as necessary):
return local.getTeamById(teamId)
.onErrorResumeNext(error -> {
if (error instanceof EmptyResultSetException) {
return remote.getTeamById(teamId));
}
return Observable.error(error);
})
.firstOrError();
I used Maybe to solve my Rxjava2 repository pattern problem.
In your case, I would use the following code to sort it out:
//you may need to rewrite your local.getTeamById method
protected Maybe<Team> getTeamById(int teamId) {
Team team = localDataHelper.getTeamById(teamId);
return team != null ? Maybe.just(team) : Maybe.empty();
}
#Override
public Single<Team> getTeamById(int teamId) {
Maybe<Team> cacheObservable = local.getTeamById(teamId);
Maybe<Team> apiCallObservable = remote.getTeamById(teamId).toMaybe();
return Maybe.concat(cacheObservable, apiCallObservable)
.toSingle();
}

How to convert Map<String, Spending> object to `Observable<List<Spendings>>` on rxjava2?

I am trying to migrate rxjava to rxjava2.
Map<String, Task> mCachedTasks;
I can convert it to Observable<List<Task>> by using the following line of code. Observable.from(mCachedTasks.values()).toList()
However, I cannot convert it on rxjava2.
1- I tried the following code.
Observable.fromIterable(mCachedTasks.values()) but it returns Observable<Task>
2- I also tried to use fromArray method, such as Observable.fromArray(mCachedTasks.values().toArray()). That gives me Observable<Object>.
How can I convert Map<String, Task> to Observable<List<Task>> on rxjava2?
I can convert it to Observable> by using the following line of code. Observable.from(mCachedTasks.values()).toList()
That's an option, but you are unnecessarily flatting the list - the from(), you taking a list and emit each item in onNext() , and then collect it back as a list using toList(), which waits for all the emissions and then emit single onNext() with the collected list.
However, I cannot convert it on rxjava2. 1- I tried the following
code. Observable.fromIterable(mCachedTasks.values()) but it returns
Observable<Task>
fromIterable() it's RxJava2 equivalent to from() of RxJava1, you can use again toList() - but in RxJava2 you will get Single instead of Observable (which is more concise API).
2- I also tried to use fromArray method, such as
Observable.fromArray(mCachedTasks.values().toArray()). That gives me
Observable<Object>
That's because toArray() return array of Object not Task.
The solution is rather simple, you actually don't need RxJava for the transformation , you've got already an in-memory Map, so first convert it to a List (if you insisting on List specifically):
List<Task> tasksList = new ArrayList<>(mCachedTasks.values());
and then use just() operator to create an Observable that emit this list as it's single value:
Observable<List<Task>> taskListObservable = Observable.just(tasksList);
Observable.fromIterable(mCachedTasks.values()).toList().toObservable();
Another option is to create a lazy Observable:
Observable<List<Task>> taskListObservable = Observable.fromCallable(
//this will be called each time someone subscribes
() -> {
//do not forget the defensive copy
return new ArrayList<>(mCachedTasks.values());
}
);
This way mCachedTasks.values() will always get the fresh version and there will be no unnecessary flattening with toList() operator.
public Observable> getOrganizations() {
List<Organization> list=new ArrayList<>(mcached.values());
if(mcached!=null && !mCacheIsDirty){
//return your map as list
return Observable.just(list);
}else {
mcached=new LinkedHashMap<>();
}
}

Categories

Resources