I am expecting the "throw RuntimeException" in ServerHandler to proceed to the catch block in registerAccount when error code 403 pops out from the server, but I am unable to catch the error... below is my code:
LoginRepo.kt:
private fun registerAccount(context: Context, jsonObject: JSONObject, username: String, password: String): Result<LoggedInUser> {
try {
ServerHandler.getInstance(context).makeHttpRequest(
"http://www.mywebpage.com/index.php",
Request.Method.POST,
jsonObject
)
return Result.Success(LoggedInUser(java.util.UUID.randomUUID().toString(), username))
} catch (e: Throwable) {
return Result.Error(IOException("Error registering account: ", e))
}
}
ServerHandler.kt:
#Throws(RuntimeException::class)
fun makeHttpRequest(url: String, method: Int, jsonBody: JSONObject? = null):Any {
// Instantiate the RequestQueue.
Log.d("makeHttpRequest","Sending request!!!!")
var stringRequest = when (method) {
Request.Method.POST ->
object : StringRequest(method, url,
Response.Listener<String> { response ->
Log.d("requestPOST", response)
}, Response.ErrorListener { error ->
#Throws(RuntimeException::class)
when(error.networkResponse.statusCode) {
403 -> {
throw RuntimeException("Username is taken.") //<--RuntimeException
}
else-> {
Log.d("UNHANDLED ERROR:", error.toString())
}
}})
}
}
Error:
java.lang.RuntimeException: Username is taken.
at com.example.inspire.data.ServerHandler$makeHttpRequest$stringRequest$5.onErrorResponse(ServerHandler.kt:75)
I do not know all the details, but it seems that the call ServerHandler.getInstance(context).makeHttpRequest( must be returning instantly (even before any HTTP requests are made).
Just put a logging statement after the call but before the return too see if that is really the case. The HTTP request is probably made later at some point (possibly in another thread), when the registerAccount function has long but exited (and so is the try/catch block defined within).
Due to the asynchronous feature in Volley callbacks, the Android Studio debugger has helped to confirm that registerAccount() has returned the result before makeHttpRequest() has done its job to communicate with the PHP server.
As registerAccount() has returned, throwing RuntimeException("Username is taken.") from makeHttpRequest() has no one left to catch its exceptions, which causes the exception unable to be caught.
In this case, catching exceptions sounds impossible, so I would just rather make a
Toast.makeText(
_context,
"Username already taken!",
Toast.LENGTH_LONG
).show()
instead of throwing exceptions...
Related
In Vapor 4, I'm processing a post request by calling a request on a 3rd party API and returning a value based on the result I get back. The following code results in the error: "Invalid conversion from throwing function ... to non-throwing function"
app.post("activate") { req -> EventLoopFuture<ActivationRequestResponse> in
return req.client.post("https://api.example.com/activation", headers: HTTPHeaders(), beforeSend: { (req) in
try req.content.encode(RequestBody(value: someValue), as: .json)
})
.map { (response) -> ActivationRequestResponse in
let response = try response.content.decode(ResponseModel.self)
return ActivationRequestResponse(success: true, message: "success")
}
}
I can't seem to use try in my chained map() after getting the API result. The above code will work if I add a ! to the try in let response = try response.content.decode(ResponseModel.self) inside the map, but ideally I want to catch this error. The first try used when creating the response body seems to be implicitly passed back up the chain, but not the second.
What am I doing wrong? How do I catch the error when decoding the response content? Why is the first try caught but not the second?
The property of map is that it will just transform a value on the “success path”. Your transformation may however fail which means that you presumably want the future to fail too.
Whenever you want to transform a value with a function that either succeeds or fails you need to use one of the flatMap* functions.
In your case, try replacing map with flatMapThrowing and then it should work.
To expand on Johannes Weiss' answer, to have a throwing closure that returns a future, you need something like:
future.flatMap {
do {
return try liveDangerously()
} catch {
future.eventLoop.makeFailedFuture(error)
}
}
After doing this too many times, I decided to roll my own (though the name is a bit dubious):
extension EventLoopFuture {
#inlinable
public func flatterMapThrowing<NewValue>(file: StaticString = #file,
line: UInt = #line,
_ callback: #escaping (Value) throws -> EventLoopFuture<NewValue>) -> EventLoopFuture<NewValue> {
return self.flatMap(file: file, line: line) { (value: Value) -> EventLoopFuture<NewValue> in
do {
return try callback(value)
} catch {
return self.eventLoop.makeFailedFuture(error)
}
}
}
}
That way you can just write:
future.flatterMapThrowing {
return try liveDangerously()
}
I create a set of promises which relies on the results from a function that may throw an error. I can get this to work as shown in the code below, but I don't like the double catch blocks. I'd like to use the a single promiseKit catch block. Anyone have a better solution that works?
do {
let accounts = try Account.getAccounts()
let mailboxPromises = accounts.map { self.fetchMailboxes($0) }
when(fulfilled: mailboxPromises).map { _ in
self.updateBadgeCount()
}
.catch { (error) in
}
} catch {
}
Maybe wrap Account.getAccounts() in a promise which you can then use in your promise chain?
func getAccounts() -> Promise<[Account]> {
return Promise {
do {
let accounts = try Account.getAccounts()
$0.fulfill(accounts)
} catch {
$0.reject(error)
}
}
}
UPDATE:
Below info is from the documentation at https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md so you should be able to use this pattern instead of your do/catch block.
Since promises handle thrown errors, you don't have to wrap calls to throwing functions in a do block unless you really want to handle the errors locally:
foo().then { baz in
bar(baz)
}.then { result in
try doOtherThing()
}.catch { error in
// if doOtherThing() throws, we end up here
}
How would I catch all 404 errors and redirect to /app/index.html (I'm trying to satisfy Angular's need to forward all not found resources to index.html) ? I thought implementing my own middleware would work, but not sure I'm doing it right
public final class ForwardToAngularMiddleware: Middleware {
public func respond(to req: Request, chainingTo next: Responder) throws -> EventLoopFuture<Response> {
do {
let response: Future<Response> = try next.respond(to: req)
return response
} catch let error as AbortError {
if error.status == .notFound && req.http.url.path.starts(with: "/app") {
return Future.map(on: req) { req.redirect(to: "/index.html") }
}
throw error
}
}
}
My program never hits the catch block no matter what URL I send. I am configuring my middleware like this:
middlewares.use(FileMiddleware.self)
middlewares.use(ForwardToAngularMiddleware())
middlewares.use(ErrorMiddleware.self)
middlewares.use(SessionsMiddleware.self)
services.register(middlewares)
You may be hitting a couple of issues here. First, the abort error could be being thrown in a future, in which case you need to add a catchMap to the next.respond(to:) call to catch that case.
It also may not throw (though this is unlikely), so you can try unwrapping the response and checking the status code.
Have you put a breakpoint in to see if it ever hits it etc?
I am working on a piece of code that is going to fetch an array of NSManagedObjects from CoreData. When using a do catch statement in my code it doesn't seem right to do it this way, but it is the simplest way I can write this line of code.
In any other scenario when you use the return statement you are jumping out of the current function you are in. And you can be assured that no other code in your function will execute past the return statement. I am wondering if the same applies to Swift's do catch paradigm.
class func getAll() -> [MMNotification] {
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<MMNotification>(entityName: "MMNotification")
do {
return try context.fetch(fetchRequest)
}
catch {
// Will this 'catch' if the try fails,
// even if we said we are 'return'ing right before the 'try'?
return []
}
}
Here I am fetching a list of notifications stored in CoreData. In the do block you can see the line of code in question.
QUESTION
Will the catch block execute if the try fails after already stating that the function should return?
What you have should work as expected. Basically what happens is if a throw occurs at any time within a do, the catch is called and any code after the throw will not be executed.
Yes, the catch block will execute if the try in return try fails. The return will not happen.
Here's a little code to prove it to yourself. Paste it into a new playground to try it out.
import UIKit
let shouldFail = true
enum DemoError:Error {
case shouldFail
}
func failableGetter() throws -> String {
if shouldFail { throw DemoError.shouldFail }
return "Succeeded"
}
func fetchInfo() -> String {
do {
return try failableGetter()
} catch {
return "Failed"
}
}
print(fetchInfo()) // "Failed" or "Succeeded" depending on shouldFail
When shouldFail is true, the failableGetter() throws an error and the do-catch in fetchInfo() skips to the catch section before returning.
When shouldFail is false, the failableGetter() doesn't fail and fetchInfo() returns the result.
Adding to this answer. Scope matters a bit here. Code inside the do block code after a throw will NOT be executed. However, code further down outside of the scope of the do block will be executed. I made a simple playground you can run to see for yourself.
import Foundation
let error = NSError(domain: "", code: 123, userInfo: [NSLocalizedDescriptionKey: "My error"])
func functionThatAlwaysThrows() throws {
throw(error)
}
func myFunction() {
do {
try functionThatAlwaysThrows()
// This will never print
print("Continuing after throw inside do scope")
} catch let err {
print("Caught Error: \(err.localizedDescription)")
}
// This will always print
print("Continuing after throw outside do scope")
}
Output:
Caught Error: My error
Continuing after throw outside do scope
If you want more information on Error handling you can take a look at the docs
I'm quite new and I'm wondering how to catch error from requests which are zipped (see snipped above) in one place. In current implementation I have error handling in two places, but my goal is to have it in one place. My requests are zipped because if one of this req gets failed whole sequence will fail so in result I want to have one error handling place in code for both request.
let firstReq = self.sendReq() // returns Observable<Bool>
.catchError {
error in
return self.just(true)
}
let secondReq = self.sendReqTwo() // returns Observable<Bool>
.catchError {
error in
return self.just(true)
}
goBttnOutlet.rx_tap
.subscribeNext {
Observable.zip(firstReqRes, secondReqRes) { (firstRes, secondRes) -> Bool in
return firstRes && secondRes
}.subscribeNext { summaryRes in
print("🎿 \(summaryRes)")
}.addDisposableTo(self.rx_disposableBag)
}.addDisposableTo(rx_disposableBag)
..maybe some link with example code with handling error in common place will be great for me.
Thanks a lot.
zip returns a new Observable<T>, so you can simply move the catchError operator application to what zip returns.
let firstReq = self.sendReq()
let secondReq = self.sendReqTwo()
let zippedReq = Observable.zip(firstReq, secondReq)
.catchErrorJustReturn { _ in true }
goBttnOutlet.rx_tap
.subscribeNext {
zippedReq.subscribeNext { summaryRes in
print("🎿 \(summaryRes)")
}.addDisposableTo(self.rx_disposableBag)
}.addDisposableTo(rx_disposableBag)
On a side note, you could improve the chain after goBttnOutlet to the following
goBttnOutlet.rx_tap.flatMap { zippedReq }
.subscribeNext { summaryRes in
print("🎿 \(summaryRes)")
}.addDisposableTo(rx_disposableBag)
See flatMap documentation for details.