Incase anyone still looking for a solution i ended up using a loop on the code blew i did not find an official api to do multiple files upload.
-------
I have an ArrayList of ImageFiles, that I want to upload to Amazon s3. Their doc provides this code:
credentials = new BasicAWSCredentials(key, secret);
s3 = new AmazonS3Client(credentials);
transferUtility = new TransferUtility(s3, getContext());
observer.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
}
#Override
public void onError(int id, Exception ex) {
}
});
observer = transferUtility.upload("buket name", upload_file.getNew_name(),
new File(upload_file.getFile_path()));
But this code only takes one file. How can i upload multiple files at once? And if they do not allow this so user can make more requests , what the alternative to do this ??
I know I'm late to answer but it will help others who'll come here looking for an answer.
Currently, the only option to upload multiple files is to use a loop passing the files from list as individual file but here's what I found last night and implemented it. I've tested it many times so far.
Advantage of this method is it runs concurrently for every file and not wait for every file to upload or download first to operate on the next file.
It's what I found here but I've modified it a little bit for my use in Kotlin.
First, Create a class, I've named it MultiUploaderS3 -
import android.content.Context
import com.amazonaws.mobileconnectors.s3.transferutility.TransferListener
import com.amazonaws.mobileconnectors.s3.transferutility.TransferState
import com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.Single
import java.io.File
class MultiUploaderS3 {
private fun transferUtility(context: Context): Single<TransferUtility?>? {
return Single.create { emitter ->
emitter.onSuccess(
TransferUtility(s3ClientInitialization(context), context)
)
}
}
fun uploadMultiple(fileToKeyUploads: Map<File, String>, context: Context): Completable? {
return transferUtility(context)!!
.flatMapCompletable { transferUtility ->
Observable.fromIterable(fileToKeyUploads.entries)
.flatMapCompletable { entry ->
uploadSingle(
transferUtility,
entry.key,
entry.value
)
}
}
}
private fun uploadSingle(
transferUtility: TransferUtility,
aLocalFile: File?,
toRemoteKey: String?
): Completable? {
return Completable.create { emitter ->
transferUtility.upload(bucketName,toRemoteKey, aLocalFile)
.setTransferListener(object : TransferListener {
override fun onStateChanged(
id: Int,
state: TransferState
) {
if (TransferState.FAILED == state) {
emitter.onError(Exception("Transfer state was FAILED."))
} else if (TransferState.COMPLETED == state) {
emitter.onComplete()
}
}
override fun onProgressChanged(
id: Int,
bytesCurrent: Long,
bytesTotal: Long
) {
}
override fun onError(id: Int, exception: Exception) {
emitter.onError(exception)
}
})
}
}
}
I've created a function for returning S3Client which is as follows -
fun s3ClientInitialization(context: Context): AmazonS3 {
val cognitoCachingCredentialsProvider = CognitoCachingCredentialsProvider(
context,
your key,
region
)
return AmazonS3Client(
cognitoCachingCredentialsProvider,
Region.getRegion(Regions.YOUR_REGION)
)
}
Then, Call it as -
val map: Map<File, String> = yourArrayList.map {it to Your_Key_For_Each_File}.toMap()
MultiUploaderS3().uploadMultiple(map, this)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe {
runOnUiThread {
Toast(this#AddActivity, "Uploading completed", Toast.LENGTH_LONG).show()
}
}
I've shared the complete working code, you can change it as you need. You can use the above MultiUploaderS3 class in the same class as well which will obviously make things easier to access the TransferListener.
For Downloading, change the
transferUtility.upload(bucketName,toRemoteKey, aLocalFile) in uploadSingle()
to
transferUtility.download(bucketName, fromRemoteKey, aLocalFile)
and Call it as
val map: Map<File, String> = yourKeysArrayList.map {Your_FILE_For_Each_KEY to it}.toMap()
What this will do is it will pass a map of Local file path to Keys.
I have tried uploading 10 files in a single run many times and it takes around 4-5 seconds to upload all of them, it also depends on your internet connection though. I hope the downloading part will work as well. Ask me if there's something or check my Github for this.
You can create observable for this task
public Observable<AWSFile> upload(List<String> paths) {
List<Observable<AWSFile>> list = new ArrayList<>();
for (String path: paths) {
list.add(upload(path));
}
return Observable.concat(list);
}
public Observable<AWSFile> upload(String filePath) {
if (filePath == null) {
Log.d(TAG, "uploadWithTransferUtility: ");
return Observable.never();
}
return Observable.create(emitter -> {
File file = new File(filePath);
TransferObserver observer = awsUtil.getTransferUtility(context).upload(awsUtil.getAwsConstants().BUCKET_NAME, file);
observer.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
stateChanged(id, state);
emitter.onNext(new AWSFile(id,state,file.getName()));
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
progressChanged(id, bytesCurrent, bytesTotal);
}
#Override
public void onError(int id, Exception ex) {
error(id,ex);
emitter.onError(ex);
}
});
emitter.setCancellable(observer::cleanTransferListener);
});
}
This is just only snippet and you can use it to many files or only one.
EDIT:
AWSFile.java
public class AWSFile {
private int id;
private TransferState newState;
private String filename;
}
You can move the uploading code to a method and pass it the file path.
Then you can loop that method on file paths list. At least that's how I did it. Let me know if you need any further help.
#Lalit-Fauzdar answer and the mentioned reference is the way to go about it. I am sharing a slight upgrade to the implementation
class MultiUploaderS3Client(bucketName:String) {
var bucketName = bucketName
fun uploadMultiple(fileToKeyUploads: MutableMap<String,File>, transferUtility: TransferUtility): Completable? {
return transferUtility(transferUtility)
.flatMapCompletable { transferUtility ->
Observable.fromIterable(fileToKeyUploads.entries)
.flatMapCompletable { entry ->
uploadSingle(
transferUtility,
entry.value,
entry.key
)
}
}
}
private fun transferUtility(transferUtility: TransferUtility): Single<TransferUtility?> {
return Single.create { emitter ->
emitter.onSuccess(
transferUtility
)
}
}
private fun uploadSingle(
transferUtility: TransferUtility?,
aLocalFile: File?,
toRemoteKey: String?
): Completable? {
return Completable.create { emitter ->
transferUtility?.upload(bucketName,toRemoteKey, aLocalFile)
?.setTransferListener(object : TransferListener {
override fun onStateChanged(
id: Int,
state: TransferState
) {
if (TransferState.FAILED == state) {
emitter.onError(Exception("Transfer state was FAILED."))
} else if (TransferState.COMPLETED == state) {
emitter.onComplete()
}
}
override fun onProgressChanged(
id: Int,
bytesCurrent: Long,
bytesTotal: Long
) {
}
override fun onError(id: Int, exception: Exception) {
emitter.onError(exception)
}
})
}
}
}
val multiUploadHashMap = mutableMapOf<String,File>()
MultiUploaderS3Client(AWSConfig.misTicketFilesBucketName).uploadMultiple(multiUploadHashMap, transferUtility)
?.subscribeOn(Schedulers.io())
?.observeOn(Schedulers.io())
?.subscribe {
//Called when all files uploaded
Runnable { callback.onComplete(_Result.Success<String>("uploaded successfully")) }
handler.post(returnCallback)
}
Related
I have a code in my repository which has to call two endpoints. I have used Flowable.zip() but it doesn't seem to return a value. The Call doesn't fail even if there is no network available.
fun fetchRateRemote(): Flowable<ResultWrapper<List<RateModel>>> {
return Flowable.zip<Flowable<CurrenciesDTO>, Flowable<RateDTO>, ResultWrapper<List<RateModel>>>(
{
apiEndpoints.fetchCurrencies(key)
}, {
apiEndpoints.fetchRate(key)
}, { t1, t2 ->
val rateList = mutableListOf<RateModel>()
t2.subscribe { rate->
for((k,v) in rate.quotes ){
val currency = k.removeRange(0,3)
t1.subscribe {cur->
val currencyName = cur.currencies[currency]
if (currencyName != null) {
rateList.add(RateModel("$currencyName ($currency)", v.toString()))
}
}
}
}
ResultWrapper.Success(rateList)
}).subscribeOn(Schedulers.io())
}
I use a wrapper to mimic state and this is what I do in my viewmodel.
private fun fetchRates(){
disposable.add(repository.fetchRateRemote()
.startWith(ResultWrapper.Loading)
.onErrorReturn {
ResultWrapper.Error(it)
}
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSubscriber<ResultWrapper<List<RateModel>>>() {
override fun onComplete() {}
override fun onNext(rate: ResultWrapper<List<RateModel>>) {
rates.postValue(rate)
}
override fun onError(error: Throwable) {
error.printStackTrace()
}
})
)
}
I then observe rate in my activity via LiveData. The wrapper or the observation isn't the issue. It works with other calls, I do not know why the zip call doesn't work. I'm fairly new to RxJava so If I didn't implement something correctly in my repository please help correct me.
Okay! I made a lot of mistakes with the code in the repository above but I managed to fix it. Here's the solution. The Type arguments for the zip method was wrong! I didn't call the BiFunction argument properly too.
fun fetchRateRemote(): Flowable<ResultWrapper<List<RateModel>>> {
return Flowable.zip<CurrenciesDTO, RateDTO, ResultWrapper<List<RateModel>>>(
apiEndpoints.fetchCurrencies(key), apiEndpoints.fetchRate(key), BiFunction { t1, t2 ->
val rateList = mutableListOf<RateModel>()
for((k,v) in t2.quotes ){
val currencyCode = k.removeRange(0,3)
val currencyName = t1.currencies[currencyCode]
if (currencyName != null) {
rateList.add(RateModel("$currencyName ($currencyCode)", v.toString()))
}
}
ResultWrapper.Success(rateList)
}).subscribeOn(Schedulers.io())
}
I am trying to upload multiple image to amazon s3 bucket. The size of the images which i am trying to upload is nearly 300KB each. I am using loop to upload the images. But it's taking more time compared to ios. I am using the below code to upload the images to S3.
val uploadObserver = transferUtility!!.upload(bucketname,
, "img_$timeStamp.jpg", File(fileUri.path!!),
md, CannedAccessControlList.PublicRead)
uploadObserver.setTransferListener(object : TransferListener {
override fun onStateChanged(id: Int, state: TransferState) {
if (TransferState.COMPLETED == state) {
} else if (TransferState.FAILED == state) {
}
}
override fun onProgressChanged(id: Int, bytesCurrent: Long, bytesTotal: Long) {
}
override fun onError(id: Int, ex: Exception) {
}
})
}
Please help me, how to increase the speed of upload.
Using RxJava and RxAndroid you can do multiple async task at a time. zip operate binds all task into one task. Sample code for your case is as following:
fun uploadTask(bucketname: String, timeStamp: Long, md: Any, uriList: List<Uri>) {
val singles = mutableListOf<Single<String>>()
uriList.forEach {
singles.add(upload(bucketname, timeStamp, it, md).subscribeOn(Schedulers.io()))
}
val disposable = Single.zip(singles) {
// If all request emits success response, then it will bind all emitted
// object (in this example String) into an Array and you will get callback here.
// You can change your final response here. In this can I am appending all strings
// to a string.
var finalString = ""
it.forEach { responseString ->
finalString += responseString
}
finalString
}
.subscribe({
// you will get final response here.
}, {
// If any one request failed and emits error all task will be stopped and error
// will be thrown here.
it.printStackTrace()
})
}
And upload() method can be as following:
fun upload(bucketname: String, timeStamp: Long, fileUri: Uri, md: Any): Single<String> {
return Single.create<String> { emitter -> // You can change String to anything you want to emmit
val uploadObserver = transferUtility!!.upload(bucketname,
, "img_$timeStamp.jpg", File(fileUri.path!!),
md, CannedAccessControlList.PublicRead)
uploadObserver.setTransferListener(object : TransferListener {
override fun onStateChanged(id: Int, state: TransferState) {
if (TransferState.COMPLETED == state) {
emitter.onSuccess("COMPLETED") // Emmit your object here
} else if (TransferState.FAILED == state) {
emitter.onSuccess("FAILED")
}
}
override fun onProgressChanged(id: Int, bytesCurrent: Long, bytesTotal: Long) {
}
override fun onError(id: Int, ex: Exception) {
emitter.onError(ex)
}
})
}
}
Just keep in mind, if any upload request is failed, all other task will be stopped. If you want to continue you have to use onErrorResumeNext() operator in that case.
I have to hit 3 API's to update the same screen so for this i think RxJava is the fastest way to do that in parallel. While i was searching for the implementation i came across Observable.zip(...) function as it can perform multiple API hits in parallel.
I am using Retrofit for calling API's and have already created Pojo class with gson annotation.
Sample Pojo classes:
data class ResponseGetFCData(
#SerializedName("End")
val end: String,
#SerializedName("Uni")
val uni: String,
#SerializedName("Y")
val y: Double
)
data class ResponseAK(
#SerializedName("End")
val end: String,
#SerializedName("Manu")
val manu: String,
#SerializedName("Start")
val start: String,
#SerializedName("TY")
val tY: Double
)
Sample Api Interface:
interface Api{
#GET("GetUniPI")
fun getFCdata(#Query("pi") pi: String
, #Query("uni") uni: String): Observable<ResponseGetFCData>
}
Objective : From the response of 2 out of 3 API's I have to compute some mathematical calculation and the third API response will carry data for recycler view. Here i have to compute (y * ty)/100 by taking y from API 1 and ty from API 2 and such similar computations.
MyCode: In activity onCreate(....):
val requests = ArrayList<Observable<*>>()
val backendApi = WinRetrofitHelper.winApiInstance()
requests.add(backendApi.getFCdata("","","",""))
requests.add(backendApi.getAKCountry())
requests.add(backendApi.getRecyclerData("","",""))
Observable
.zip(requests) {
}
)
.subscribe({
Log.e("Exe Summary","******************Success*******************")
}) {
Log.e("Exe Summary",it.stackTrace.toString())
}
So here i am not getting how to fetch the response from these 3 API's and how and where to compute the maths and how will i update the data in recyclerview adapter from 3rd API response.
Please help me to understand this with a better approach.
Or you can give coroutines a try. It has simple syntax easy to understand
fun toDoWorkConcurrent() {
job2 = launch {
try {
val work1 = async { getThingsDone(43) }
val work2 = async { getThingsDoneAgain(123) }
val result = computeResult(work1.await(), work2.await())
withContext(UI) {
tvResult1.text = result.toString()
}
} catch (exception: Exception) {
exception.printStackTrace()
}
}
}
private fun computeResult(await: Int, await1: Int): Int {
return await + await1
}
Edit: Source
Try using below:
Observable.zip(
backendApi.getFCdata("","","",""),
backendApi.getAKCountry(),
backendApi.getRecyclerData("","",""),
Function3<ResponseGetFCData, ResponseAK, List<ResponseMarket>, List<ResponseMarket>> {
fcData, akCountry, recyclerData ->
// Your operation here
return recyclerData
})
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { /* Loading Start */ }
.doOnTerminate { /* Loading End */ }
.subscribe(
{ /* Successfully Synced */ },
{ /* Having error */ }
)
Please try like this
Observable.zip(yourobservalelist, new Function<Object[], Object>() {
#Override
public Object apply(Object[] objects) throws Exception {
return objects;
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(new Consumer<Disposable>() {
#Override
public void accept(Disposable disposable) throws Exception {
}
})
.doOnTerminate(new Action() {
#Override
public void run() throws Exception {
}
})
.subscribe(new Consumer<Object>() {
#Override
public void accept(Object o) throws Exception {
//Do something on successful completion of allrequests
//}
}
},
// Will be triggered if any error during requests will happen
new Consumer<Throwable>() {
#Override
public void accept(Throwable e) throws Exception {
//Do something on error completion of requests
e.printStackTrace();
}
});
}
I am rewriting a java class to kotlin replacing callback with a suspend function. This is my java code:
#IgnoreExtraProperties
public class DeviceType {
public String manufacturer;
public String marketName;
public String model;
public DeviceType(String manufacturer, String marketName, String model) {
this.manufacturer = manufacturer;
this.marketName = marketName;
this.model = model;
}
public DeviceType(){}
public DeviceType(Context context) {
DeviceName.with(context).request(new DeviceName.Callback() {
#Override
public void onFinished(DeviceName.DeviceInfo info, Exception error) {
if (error == null) {
manufacturer = info.manufacturer;
marketName = info.marketName;
model = info.model;
} else
Log.e("DeviceType: ", error.getMessage());
}
});
}
#Override
public String toString() {
if (model == null) {
return "No device type recognized!";
} else {
if (marketName.equals(model))
return manufacturer + " " +marketName;
else
return manufacturer + " " +marketName+ " (" +model+ ")";
}
}
DeviceName class belongs to library AndroidDeviceNames.
Below is my new code in Kotlin:
#IgnoreExtraProperties
data class DeviceType(
var manufacturer: String? = null,
var marketName: String? = null,
var model: String? = null
) {
constructor(context: Context) : this(
context.deviceType()?.manufacturer,
context.deviceType()?.marketName,
context.deviceType()?.model
)
override fun toString(): String {
val stringSuffix =
if (marketName == model)
""
else
" ($model)"
return model?.let { "$manufacturer $marketName$stringSuffix" }
?: "No device type recognized!"
}
}
/**
* return DeviceType "from" UI Context
*/
fun Context.deviceType(): DeviceType? = runBlocking {
withContext(Dispatchers.IO) {
/*
delay(1000L)
DeviceType("Nokia","Banana","R2D2")
^
This works!
*/
DeviceName
.with(this#deviceType)
.awaitWith(this#deviceType)
// ^ that doesn't!
}
}
suspend fun DeviceName.Request.awaitWith(context: Context): DeviceType? = suspendCoroutine { cont ->
DeviceName.with(context).request { info, error ->
if (error == null) {
cont.resume(DeviceType(
info.manufacturer,
info.marketName,
info.model
))
} else
cont.resumeWithException(Throwable(error.message))
.let {
Log.e(
"FirebaseUserData",
"DeviceName.Request.awaitWith(): $error.message"
)
}
}
}
Executing deviceType().toString()) in MainActivity makes infinite looping in runBlocking() function.
The fundamental question is of course "why my implementation of awaitWith() does not work?", but I am also interested, taking first steps in kotlin and coroutines, if I should provide additional solutions for exception handling, as I read the "coroutines may hide exceptions".
And one more question:
Is Dispatcher.IO here OK? DeviceName gets data from Google API json query.
Should I use that dispatcher type also for coroutines related to firebase DB?
First of all, responding to the question's title, the loop is happening because the constructor is calling Context.deviceType() that calls DeviceName.Request.awaitWith that calls the constructor again:
cont.resume(DeviceType(
info.manufacturer,
info.marketName,
info.model
))
The Context.deviceType() return a DeviceType by itself, but you desire to use it to configure each attribute in the initialization. Each DeviceType's attribute initialization instantiate a DeviceType which each attribute instantiate another DeviceType and so on....
Using Dispatcher.IO is OK and even desired when it comes to IO operations, like network, but you are not quite using it.
The runBlocking call blocks the current thread. The way you are using is like that:
## Assume we are on Thread (A)
fun Context.deviceType(): DeviceType? = runBlocking { ## Still in thread (A)
withContext(Dispatchers.IO) { ## Execute in an IO thread pool, but (A) is waiting
DeviceName
.with(this#deviceType)
.awaitWith(this#deviceType)
} ## Returns to thread (A)
} # Resumes Thread (A)
So, although this is kinda running in an IO dispatcher, the calling thread is blocked until the execution is finished, making it synchronous and indifferent.
Actually my goal was to see the deviceType() function output in non-coroutine environment. This function will be used anyway in other suspend functions or coroutine scope.
This is DeviceType class with its public functions without additional constructor:
#IgnoreExtraProperties
data class DeviceType(
var manufacturer: String? = null,
var marketName: String? = null,
var model: String? = null
) {
override fun toString(): String {
val stringSuffix =
if (marketName == model)
""
else
" ($model)"
return model?.let { "$manufacturer $marketName$stringSuffix" }
?: "No device type recognized!"
}
}
fun Context.deviceTypeByRunBlocking(): DeviceType? = runBlocking {
withContext(Dispatchers.IO) {
DeviceName
.with(this#deviceTypeNoSuspend)
.awaitWith(this#deviceTypeNoSuspend)
}
}
suspend fun Context.deviceType(): DeviceType? =
DeviceName
.with(this#deviceType)
.awaitWith(this#deviceType)
private suspend fun DeviceName.Request.awaitWith(context: Context): DeviceType? =
suspendCoroutine { cont ->
DeviceName.with(context).request { info, error ->
if (error == null) {
cont.resume(
DeviceType(
info.manufacturer,
info.marketName,
info.model
)
//.also{Log.d("TAG","Inside awaitWith(): $it")}
)
} else
cont.resumeWithException(Throwable(error.message))
.let {
Log.e(
"TAG",
"DeviceName.Request.awaitWith(): $error.message"
)
}
}
}
Main Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GlobalScope.launch { Log.d("MainActivity", "${this#MainActivity.deviceType()}") }
//^ this works
Log.d("MainActivity", "${this.deviceTypeByRunBlocking()}")
//^ this still does not, loops in joinBlocking(), isComplete = false
}
}
I know that using GlobalScope is not recommended, but for testing it is fine for me.
I'm having a hard time making a call to my api. I'm using Reactivex with kotlin and Flowables. My API returns a list of items if the date I passed by the "If-Modified_since" header is less than the last update.
If there is no update I get as an app return android app a 304 error.
I need to do the following procedure.
1-> I make a call to the api
2-> If the call is successful, save the list in Realm and return to the viewmodel
3-> If the error is 304, I perform a cache search (Realm) of the items
4-> If it is another error, I return the error normally for the ViewModel
Here is the code below, but I'm not sure if it's that way.
override fun getTickets(eventId: String): Flowable<List<Ticket>> {
return factory
.retrieveRemoteDataStore()
.getTickets(eventId)
.map {
saveTickets(it)
it
}.onErrorResumeNext { t: Throwable ->
if (t is HttpException && t.response().code() == 304) {
factory.retrieveCacheDataStore().getTickets(eventId)
} else
//Should return error
}
The question is, what is the best way to do this?
Thank you.
I'm going to assume, that you're using Retrofit. If that's the case, then you could wrap your getTickets call in Single<Response<SomeModel>>. This way, on first map you can check the errorcode, something among the lines of:
...getTickets(id)
.map{ response ->
when {
response.isSuccessful && response.body!=null -> {
saveTickets(it)
it
}
!response.isSuccessful && response.errorCode() == 304 -> {
factory.retrieveCacheDataStore().getTickets(eventId)
}
else -> throw IOException()
}
}
This could of course be made pretty using standard/extension functions but wanted to keep it simple for readability purposes.
Hope this helps!
Most of my comments are my explanations.
data class Ticket(val id:Int) {
companion object {
fun toListFrom(jsonObject: JSONObject): TICKETS {
/**do your parsing of data transformation here */
return emptyList()
}
}
}
typealias TICKETS = List<Ticket>
class ExampleViewModel(): ViewModel() {
private var error: BehaviorSubject<Throwable> = BehaviorSubject.create()
private var tickets: BehaviorSubject<TICKETS> = BehaviorSubject.create()
/**public interfaces that your activity or fragment talk to*/
fun error(): Observable<Throwable> = this.error
fun tickets(): Observable<TICKETS> = this.tickets
fun start() {
fetch("http://api.something.com/v1/tickets/")
.subscribeOn(Schedulers.io())
.onErrorResumeNext { t: Throwable ->
if (t.message == "304") {
get(3)
} else {
this.error.onNext(t)
/** this makes the chain completed gracefuly without executing flatMap or any other operations*/
Observable.empty()
}
}
.flatMap(this::insertToRealm)
.subscribe(this.tickets)
}
private fun insertToRealm(tickets: TICKETS) : Observable<TICKETS> {
/**any logic here is mainly to help you save into Realm**/
/** I think realm has the option to ignore items that are already in the db*/
return Observable.empty()
}
private fun get(id: Int): Observable<TICKETS> {
/**any logic here is mainly to help you fetch from your cache**/
return Observable.empty()
}
private fun fetch(apiRoute: String): Observable<TICKETS> {
/**
* boilerplate code
wether you're using Retrofit or Okhttp, that's the logic you
should try to have
* */
val status: Int = 0
val rawResponse = ""
val error: Throwable? = null
val jsonResponse = JSONObject(rawResponse)
return Observable.defer {
if (status == 200) {
Observable.just(Ticket.toListFrom(jsonResponse))
}
else if (status == 304) {
Observable.error<TICKETS>(Throwable("304"))
}
else {
Observable.error<TICKETS>(error)
}
}
}
override fun onCleared() {
super.onCleared()
this.error = BehaviorSubject.create()
this.tickets = BehaviorSubject.create()
}
}