I am using Moya with RxSwift and I am trying to set the request timeout for the network call (API Calls) using below code as suggested :
which is including the custom Alamofire Manager when declaring your Provider
lazy var provider: RxMoyaProvider<CAPProviderAPI> = {
return RxMoyaProvider<CAPProviderAPI>(endpointClosure: Utility.getEndPointClosure(forProviderType: .authorized), manager: DefaultAlamofireManager.sharedManager, plugins: [NetworkActivityPlugin(networkActivityClosure: networkActivityClosure)])
}()
but I am getting an error saying : Use of unresolved identifier 'networkActivityClosure'
I would like to share with you the way I did it. It might not answer your question, but it shows the way to achieve the desired behavior using RxSwift operators.
I have some function which accepts timeout parameter and makes a request:
func request(timeout: TimeInterval, ...other params) -> Observable<...>
Inside this function I transform timeout to Observable this way:
func observableTimeout(timeout: TimeInterval, ...other params) -> Observable<...> {
return Observable<Int>
.timer(timeout, period: ..., scheduler: ...)
.take(1)
.map(to: ...) // map to timeout error
}
If the timeout takes place - I need to cancel the request. I have made some flatMapLatest analogue which also accepts a cancel signal:
public extension Observable {
public func flatMapLatest<T>(cancel: Observable<T>, factory: #escaping (E) throws -> Observable<T>) -> Observable<T> {
let observableNormal = self
.flatMap({ try factory($0) })
return Observable<Observable<T>>
.of(cancel, observableNormal)
.merge()
.take(1)
}
}
As a result, the request function will work this way:
func request(timeout: TimeInterval, ...other params) -> Observable<...> {
let cancel = observableTimeout(timeout: timeout, ...)
let factory = ...// your observable factory which makes a request using Moya
return Observable
.just((), scheduler: ...)
.flatMapLatest(cancel: cancel, factory: factory)
}
Related
I have been trying to create a decorator for a lambda function in Kotlin. The requirements for the decorator are following:
The decorator must be able to REPEAT the process passed in the form of lambda function
The decorator must be able to TIMEOUT after a certain time which will be passed along in the decorator.
This is what I have been able to come up so far:
inline fun <T, R> testDecorator(input: T,
timesToRepeat: Int,
timeout: Long,
crossinline work: (T) -> R): R {
repeat(timesToRepeat - 1) {
try {
return runBlocking {
withTimeout(timeout) {
work(input)
}
} catch (ex: Exception) {
// Deal with exception
}
}
return work(input)
}
Right now, the work function will only be making a http request but later on I plan to add some more work.
Sample work:
fun sampleWork() {
val client = HttpClient.newBuilder().build();
val request = HttpRequest.newBuilder() .uri(URI.create("some url")) .build();
val response = client.send(request, HttpResponse.BodyHandlers.ofString());
// Parse respone
return parsedResponse;
}
The problem is that the timeout is not working as expected and the function only ends after all the processes in the lambda have ended. Can someone tell me what am I missing here?
I feel like I'm missing some simple fundamental nuance but for some reason Mono.delay() is not working for me. I have Mono that makes http request that could be throttled. I need to wait provided time to retry. Here is how it looks now
internal fun <T> Mono<T>.retryAfter(maxRetries: Int, uri: URI): Mono<T> {
// `this` is instance of Mono<T>
return this.retryWhen(Retry.from {
it.map { rs ->
val ex = rs.failure()
if (ex is RetryAfterException && rs.totalRetries() < maxRetries) {
println("*** API throttling on call to $uri. Will wait for ${ex.delay}. Retry count: ${rs.totalRetries()}. ms ${System.currentTimeMillis()}")
Mono.delay(ex.delay.plusMillis(500), Schedulers.parallel()).then(Mono.defer({
println(" Waited. ${System.currentTimeMillis()}")
this
}))
} else
Mono.error(rs.failure())
}
})
}
you are using map, which results in a Flux<Mono<?>> being returned to the operator for retry control. from the operator's perspective (Flux<?>), any onNext means "you should retry". whether it is onNext("example") or onNext(Mono.error(err)) doesn't matter.
instead of using map, use concatMap. The Mono that you produce in your function will correctly result in a Flux<?> in which the "delay" branch of the if produces (delayed) onNext while the other branch produces onError.
You can use built in retry builders
I suggest you to use Retry.fixedDelay() which allow you to define a max retry and a delay between each try. When max retry is reached you'll get a Mono.error()
internal fun <T> Mono<T>.retryAfter(maxRetries: Long, uri: URI): Mono<T> {
// `this` is instance of Mono<T>
return this.retryWhen(Retry.fixedDelay(maxRetries, Duration.ofMillis(500))
.doBeforeRetry { rs: Retry.RetrySignal? -> println("Will retry " + rs?.totalRetries()) }
.doAfterRetry{ rs: Retry.RetrySignal? -> println("Has retried " + rs?.totalRetries()) }
.onRetryExhaustedThrow { _, rs -> rs.failure()})
}
I'm making a call to the API and the response body is assigned to an object inside Retrofit's enqueue(), the problem is that enqueue finishes too quickly for the value to be assigned before the return statement of the function body is called.
Previously, I was using MutableLiveData before and it took care of that because it's always observing the data and when it changes it assigns it with no problem but now I don't want to use any MutableLiveData or Observables because I'm trying to prepare the data before any UI is actually drawn on the screen.
fun getResponse(
weatherLocationCoordinates: WeatherLocation
): RequestResponse {
weatherApiService.getCurrentWeather(
weatherLocationCoordinates.latitude,
weatherLocationCoordinates.longitude
).enqueue(object : Callback<WeatherResponse> {
override fun onResponse(
call: Call<WeatherResponse>,
response: Response<WeatherResponse>
) {
if (response.isSuccessful) {
// This where I do the assigning
requestResponse = RequestResponse(response.body(), true)
}
}
override fun onFailure(call: Call<WeatherResponse>, t: Throwable) {
requestResponse = RequestResponse(null, false)
}
})
// When this is called, enqueue is still not finished
// therefore I get the wrong value, I get the previously set initialization value of the obj.
return requestResponse
}
Should I be using Callbacks or something else? I'm not sure on how to implement the callback.
Following up on the comments here's an approach with callbacks:
Let's suppose we change the method signature to:
fun getResponse(
weatherLocationCoordinates: WeatherLocation,
onSuccess: (WeatherResponse) -> Unit = {},
onError: (Throwable) -> Unit = {}
) {
weatherApiService.getCurrentWeather(
weatherLocationCoordinates.latitude,
weatherLocationCoordinates.longitude
).enqueue(object : Callback<WeatherResponse> {
override fun onResponse(
call: Call<WeatherResponse>,
response: Response<WeatherResponse>
) {
if (response.isSuccessful) {
onSuccess(response.body())
} else {
onError(CustomHttpExceptionWithErrorDescription(response))
}
}
override fun onFailure(call: Call<WeatherResponse>, t: Throwable) {
onError(t)
}
})
}
CustomHttpExceptionWithErrorDescription will have to be something you code that can simply parse the error gotten from the server. Anything that is not 2XX status code
This method accepts 2 extra parameters - one gets called upon success the other on error. The idea is to call it like:
getResponse(
weatherLocationCoordinates,
onSuccess = {
// do something with response
},
onError = {
// do something with the error
}
)
Because they have default parameters you actually don't need to specify both callbacks. Just the one you are interested in. Examples:
// react only to successes
getResponse(
weatherLocationCoordinates,
onSuccess = {
// do something with response
}
)
// react only to errors
getResponse(weatherLocationCoordinates) {
// do something with the error
}
// just call the network calls and don't care about success or error
getResponse(weatherLocationCoordinates)
I create a mock of a class with mockk.
On this mock I now call a method that gets a lambda as a parameter.
This lambda serves as a callback to deliver state changes of the callback to the caller of the method.
class ObjectToMock() {
fun methodToCall(someValue: String?, observer: (State) -> Unit) {
...
}
}
How do I configure the mock to call the passed lambda?
You can use answers:
val otm: ObjectToMock = mockk()
every { otm.methodToCall(any(), any())} answers {
secondArg<(String) -> Unit>().invoke("anything")
}
otm.methodToCall("bla"){
println("invoked with $it") //invoked with anything
}
Within the answers scope you can access firstArg, secondArg etc and get it in the expected type by providing it as a generic argument. Note that I explicitly used invoke here to make it more readable, it may also be omitted.
I had to look for a bit more example for the callback and found some example in Kotlin Test with Mockk. In my case, it's a bit more specific.
I wanted to check and mock the onFailure and onSuccess case of a a custom callback implementation MyCustomCallback implementing the ListenableFutureCallback.
The code would look like that for my ExampleProducer class that would have a send function:
fun send(data: String) {
val responseFuture = kafkaTemplate.send(topic, data)
responseFuture.addCallback(MyCustomCallback())
}
So here who would the test go:
#Test
fun onFailureTest() {
kafkaTemplate: KafkaTemplate<String, String> = mockk()
val captureCallback = slot<ListenableFutureCallback<SendResult<String, String>>>()
every { callback.addCallback(capture(captureCallback)) } answers {
captureCallback.captured.onFailure(Throwable())
}
every { kafkaTemplate.send(any()) } returns callback
val prod: ExampleProducer = ExampleProducer()
prod.send("test")
// Then you can verify behaviour or check your captureCallback.captured
verify { kafkaTemplate.send(any()) }
assertNotNull(captureCallback.captured)
}
Maybe not exactly what you ask about, but you can use the funciton type for the mock:
val observerMock = mockk<(State) -> Unit>()
I am studying Kotlin programming for Android and I am trying to understand this code (that works) deeply.
It comes from the Volley library for network request:
//Network stuff
// Request a string response from the provided URL.
val jsonObjectRequest = object : JsonObjectRequest(Method.POST, http, ob,
Response.Listener<JSONObject> { response ->
// Display the first 500 characters of the response string.
Log.d("Debug","Response is: ${response.toString()} ")
},
Response.ErrorListener { error ->
Log.d("Debug","That didn't work! Code: ${error.message}")
})
{
#Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val headers = HashMap<String, String>()
headers.put("Content-Type", "application/json")
headers.put("Accept", "application/json")
return headers
}
}
My question is about the first block, right inside the constructor of the JsonObjectRequest object. I know the object construct, lambdas, classes, and interfaces but there is one little thing that I don't get here. Moreover, I have already seen this thread Pass interface as parameter in Kotlin.
My question is: what is happening in the fourth parameter used to construct the JsonObjectRequest? From what I see, there is a lambda overriding some function related to Response.Listener<JSONObject> but I don't find any reference to this syntax.
To conclude, the objectRequest has the former constructor:
public JsonObjectRequest(int method, String url, JSONObject jsonRequest,
Listener<JSONObject> listener, ErrorListener errorListener) {
super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
errorListener);
}
and the listener has the following section:
public class Response<T> {
/** Callback interface for delivering parsed responses. */
public interface Listener<T> {
/** Called when a response is received. */
void onResponse(T response);
}
/** Callback interface for delivering error responses. */
public interface ErrorListener {
/**
* Callback method that an error has been occurred with the
* provided error code and optional user-readable message.
*/
void onErrorResponse(VolleyError error);
}
Reading this I get that with this syntax we are implementing the Listener interface but I don't get why we use a lambda, since in the Listener there is no reference to it, and in particular what does this mean:
Response.Listener<JSONObject> { response ->
// Display the first 500 characters of the response string.
Log.d("Debug","Response is: ${response.toString()} ")
}
Anyone willing to explain this or pointing to some references related to this syntax?
This is a SAM-conversion. Because Response.Listener<JSONObject> is a SAM-interface (has a single method without a default implementation) and defined in Java, you can write
Response.Listener<JSONObject> /* lambda */
and the lambda is used as the implementation of the method. I.e. it's equivalent to
object : Response.Listener<JSONObject> {
override fun onResponse(response: JSONObject) {
Log.d("Debug","Response is: ${response.toString()} ")
}
}