Explicit old fashioned error handling in Swift 2 - ios

Apple introduced fancy new error handling in Swift 2
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014216-CH7-ID10
I'm working with project that uses AFNetoworking v2.x where AFHTTPRequestSerializer has 2 selectors:
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters DEPRECATED_ATTRIBUTE;
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError * __autoreleasing *)error;
First is deprecated but second is converted automatically to first signature by Swift 2 compiler. Old fashioned method now doesn't allowed:
var error: NSError?
let request = self!.operationManager.requestSerializer.requestWithMethod(method, URLString: url?.absoluteString, parameters: params, error: error)
gives me a compile time error:
Cannot convert value of type 'NSError?' to expected argument type '()'
But brand new notation reduces selector with error processing to deprecated variant without it.
do {
let request = try
self!.operationManager.requestSerializer.requestWithMethod(method, URLString: url?.absoluteString, parameters: params)
} catch let error as NSError {
// ...
}
What is best practice in Swift 2 to solve this problem? Is there any way to specify certain selector in this situation?
UPD: More precise link on Swift 2 feature that become a reason of my problem. https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10

Seems this works properly in Xcode 7.1 (I recall having this issue in 7.0) just change your method to use an error pointer i.e &error
var error: NSError?
let request = self!.operationManager.requestSerializer.requestWithMethod(method, URLString: url?.absoluteString, parameters: params, error: &error)

Swift 2 prefers to provide Try Catch block to necessary methods only. Thus if your method have cases where you might need error handling then only provide this block.
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let NSError = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}

Related

How can I throw an NSError from a Swift class and catch it in an Objective-C class?

I need to implement a try-catch structure in Objective-C to handle Swift thrown NSErrors.
I've written an NetService manager with Swift code and I am implementing it on an already existent Objective-C UI.
However, whenever I throw an error from my Swift class, the try-catch structure fails to catch the error and proceeds to the finally block.
Swift error definition:
enum NEONetServiceErrors: Int
{
case requestMadeWithoutIp
}
struct NEONetServiceErrorStrings
{
let requestMadeWithoutIp = ["NEONetService Error: Request made without IP": NEONetServiceErrors.requestMadeWithoutIp]
}
Swift error throwing:
#objc func requestHelloPage() throws
{
if serviceiPAddress != nil
{
makeHelloRequest()
}
else
{
throw NSError(domain: errorStrings.domain, code: NEONetServiceErrors.requestMadeWithoutIp.rawValue, userInfo:errorStrings.requestMadeWithoutIp)
}
}
Objective-C properties:
#property NEONetServiceManager* netServiceManager;
#property NSError* _Nullable __autoreleasing * _Nullable netServiceError;
Objective-C error handling:
- (IBAction)pressUpdateButton:(id)sender
{
#try
{
[self.netServiceManager requestHelloPageAndReturnError: self.netServiceError];
}
#catch (NSException *exception)
{
NSLog(#"Throwing");
}
#finally
{
NSLog(#"Finally");
}
}
Output:
2019-10-18 14:47:03.289268-0300 NEOFirmUpdate[16533:2389800] Start
2019-10-18 14:47:03.292696-0300 NEOFirmUpdate[16533:2389800] Finally
Could you help me figure out what I am doing wrong with my error-handling?
The problem is that a Swift Error / Objective-C NSError is not an NSException. You are configured to catch NSExceptions but that is irrelevant.
The way to "catch" an NSError in Objective-C when Swift throws an Error is by indirection with the NSError** parameter, just as it always has been.
NSError* err = nil;
BOOL ok = [self.netServiceManager requestHelloPageAndReturnError:&err];
if (ok) {
// proceed normally
} else {
// you got an error, it is sitting in `err`
}
(Notice how Swift supplies a BOOL result exactly so you can implement the correct pattern.)
That's because you're using objective-c exceptions there, and not actually checking for an error. To check for errors in objective-c, you pass a reference to your pointer and your function will fill that error out if there was an issue.
NSError *serviceError = nil;
[self.netServiceManager requestHelloPageAndReturnError:&serviceError];
if (serviceError) {
// there was a problem
}
If this is an asynchronous call, you'll need to do this in a closure instead:
NSError *serviceError = nil;
[self.netServiceManager requestHelloPage:^(NSError *error) {
if (error) {
// there was a problem
}
}];
In your Objective-C code you are Catching and NSException, not an NSError
Swift automatically bridges between the Error type and the NSError
class. Objective-C methods that produce errors are imported as Swift
methods that throw, and Swift methods that throw are imported as
Objective-C methods that produce errors, according to Objective-C
error conventions.
for more information you can click here

Firebase: How To Access error object in iOS callback [duplicate]

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

How do I get the error message in Swift 2.0?

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

Objective-C to Swift framework throwing function conversion

I am using CHCSVParser to parse some data. It has been installed through CocoaPods with use_frameworks!. Viewing the header file, there are two functions that I can use, I want to use the one that returns an error to see if something went wrong.
public convenience init!(contentsOfDelimitedURL fileURL: NSURL!, options: CHCSVParserOptions, delimiter: unichar)
// This is the one I want to use as I need the error if it fails.
public convenience init(contentsOfDelimitedURL fileURL: NSURL!, options: CHCSVParserOptions, delimiter: unichar, error: ()) throws
The actual Objective-C methods of the two above:
- (NSArray *)componentsSeparatedByDelimiter:(unichar)delimiter options:(CHCSVParserOptions)options;
- (NSArray *)componentsSeparatedByDelimiter:(unichar)delimiter options:(CHCSVParserOptions)options error:(NSError *__autoreleasing *)error;
I am using the error function like this:
do {
let components = try NSArray(contentsOfDelimitedURL: url, options: CHCSVParserOptions.RecognizesBackslashesAsEscapes, delimiter: 0x002C, error: ())
// Do what ever I need to do...
} catch {
print("Error: \(error)")
}
However I assume passing () to the error param is wrong. When I run this with something I know will fail the catch is never being called. Unsure on how to call this function correctly?
Using Xcode 7 beta 5.
You need to pass the error like this
var error: NSError?
var results = context.executeFetchRequest(request, error: &error)
and also i think you need to check the error object for any error rather than putting try catch

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.

Resources