Under what circumstances does JSONSerialization.data(withJSONObject:) throw a catchable error? - ios

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.

Related

'no calls to throwing functions occur within 'try' expression [warning] in swift 3

I'm trying to delete an object from core data and I tried it within try catch as below.
do {
try self.managedContext.deleteObject(self.productList[indexPath.row])
} catch let error as NSError {
print("something wrong with the delete : \(error.userInfo)")
}
it say 'no calls to throwing functions occur within 'try' expression and 'catch block is unreachable because no errors are throw in 'do' block. following image give you more idea.
why is this. I have no idea. how to solve this. hope your help.
The deleteObject method doesn't throw. Remove the Do-Catch block and the warning will go away.
Make sure that the method that you are invoking after try throws.
Example:
func someThrowingFunction() throws -> Int {
// ...
}
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
more information about that here: ErrorHandling

Catching NSJSONSerialization errors in Swift

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)")
}

Nil is not compatible with expected argument type '()'

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.

handling error using xcode 7.0 beta in asynchronous block

I am trying to validate different errors while downloading text files from AWS S3, and with the next piece of code:
... above here function receiving String parameters ruta, archivo, archivoLocal
let directorioURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first! as NSURL
let archivoURL = directorioURL.URLByAppendingPathComponent("b\(archivoLocal)")
let downloadRequest = AWSS3TransferManagerDownloadRequest()
downloadRequest.bucket = ruta
downloadRequest.key = archivo
downloadRequest.downloadingFileURL = archivoURL
let transferManager = AWSS3TransferManager.defaultS3TransferManager()
let task = BFTask()
let executor = BFExecutor.mainThreadExecutor()
transferManager.download(downloadRequest).continueWithExecutor(executor, withBlock: { (task) -> AnyObject! in
if task.error != nil {
if task.error.domain == AWSS3TransferManagerErrorDomain {
self.processDomainErrorType(AWSS3TransferManagerErrorType(rawValue: task.error.code))
} else {
self.processError(task.error)
}
} else if task.result != nil {
do {
let mytext = try String(contentsOfURL: archivoURL, encoding: NSUTF8StringEncoding)
self.processResult(mytext)
} catch let urlerror as? NSError {
self.processError(urlerror)
}
}
...
I am getting the error:
Invalid conversion from throwing function of type '(_) throws -> AnyObject!' to non-throwing function type '#convention(block) (BFTask!) -> AnyObject!'
I obtained the "do { try } catch" syntax from https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10
I can remove the error by replacing the catch clause with:
} catch _ {
self.processError(NSError(domain: "String-ContentsOfURL Error", code: 100, userInfo: nil))
}
Of course this way I will never know the real cause why ContentsOfURL could be failing.
All I can figure out why this error happens is because this syntax is valid only for OS X apps and for iOS the error handling guide at
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42
allows only the second syntax unless you own the object throwing the errors from an enum structure of ErrorType type, which is not the case since I want to catch the NSError from String object, contentsOfURL function.
I hope someone could guide me through this, maybe being XCode 7 a beta, the catch syntax is still incomplete or maybe I should not matter about the reason why this function fails, but I think it is important to determine what is making this function fail and if it could be traced and fixed before reaching the do-try-catch clause.
Additionally to the above error, I am getting a warning in the task variable assignation line to BFTask() saying that "Initialization of immutable value 'task' was never used". I think this is a bug with this beta version that it doesn't include the pattern to acknowledge that the variable task is being used in the asynchronous block. I'd appreciate a lot some confirmation about this and if I just need to ignore it.
By the way, the only reason I am using XCode 7 beta is because my client wants to evaluate the app before acquiring their apple membership.
Apple replaced NSError with ErrorType in Swift 2.
Replace your own explicit usage of NSError with ErrorType and you don't get this type of compiler errors.

SLRequestHandler error using Swift

I am trying to mimic the app as in this youtube tutorial, but using Swift and I am facing a problem constructing the closure as shown in the code snippet below.
func twitterTimeline() {
let account = ACAccountStore()
let accountType = account.accountTypeWithAccountTypeIdentifier(ACAccountTypeIdentifierTwitter)
// take action
account.requestAccessToAccountsWithType(accountType, options: nil,
completion: { (granted, error) in
if (granted) {
// invoke twitter API
let arrayOfAccount: NSArray = account.accountsWithAccountType(accountType)
if (arrayOfAccount.count > 0) {
let twitterAccount = arrayOfAccount.lastObject as ACAccount
let requestAPI = NSURL.URLWithString("http://api.twitter.com/1.1/atuses/user_timeline.json")
var parameters = Dictionary<String, String>()
parameters["100"] = "count"
parameters["1"] = "include_entities"
let posts = SLRequest(forServiceType: SLServiceTypeTwitter, requestMethod: SLRequestMethod.GET, URL: requestAPI, parameters: parameters)
posts.account = twitterAccount
// This is the Error Prone Area
let handler: SLRequestHandler = { (response, urlResponse, error) in
self.array = NSJSONSerialization.JSONObjectWithData(data: response, options: NSJSONReadingOptions.MutableLeaves, error: &error) as NSArray
}
posts.performRequestWithHandler(handler)
}
} else {
// do something
}
}
)
}
The error I get is
Cannot convert expression's type '($T1, $T2, $T3) -> $T0' to type '()'
I have tried checking and explicitly casting the types with no much help. I believe the error is somewhere else. Could anyone help me with what exactly is the trouble? I am sorry, if this turns out to be a näive question.
Thanks in advance,
Nikhil
This looks like an interesting case of error propagation in the compiler — I'd suggest filing a bug report with Apple.
The error message you're getting says that you can't assign a closure (which takes three parameters and returns one value) to something that takes no parameters. What's actually going wrong is that the handler closure you're defining takes its error input parameter and tries to pass it to JSONObjectWithData(_:options:error:). That's problematic from a language perspective because the error you're getting in is an immutable (optional) reference to one error, and the parameter you're passing it to expects a mutable pointer for it to (potentially) write another error into.
It's also incorrect API usage. The error you receive as a parameter in the closure is a report of an error that happened in whatever procedure calls your closure. You should log this error, present it to the user, or examine it so your app can gracefully fail. The error parameter you pass to JSONObjectWithData is a place for you to receive reports of additional errors that occur when decoding JSON from your data — you should be handling this error, too. These are two separate places to receive errors, so you shouldn't be passing one to the other.
If you fix that, you'll find a more helpful compiler message saying that the data: label on the first parameter to that function should be omitted. Also, you can use type inference for the options: parameter. So, your handler definition should look something more like this:
let handler: SLRequestHandler = { (response, urlResponse, error) in
// check for error and do something about it if need be, then...
var err: NSError?
if let jsonArray = NSJSONSerialization.JSONObjectWithData(response, options: NSJSONReadingOptions.MutableLeaves, error: &err) as? NSArray {
self.array = jsonArray
} else {
// do something about err
}
}
(You can also probably use a Swift typed array instead of an NSArray if you know what to expect from your JSON. But that's another subject for another question. Actually, several questions.)

Resources