I try to decode the response returned from a catch, here is what I tried :
try{
...
} catch (err) {
//JsonCodec codec = new JsonCodec(); // doesn't work
//var decoded = codec.decode(err);
...
}
Error: type '_Exception' is not a subtype of type 'String'
print(err) :
Exception: {
"error": {
"code": "invalid_expiry_year"
}
}
I would like to get the value of "code", I tried many things but it doesn't work,
Any idea?
print(err.code);
then I get :
You could get the message property and jsonDecode it.
try {
} catch (e) {
var message = e.message; // not guaranteed to work if the caught exception isn't an _Exception instance
var decoded = jsonDecode(message);
print(decoded['error']['code'])'
}
All of this strikes as a misuse of Exception. Note that you generally don't want to throw Exception(message);. Putting a json encoded message in there is not the best way to communicate details about the exception. Instead, write a custom implementation of Exception and catch the specific type.
class InvalidDataException implements Exception {
final String code;
InvalidDataException(this.code);
}
try {
} on InvalidDataException catch(e) {
print(e.code); // guaranteed to work, we know the type of the exception
}
See the docs for Exception:
Creating instances of Exception directly with new Exception("message") is discouraged, and only included as a temporary measure during development, until the actual exceptions used by a library are done.
See also https://www.dartlang.org/guides/language/effective-dart/usage#avoid-catches-without-on-clauses
I finally figured out a solution :
catch (response) {
var err=response.toString().replaceAll('Exception: ', '');
final json=JSON.jsonDecode(err);
print(json['error']['code']);
}
Related
I need to catch a header datum(response.headers().get("x-rate-limit-remaining") in Response to validate it. The following solution is working, but It is ugly. Instead of this, I want to write ResponseInterceptor to catch the header rate-limit datum.
How can I do it?
Response response = myService.callMethod(request);
// for catching header data
Integer rateLimit = Integer.valueOf(response.headers().get("x-rate-limit-remaining").stream().findFirst().orElse("0"));
// For getting body
Decoder.Default decoder = new Decoder.Default();
CustomResponse customResponse;
try {
String body = (String) decoder.decode(response, String.class);
ObjectMapper mapper = new ObjectMapper();
customResponse = mapper.readValue(body, CustomResponse.class);
} catch (Exception e) {
log.error("Error occured during realty transfer response decoding. Error: {}", e.getMessage());
return;
}
Many thanks.
I'm new in iOS development, so maybe I'm thinking in the wrong way. I coded a view model with a function that calls an API, and everything works fine.
class SearchCityViewModel : ViewModelProtocol {
//OBSERVABLES
var cities = PublishSubject<[City]>()
var networkError = PublishSubject<Void>()
var generalError = PublishSubject<Void>()
init(){
print("Init SearchCityViewModel")
reinit()
}
func reinit(){}
func searchCity(stringToSearch: String){
async {
do {
if stringToSearch.count>=2 {
let cities = try await(api.getCities(cityToSearch: stringToSearch)).payload!
self.cities.onNext(cities)
}
else {
self.cities.onNext([])
}
}
catch {
self.generalError.onNext(Void())
}
}
}
Now I want to handle errors. In the catch block I want to distinguish all the errors I want to handle gracefully, and for the other ones I just want to emit a general error. To do that, firstly I need to know which error is thrown when the situation I want to handle occurs. I usually do this with the debugger. For instance, I disable the internet connection, and i create a breakpoint inside the catch block. The idea is to check which error is thrown when the internet connection is disabled, in order to create a catch block for that kind of error.
Image of the debugger
I'm struggling because with the debugger I only see that is an AFError instance, but it's not telling me nothing more that can help me to catch it.
What is wrong with my workflow? Do I really need to read all the docs every time? For each library I use?
Thank you!
Perhaps you can read the articles and then you will know how to do it better, you can use the framework -oslog instead of using print function.
debugging your logging info
I found the way. What I was missing is casting the error as NSError. In this way, with the debugger is possible to see the domain and the code of the error. In the case of Alamofire, the real error is wrapped, and it's accessible through the underlyingError attribute. Once I had the domain and the code of the error, I wrote the following code:
class SearchCityViewModel : ViewModelProtocol {
//OBSERVABLES
var cities = PublishSubject<[City]>()
var networkError = PublishSubject<Void>()
var generalError = PublishSubject<Void>()
init(){
print("Init SearchCityViewModel")
reinit()
}
func reinit(){}
func searchCity(stringToSearch: String){
async {
do {
if stringToSearch.count>=2 {
let cities = try await(api.getCities(cityToSearch: stringToSearch)).payload!
self.cities.onNext(cities)
}
else {
self.cities.onNext([])
}
}
catch {
if let afError = asAFError, let underlyingError = afError.underlyingError as NSError?, underlyingError.domain == NSURLErrorDomain, underlyingError.code == NSURLErrorNotConnectedToInternet || underlyingError.code == NSURLErrorTimedOut {
self.networkError.onNext(Void())
}
else {
self.generalError.onNext(Void())
}
}
}
}
I am new in swift and found that RSSReader code from internet and getting error in swift2.
class func saveManagedObjectContext(managedObjectContext:NSManagedObjectContext)->Bool{
if managedObjectContext.save(nil){
return true
}else{
return false
}
}
Nil is not compatible with expected argument type '()'
Call can throw, but it is not marked with 'try' and the error is not handled
Can anyone tell me how i can fix it in swift2?
Thanks
Remove nil from the argument list. The method managedObjectContext.save() throws an error in case something goes wrong. The right way of doing it is
do{
try managedObjectContext.save()
return true
}
catch{
return false
}
The save() method does not take any parameters, so using nil as a parameter is both redundant and invalid. Also, when calling the save method, it has the possibility of throwing an error, so you have to program your function to handle that possible error, like so:
func saveManagedObjectContext(managedObjectContext:NSManagedObjectContext)->Bool {
do {
try managedObjectContext.save()
return true
} catch {
return false
}
}
If you have specific errors you want to catch, the syntax is written like so:
catch [errorNameHere] {
[codeToRun]
}
And if you want to catch multiple errors and run corresponding code, you can write this:
catch [errorNameHere] {
[codeToRun]
} catch [anotherErrorNameHere] {
[codeToRun]
} catch {
[defaultCodeToRun] /* if no errors are thrown that were written above, but
there is an error thrown, this default catch block will handle it. If there
is no catch block to handle an error thrown and no default catch block, the
compiler will simply exit without having run anything. */
}
You can read all about error handling in the Swift Documentation here.
How can one write unit tests in Swift that communicate useful information when calling functions that can throw?
What I'd really like to be able to do is something like this:
class TestTestsTests: XCTestCase {
func doFoo() throws -> String {
// A complex operation that might throw in various places
return "foo"
}
func doBar() throws -> String {
// A complex operation that might throw in various places
return "bar"
}
func testExample() throws {
let foo = try doFoo()
let bar = try doBar()
XCTAssertNotEqual(foo, bar)
}
}
Ideally the unit test runner would stop on the line where an unhandled exception occurred, and let the user explore the stack and error message. Unfortunately, adding throws to the test function causes it to be silently skipped over (and there's a UI bug that makes it look as though the test is still being run, and getting the same result as before adding throws).
Of course it is also possible to do this:
func testExample() {
let foo = try! doFoo()
let bar = try! doBar()
XCTAssertNotEqual(foo, bar)
}
But now a failure doesn't really provide the context we need. Add a throw to doFoo, and we get a message like fatal error: 'try!' expression unexpectedly raised an error: TestTestsTests.Error(): file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-700.1.101.15/src/swift/stdlib/public/core/ErrorType.swift, line 50, which only gives us the line number within testExample, and not within doFoo where the error occurred. This also seems to get the debugger 'stuck' (clicking continue just returns us to the same line with the same error message) on that line, regardless of whether breakpoints are enabled, and prevents other tests from running.
So maybe we could try something like this?
func testExample() {
do {
let foo = try doFoo()
let bar = try doBar()
XCTAssertNotEqual(foo, bar)
} catch {
XCTFail("\(error)")
}
}
This runs as expected, however we can't determine which of doFoo or doBar threw the error, and also no line number information. At least we can get the error message and not prevent other tests from running.
I could go on, but the short of it is that I can't find a way to simultaneously not break the unit test running (like with try!), figure out which function threw the error, and get the error information -- Unless I do something ridiculous like:
func testExample() {
var foo: String? = nil
var bar: String? = nil
do {
foo = try doFoo()
} catch {
XCTFail("\(error)")
return
}
do {
bar = try doBar()
} catch {
XCTFail("\(error)")
return
}
if let foo = foo, bar = bar {
XCTAssertNotEqual(foo, bar)
}
}
And I still don't get to find out where in doFoo or doBar the error occurred.
Is this just the sad state of unit testing in Swift, or am I missing something?
This runs as expected, however we can't determine which of doFoo or doBar threw the error, and also no line number information.
Maybe this is just an old problem or I'm not understanding your problem, or maybe you're just kind of making a statement rather than asking a question. But with respect to the quoted statement above, you can add any information you like to the messages in XCTest Assertions.
Swift also provides the following literals:
#file String The name of the file in which it appears.
#line Int The line number on which it appears.
#column Int The column number in which it begins.
#function String The name of the declaration in which it appears.
func testExample() {
var foo: String? = nil
var bar: String? = nil
do {
foo = try doFoo()
} catch {
XCTFail("try doFoo() failed on line: \(#line) in file: \(#file) with error: \(error)")
return
}
do {
bar = try doBar()
} catch {
XCTFail("try doBar() failed on line: \(#line) in file: \(#file) with error: \(error)")
return
}
if let foo = foo, bar = bar {
XCTAssertNotEqual(foo, bar)
}
}
If you really want to go crazy with it you can add error handling to your doBar() method and that error can contain any internal information you'd like.
In fact... by implementing your own errors in your methods you might not even need to separate the methods into two blocks in your tests, just printing the error should be enough. You can put any information you like in the error message.
Anyway, I think this is an outdated issue, you can get all the information you need from the test logs - they list out all the methods that failed and even have little arrows that let you jump right to the test that failed. They then highlight the specific assertion that failed... from there it's quite easy to tell what is happening in most cases. Worst case scenario you have to set a breakpoint or two and run the test again.
You can do your own errors, using Error or LocalizedError protocols
enum Errors: Error, CustomStringConvertible {
case foo_param_is_null
case bar_param_is_null(paramIndex: Int)
var description: String {
switch self {
case .foo_param_is_null:
return "Param is null in foo"
case .bar_param_is_null(let paramIndex):
return "Param at index \(paramIndex) is null in bar"
}
}
}
func foo(_ param: Int) throws {
guard param != 0 else {
throw Errors.foo_param_is_null
}
print("foo = \(param)")
}
func bar(_ params: [Int]) throws {
if let index = params.firstIndex(where: {$0 == 0}) {
throw Errors.bar_param_is_null(paramIndex: index)
}
print("bar = \(params)")
}
do {
try foo(1)
try foo(0)
} catch {
print("\(error)")
}
do {
try bar([1,2,3])
try bar([1,0,3])
} catch {
print("\(error)")
}
Result:
foo = 1
Param is null in foo
bar = [1, 2, 3]
Param at index 1 is null in bar
And if you need even more information, you can use structures to define errors and error domains. Something like :
struct FooBarError: Error, CustomStringConvertible {
var string: String
var context: Any?
static func fooError() {
FooBarError(string: "Foo Error")
}
static func barError(context: BarErrorContext) { FooBarError(string: "Bar Error", context: context)
}
var description: String {
if let cox = context as? BarErrorContext {
return "\(string) - paramIndex: \(ctx.paramIndex) - \(ctx.insidiousReason)"
}
return string
}
}
Note:
As #ibrust proposed, you can pass #function, #line and other special parameters to your errors initialisers to provide this information
do {
try foo()
} catch {
throw(BarFooError.foo(line: #line))
}
You can also propagate the original error
do {
try bar()
} catch {
throw(BarFooError.bar(exception: error))
}
Edited:
Finally , you can also use print(Thread.callStackSymbols) in your error description, but at this point, there is a risk of confusion between debugging and testing. Just a personal thought.
I have a function assignName(name:) which throws an error. When that function is called from a do block with multiple catch, It shows the error as:
Errors thrown from here are not handled because the enclosing catch is not exhaustive
My Code is:
enum PersonError: ErrorType {
case IsNotAPerson
case IsNotAGoodPerson
case IsNotAValidPerson
}
func assignName(name: String?) throws {
guard name != nil else {
throw PersonError.IsNotAPerson
}
personName = name
}
func catchingSpecificError() {
do {
try assignName(nil) // Compiler Error displays at this line.
}catch PersonError.IsNotAPerson {
print("Propagated error is caught in catch on case .NotAPerson")
}
}
Thanks in Advance!
You need to include a default catch block (much like when using a switch case) to make your error handling exhaustive; in case the error thrown is not one of the ones you specify.
func catchingSpecificError() {
do {
try assignName(nil) // Compiler Error displays at this line.
}catch PersonError.IsNotAPerson {
print("Propagated error is caught in catch on case .NotAPerson")
}catch {
print("default..")
}
}
Slightly off-topic, but I assume personName = name refers to a class property personName that we cannot see in your example above.
With the default catch block added, you mention in the comments below that function catchingSpecificError() does not cast the error you expect; or rather, the default catch block catches your error.
Now, since I don't know the context of your code, I cannot infer what is actually going wrong in your case. I'll post a working example for your below where---in the context of this question---the throw and catch work as expected. Note however that your use of the guard block is somewhat out of by convention. Usually you make use of guard just like if let blocks, i.e., guard let name = name else { .., which will enter the guard block if name contains nil.
Anyway, consider the following fully working example:
enum PersonError: ErrorType {
case IsNotAPerson
case IsNotAGoodPerson
case IsNotAValidPerson
}
class Person {
var personName : String? = ""
func assignName(name: String?) throws {
guard name != nil else {
throw PersonError.IsNotAPerson
}
personName = name
}
func catchingSpecificError() {
do {
try assignName(nil)
}catch PersonError.IsNotAPerson {
print("Propagated error is caught in catch on case .NotAPerson")
}catch {
print("default..")
}
}
}
var myPerson = Person()
var myName : String? = nil
myPerson.catchingSpecificError()
/* Prints: "Propagated error is caught in catch on case .NotAPerson" */
As expected, we catch PersonError.IsNotAPerson thrown by function assignName. Hopefully you can make use of this example your get your own code (the parts that you haven't shown us in your question) working.