Uncaught Error/Exception Handling in Swift

Uncaught Error/Exception Handling in Swift

Swift has no mechanism to catch all arbitrary runtime exceptions.
The reasons are explained in

  • [swift-users] "business applications market" flame

in the swift-users forum. Extract:

Swift made a conscious choice not to include exceptions thrown through
arbitrary stack frames not because it was technically impossible, but
because its designers judged the costs to be too high.

The problem is this: if a piece of code is going to exit early because
of an error, it has to be written to handle that early exit. Otherwise
it will misbehave—fail to deallocate memory, fail to close file
handles/sockets/database connections/whatever, fail to release locks,
etc. In a language like Java, writing truly exception-safe code
requires a ridiculous quantity of try/finally blocks. That's why
nobody does it. They make judgements about which exceptions they're
likely to see and which resources are dangerous to leak, and only
protect their code against those specific anticipated conditions. Then
something unforeseen happens and their program breaks.

This is even worse in a reference-counted language like Swift because
correctly balancing the reference counts in the presence of exceptions
basically requires every function to include an implicit finally block
to balance all the retain counts. This means the compiler has to
generate lots of extra code on the off chance that some call or
another throws an exception. The vast majority of this code is never,
ever used, but it has to be there, bloating the process.

Because of these problems, Swift chose not to support traditional
exceptions; instead, it only allows you to throw errors in
specially-marked regions of code. But as a corollary, that means that,
if something goes really wrong in code that can't throw, all it can
really do to prevent a disaster is crash. And currently, the only
thing you can crash is the entire process.

For more information, see

  • Error Handling Rationale and Proposal

Handle all unhandled exceptions swift

You can set NSSetUncaughtExceptionHandler in your AppDelegate, refer to this post

But Swift does not fully support it, as explain in this post, you need to do it in Objective-C

EDIT

Thanks for Martin R's comment, Swift now supports it.

How to handle errors/exceptions in swift

The Swift do-try-catch syntax is designed to handle the errors analogous to the NSError patterns in Objective-C, not for handling fatal errors/exceptions. Do not be confused by the similarity of Swift's error handling do-try-catch with the completely different Objective-C exception handling pattern of @try-@catch.

One should only use forced unwrapping (!) and forced type casting (as!) when one knows with certainty that they cannot possibly fail. If they could fail (as in this case), you should gracefully detect this scenario and handle it accordingly.

You could, for example, use Swift error handling to communicate a failure converting a string to a date (and then use do-try-catch pattern when you call it in order to detect and handle that error):

enum DateError: Error {
case badDate
case dateNotFound
}

func getDateFromTextfield() throws -> Date {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)

guard let dateString = dictOfLabelsAndText["Birthdate"] as? String else {
throw DateError.dateNotFound
}

guard let date = formatter.date(from: dateString) else {
throw DateError.badDate
}

return date
}

Then you could do:

do {
child.birthdateAT = try getDateFromTextfield()
} catch {
child.birthdateAT = formatter.date(from: "01.01.2000")
}

Or, more concisely, use try? and nil coalescing operator:

child.birthdateAT = try? getDateFromTextfield() ?? formatter.date(from: "01.01.2000")

Alternatively, you might just change the method to return an optional and use nil as a way of detecting a failure:


func getDateFromTextfield() -> Date? {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)

guard let dateString = dictOfLabelsAndText["Birthdate"] as? String else {
return nil
}

return formatter.date(from: dateString)
}

And then do:

if let birthDate = getDateFromTextfield() {
child.birthdateAT = birthDate
} else {
child.birthdateAT = formatter.date(from: "01.01.2000")
}

Or, again, use the nil coalescing operator:

child.birthdateAT = getDateFromTextfield() ?? formatter.date(from: "01.01.2000")

But, bottom line, do not use ! to unwrap an optional unless you know it can never be nil. Otherwise, use optional binding and/or guard statements to gracefully detect and report the failure.


This has been updated for contemporary versions of Swift. For original Swift 2 answer, see previous revision of this answer.

Why the swift throws exception despite try?

try? does not catch exceptions. It catches thrown errors. Those are different things in Swift. Exceptions are at the Objective-C level and cannot be caught by Swift at all (they can't be safely caught in ObjC in most cases either, but that's a different discussion).

The solution in this case is to use JSONEncoder rather than JSONSerialization. JSONEncoder is a pure-Swift system. JSONSerialization is bridged from ObjC.

let body = try? JSONEncoder().encode([data])

See Handling Errors for more information:

Error handling in Swift resembles exception handling in other languages, with the use of the try, catch and throw keywords. Unlike exception handling in many languages—including Objective-C—error handling in Swift does not involve unwinding the call stack, a process that can be computationally expensive. As such, the performance characteristics of a throw statement are comparable to those of a return statement.

If you want to use JSONSerialization, it's important to recognize that it is a programming error to call it this way. The exception is intended to crash the program (even in ObjC). The correct way to write this code is:

if JSONSerialization.isValidJSONObject([data]), // <=== first, check it is valid
let body = try? JSONSerialization.data(withJSONObject: [data]) {
print("success")
} else {
print("unable to make body for call")
}

See the docs for more information:

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(_:).

The thrown error from JSONSerialization is only to indicate an internal error in the serializer, not an attempt to encode an invalid object:

error

If an internal error occurs, upon return contains an NSError object with code NSPropertyListWriteInvalidError that describes the problem.

Error Handle of NSRangeException in swift

At last I reached at following solution that in swift if need to handle range exceptions then we need to do this

var sourceString = "This is demo text"
let sourceData = sourceString.data(using: String.Encoding.utf8)! // sourceData is equivalent to "wav" from question

Total data length(sourceData.length) should be maximum from your
staring point(4000) and length of data(20) which you need to retrieve

guard sourceData.length >= (4000 + 20) else{

throw LevelParsingException.InvalidLevelContent
}

terminating with uncaught exception of type NSException in Swift 5

Maybe something like this

var islem: String = screenTextfield.text!

if let number = Int(String(islem.last!)) {
print(number)
}
else {
islem.removeLast()
}

let exp = NSExpression(format: islem)
if let result = exp.expressionValue(with: nil, context: nil) as? NSNumber {
islemLabel.text = String(result.doubleValue)
}
else {
makeAlert(title: "Error", message: "Wrong math type")
}


Related Topics



Leave a reply



Submit