I'm trying to understand why I'm unable to catch the errors thrown by NSJSONSerialization.
I expect the NSInvalidArgumentException exception to be raised and caught, but instead the app crashes.
This is occurring in both Swift 3 and Swift 2.3 using Xcode 8.
Swift 3:
do {
_ = try JSONSerialization.data(withJSONObject: ["bad input" : NSDate()])
}
catch {
print("this does not print")
}
Swift 2.3:
do {
_ = try NSJSONSerialization.dataWithJSONObject(["bad input" : NSDate()], options: NSJSONWritingOptions())
}
catch {
print("this does not print")
}
This code is put in applicationDidFinishLaunching inside a blank Xcode project. Tested on both simulator and device.
Full exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (__NSDate)'
Any ideas why the catch block is not catching this particular error?
From the documentation for JSONSerialization data(withJSONObject:options:):
If obj will not produce valid JSON, an exception is thrown. This exception is thrown prior to parsing and represents a programming error, not an internal error. You should check whether the input will produce valid JSON before calling this method by using isValidJSONObject(_:).
What this means is that you can't catch the exception caused by invalid data. Only "internal errors" (whatever that actually means) can be caught in the catch block.
To avoid a possible NSInvalidArgumentException you need to use isValidJSONObject.
Your code then becomes:
do {
let obj = ["bad input" : NSDate()]
if JSONSerialization.isValidJSONObject(obj) {
_ = try JSONSerialization.data(withJSONObject: obj)
} else {
// not valid - do something appropriate
}
}
catch {
print("Some vague internal error: \(error)")
}
Related
I am trying to start a one on one call using Azure CommunicationCalling sdk. I am getting a crash inside one of the sdk functions as shown in screenshot for backtrace below.
Code for start call:-
func startCall(callee: String, successHandler: #escaping (Call?) -> Void ) {
// start call logic
debugPrint(callee)
let callees:[CommunicationIdentifier] = [createCommunicationIdentifier(fromRawId: callee)]
debugPrint(callees)
self.callAgent?.startCall(participants: callees, options: StartCallOptions()) { (call, error) in
if let error = error {
debugPrint(error.localizedDescription)
successHandler(nil)
} else {
self.call = call
debugPrint("call placed successfully")
successHandler(call)
}
}
}
I have verified that call agent and communication identifier arr obj passed is not nil.
Crash log:-
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
terminating with uncaught exception of type NSException
BackTrace for crash:-
I have referred to following documentation:-
https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/ui-library/get-started-composites?tabs=kotlin&pivots=platform-ios
This crash doesnt happen anymore with same sdk version. Also the beta sdk version (v2.3.0-beta.2) is working fine too.
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']);
}
I used this method very much in Swift 1.2: NSURLConnection.sendSynchronousRequest(:_:_:_) but this is apparently deprecated in iOS9. It still works however but now it uses the new Swift 2.0 Error Handling and I don't know how I will get the error message if it fails, ex. if time runs out.
I know I have to put it into a do-catch and then say try before the metho but I dont know how to catch the error message.
do {
let data = try NSURLConnection.sendSynchronousRequest(request, returningResponse: nil)
return data
}
catch _ {
return nil
}
Before I used NSError and then its description property, but now I have no clue.
Use automatic error variable, and you can cast it to NSError if you wish:
catch {
let nsError = error as NSError
print(nsError.localizedDescription)
}
You can now throw any object inheriting ErrorType, and provide custom handling in the catch sentence. You can also cast the error to NSError to access localizedDescription for handling third party errors.
Casting an enum ErrorType will produce a NSError with domain equal to the enum name, code equal to the enum value and an auto-generated localizedDescription with the following format:
The operation couldn’t be completed. (DOMAIN error CODE.)
For example, the following code:
enum AwfulError: ErrorType {
case Bad
case Worse
case Terrible
}
func throwingFunction() throws {
throw AwfulError.Worse
}
do {
try throwingFunction()
}
catch AwfulError.Bad {
print("Bad error")
}
catch let error as NSError {
print(error.localizedDescription)
}
Will print
The operation couldn’t be completed. (AwfulError error 1.)
Despite the question title specifying Swift 2, this answer is for Swift 3.
As #redent84 points out, since Swift 2 an Error object may be a home-made one. Here's a method I wrote to analyze and print the default error object available in a "catch" statement that doesn't specify any specific error type:
// Method to print an unknown Error type object to the system output.
static func printCaughtError(_ unknownError : Error) {
let objectDescription = String(describing: unknownError)
let localizedDescription = unknownError.localizedDescription
if localizedDescription != "" {
if localizedDescription.contains(objectDescription) {
print(localizedDescription)
return
}
if !objectDescription.contains(localizedDescription) {
print(objectDescription + ": " + localizedDescription)
return
}
}
print(objectDescription)
}
Then you can call it like this:
catch {
printCaughtError(error)
}
How to get the error message that is inside userInfo:
let errorMessage = (error as NSError).userInfo["message"] as? String
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.
JSONSerialization.data(withJSONObject:options:) (aka dataWithJSONObject in Swift 2) is declared throws. However, passing invalid objects causes a crash, not a catchable error:
do {
// Crash
try JSONSerialization.data(
withJSONObject: NSObject(),
options: [])
}
catch
{
// Never reached
print("Caught error:", error)
}
Why is that method declared “throws,” then? Under what circumstances does it throw an exception?
Not knowing what causes an error to be thrown makes it hard to know how to handle the error, and makes it impossible to write tests that verify that handling.
Turns out it’s the same situation as this question: you can create a Swift string that contains invalid unicode (what?!), and that causes an exception.
let bogusStr = String(
bytes: [0xD8, 0x00] as [UInt8],
encoding: String.Encoding.utf16BigEndian)!
do {
let rawBody = try JSONSerialization.data(
withJSONObject: ["foo": bogusStr], options: [])
}
catch
{
// Exception lands us here
print("Caught error:", error)
}
Why does the example code in the original question crash, then, instead of also throwing an error?
Replying to a bug report, Apple informed me that you should call JSONSerialization.isValidJSONObject(_:) before data(withJSONObject:) if you don’t know for sure that the object is encodable, failing to do that is a misuse of the API, and that’s why they decided it should crash instead of throwing something catchable.