rx kotlin subscription not working, not receiving items - kotlin

I created a function which returns an Observable<String> with file names, but I don't get any event in my subscription where I call this method. Also there is no call of onError, or onComplete
See my code:
fun getAllFiles(): Observable<String> {
val allFiles = File("/Users/stephan/Projects/Playground/kotlinfiles/")
.listFiles { file -> !file.isDirectory() }
return observable { subscriber ->
allFiles.toObservable()
.map { f -> "${f.name}" }
.doOnNext { println("Found file $it") }
.subscribe { subscriber}
}
}
fun test() {
getAllFiles()
.doOnNext { println("File name$it") }
.subscribe(
{n -> println("File: $n")},
{e -> println("Damn: $e")},
{println("Completed")})
}
Though everything is being called in the getAllFiles() function, so what am I missing?

observable is for creating an Observable from scratch but you already have Observable<String> from toObservable() so you don't need it. The code below works for me:
fun getAllFiles(): Observable<String> {
val allFiles = File("/Users/stephan/Projects/Playground/kotlinfiles/")
.listFiles { file -> !file.isDirectory }
return allFiles.toObservable()
.map { f -> "${f.name}" }
}
fun test() {
getAllFiles()
.doOnNext { println("File name $it") }
.subscribe(
{ n -> println("File: $n") },
{ e -> println("Damn: $e") },
{ println("Completed") })
}
You can also fix this by changing from:
.subscribe{subscriber}
to
.subscribe(subscriber)
but this nested Observable version is confusing to me.

Related

Kotlin coroutines, how to async alist of calls and return the result as a map

var responseMap = mutableMapOf<VendorType, ChargeResponse>()
requests.forEach {
val response = when (it.vendorType) {
VendorType.Type1 -> service.chargeForType1()
VendorType.Type2 -> service.chargeForType2()
else -> {
throw NotImplementedError("${it.vendorType} does not support yet")
}
}
responseMap[it.vendorType] = response
}
responseMap
So I want all the service.charge function run in separate thread. Return the map when all is done
Hope to solve your problem:
Assume your service and request like this:
interface Service {
suspend fun chargeForType1(): ChargeResponse
suspend fun chargeForType2(): ChargeResponse
}
data class Request(val vendorType: VendorType)
suspend fun requestAll(requests: List<Request>): Map<VendorType, ChargeResponse> {
return coroutineScope {
requests
.map { request ->
async {
request.vendorType to when (request.vendorType) {
VendorType.Type1 -> service.chargeForType1()
VendorType.Type2 -> service.chargeForType2()
else -> throw NotImplementedError("${request.vendorType} does not support yet")
}
}
}
.awaitAll()
.toMap()
}
}

Idiomatic Arrow

I have the following method:
internal typealias MaybeError<T> = Either<GenericError, T>
override fun createCompany(companyDomain: CompanyDomain): MaybeError<CompanyDomain> =
checkCompany(companyDomain).map { it.toEntity() }.fold({ Either.left(it) }) { company ->
with (companyRepository) {
isCompanyExists(company).fold({ Either.left(it) }) { isExists ->
if (isExists) return#with Either.left(CompanyNameExists(companyDomain))
createCompany(company).fold({ Either.right(companyDomain) }) { Either.left(it) }
}
}
}
Is there a better/more idiomatic way to write this using Arrow?
It is hard to refactor because I can only assume what used methods should return. But I guess the methods returns MaybeError. In this case we can omit fold({ Either.left(it) }) and we can use map or flatMap.
internal typealias MaybeError<T> = Either<GenericError, T>
override fun createCompany(companyDomain: CompanyDomain): MaybeError<CompanyDomain> =
checkCompany(companyDomain)
.map { it.toEntity() }
.flatMap { company ->
companyRepository.isCompanyExists(company)
.flatMap { isExists ->
if (isExists) {
MaybeError.left(CompanyNameExists(companyDomain))
} else {
companyRepository.createCompany(company)
}
}
}

Single with flowable?

Try in rxJava2 Kotlin combine Single with Flowable but nothing not happening:
Does not undrstand what wrong
Flowable.create<Int>({ emmit ->
loadNewListener = object :Listener {
override fun onEmit(id: Int) {
emmit.onNext(id)
}
}
}, BackpressureStrategy.LATEST)
.debounce(500, TimeUnit.MILLISECONDS)
.flatMapSingle {
loadNew(id = it.id)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ (data:Data) ->
}, {
Timber.e("Failed load data ${it.message}")
})
my method is returning Single:
private fun loadNew(id: Int): Single<Data> {
return when (pdfType) {
CASE_0 -> {
Single.create<Data> { emmit ->
service.get("data")
.enqueue(
object : Callback<Void> {
override fun onFailure(call: Call<Void>?, t: Throwable?) {
// failure
}
override fun onResponse(call: Call<Void>?, response: Response<Void>?) {
emmit.onSuccess(it.data)
}
}
}//single
}//case_0
CASE_1 -> 1Repository.loadsome1Rx(id = id).map { it.getData() }
CASE_2 -> 2Repository.loadsom2LocalRx(id = id).map { it.getData() }
else -> {
throw java.lang.RuntimeException("$this is not available type!")
}
}
What is wrong im my code?
Need Maby call Single in Flowable subscribe() seppurate
like this?
Flowable.create<Int>({ emmit ->
loadNewListener = object :Listener {
override fun onEmit(id: Int) {
emmit.onNext(id)
}
}
}, BackpressureStrategy.LATEST)
.debounce(500, TimeUnit.MILLISECONDS)
.subscribe({
loadNew(id = it.id)
}, {
Timber.e("")
})
This code is workin but looks not simple as via combine try.
This simple example based on your code is working
var i = 0
fun foo() {
Flowable.create<Int>({ emmit ->
emmit.onNext(i)
i++
}, BackpressureStrategy.LATEST)
.debounce(500, TimeUnit.MILLISECONDS)
.flatMapSingle {
Single.create<String> { emmit ->
emmit.onSuccess("onSuccess: $it")
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
Log.i("RX", "Subscribe: $it")
}, {
it.printStackTrace()
})
}
Check SingleEmitter.onSuccess() and SingleEmitter.onError() is called in all cases in when (pdfType)...
As #Stas Bondar said in answer below This simple example based on your code is working!!
Problem was in loadNewListener .
It does not init in time and has null value when need. Call create Flowable on init ViewModel but loadNewListener did not have time to create when i call him from fragment.
loadNewListener = object :Listener{...}
Becuse need some time mutch for init rxJava expression!
And combine flowable with single via flatMapSingle spent more time than just call single on flowable dubscrinbe!
So use temp field:
private var temp: Temp? = null
fun load(id: Int) {
loadNewListener.apply {
when {
this != null -> load(id = id)
else -> userEmitPdfTemp = Temp(id = id)
}
}
}
Flowable.create<Data>({ emmit ->
userEmitPdfTemp?.let {id->
emmit.onNext(Data(id))
userEmitPdfTemp =null
}
loadNewListener = object :Listener {
override fun load(id: Int) {
emmit.onNext(Data(id))
}
}
}

Observable from merged Observable.just and Subject emits nothing

i have a chain of calls from a presenter to repository which returns an observable. This is the code:
Presenter:
private fun getCategories() =
compositeDisposable.add(
categoriesUseCase.getCategories()
.timeout(TIMEOUT, TIMEOUT_UNIT)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleCategories, this::handleCategoriesTimeout)
)
This is the usecase:
fun getCategories(): Observable<List<Category>> =
repository.getCategories()
.map { it.map { Category(it.id, it.text, it.icon) } }
This is the repo: //subject is BehaviorSubject.create()
fun getcategories(): Observable<List<DiscoverabilityCategoryElement>> =
Observable.just(storage.getCategories())
.mergeWith { subject.flatMapIterable { it.categories }.publish() }
.subscribeOn(Schedulers.io())
.doOnNext { Logger.d("Data", "next categories $it") }
.filter { it.isPresent }
.map { it.get() }
.take(1)
.doOnSubscribe { Logger.d("Data", "Subcribed categories") }
fun saveApiResult(response: Response) {//This is being called after subscribe
subject.onNext(response.categories)
subject.onComplete()
}
Method on storage will always return Optional.empty() (Meanwhile i'm developing)
My problem is, even seeing that subject.onNext is being called, that value never comes to the presenter, i've debug a bit and subject always returns false to hasObservables, maybe i'm losing my observer in some point?
Why do you call publish() on that line? It returns a ConnectableObservable which does nothing until connect is called. However, there is nothing on that line that would require sharing.
Try this:
fun getcategories(): Observable<List<DiscoverabilityCategoryElement>> =
Observable.just(storage.getCategories())
.mergeWith { subject.flatMapIterable { it.categories } } // <-------------
.subscribeOn(Schedulers.io())
.doOnNext { Logger.d("Data", "next categories $it") }
.filter { it.isPresent }
.map { it.get() }
.take(1)
.doOnSubscribe { Logger.d("Data", "Subcribed categories") }
Solution was change
.mergeWith { subject.flatMapIterable { it.categories }.publish() }
by
.mergeWith(subject.flatMap({ rootElement -> Observable.fromArray(element.categories.toOptional()) }))

Chain two request and return the first one

I have a class A like this:
A {
id: Long
eventId: Long
event: Event
}
B{
id:Long
name: String
}
I want to retrieve A by executing:
aService.getA(id)
then with the result (which has a null event) use eventId to retrieve the proper Event (eventService.getEvent()), assign it to A.event, and then return A.
How can I chain the request to achieve this? I tried flatmap to return the Event but then I lose the result from A.
This is my current implementation:
aRepository.getA().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
loadingState.onNext(true)
}
.doOnEvent { t1: Highlight, t2 ->
loadingState.onNext(false)
}
.subscribeWith(object : DisposableSingleObserver<A>() {
override fun onSuccess(a: A) {
aObservable.onNext(a)
}
override fun onError(e: Throwable) {
fetchErrors.onNext(e)
}
})
I tried this:
aRepository.getA()
.flatMap {
a: A ->
val event = eventsRepository.getEvent(a.eventId)
event
}
,subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
loadingState.onNext(true)
}
.doOnEvent { t1: Highlight, t2 ->
loadingState.onNext(false)
}
.subscribeWith(object : DisposableSingleObserver<A>() {
override fun onSuccess(a: A) {
aObservable.onNext(a)
}
override fun onError(e: Throwable) {
fetchErrors.onNext(e)
}
})
Use flatMap and just map its inner flow back to the updated original value:
aRepository.getA()
.subscribeOn(Schedulers.io())
.flatMap(a -> {
if (a.event == null) {
return eventsRepository.getEvent(a.eventId)
.map(evt -> {
a.event = evt;
return a;
});
}
return Single.just(a);
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(/* ... */)
;