Coming from Android, wrapping a block of code in a try..catch statement is straightforward.
try{
//A whole lot of code
} catch (Exception e) {
//Exception is a parameter
}
I wanted to do the same thing in Swift, but it doesn't seem to be quite this easy.
If I understand correctly, you need place a try in front of something that you think will go wrong.
Is there anything similar to the Android - functionality I described in the Swift toolbox?
Error handling in Swift 2 takes two things into consideration:
Whether the method can throw an error (marked with throws keyword in its declaration).
Whether the method that can throw is called from throwing scope (e.g. another methods that can throw) or not.
For non-throwing methods it's simple: you do not need to put any try or do/catch.
Methods that throw must always be prefixed by try, regardless of scope. So, that's a part of the answer to your question.
In case if scope is throwing, you do not have to use do/catch (but you can). In this case all errors thrown in current scope that are not handled by it will be passed upstream to the parent throwing scope.
If the scope is non-throwing, then you must handle all errors that can possibly be thrown by the methods that are called from said scope. Therefore, you must use do/catch.
Depending on your particular case, you might have to put do/catch around as well. If your current scope is non-throwing you will have to.
There are 3 types of do/catch.
I. Catch all
do {
methodThadDoesNotThrow()
try methodThatThrows()
}
catch {
// This will catch all errors that can be possibly thrown.
// You get implicit local-scope variable `error` that holds thrown error.
}
II. Catch concrete error type
Errors that can be thrown must implement ErrorType protocol. This is the only pre-requisite. They do not really have to be NSError or its descendants (this type is just a legacy from Objective-C). Therefore, if you know the concrete error type that you expect you can do something like:
do {
methodThadDoesNotThrow()
try methodThatThrows()
}
catch let error as CustomErrorType {
// This will catch all `CustomErrorType` errors.
// You get explicit local-scope variable `error` of `CustomErrorType` type.
}
III. Catch concrete error instance
Typically error types are declared as enums. For example:
enum CustomErrorType {
case SomethingStrange(String)
case SomethingMildlyHorrible
case Apocalypse
}
If you want to catch some specific case of CustomErrorType, you can do:
do {
methodThadDoesNotThrow()
try methodThatThrows()
}
catch CustomErrorType.SomethingStrange(let message) {
// This will catch only `CustomErrorType.SomethingStrange` errors.
// You get explicit local-scope variable `message` that
// will come from the value associated with the error enum-case.
}
There is, of course, the case of:
IV. Combine all together
do {
methodThadDoesNotThrow()
try methodThatThrows()
}
catch CustomErrorType.SomethingStrange(let message) {
// This will catch `CustomErrorType.SomethingStrange` error.
}
catch let error as CustomErrorType {
// This will catch the rest of `CustomErrorType` errors.
}
catch {
// Finally, this will catch all the rest of the errors
// (anything that is not `CustomErrorType` in this case).
}
Related
I looked for this article, but it does not cover my case.
If i understand correct, we can use try either in do..catch.. statement or in a function that can throw.
But sometimes i see something like:
let jsonData = try jsonEncoder.encode(employee1)
Where jsonData is not optional. What is meaning of this? What if try sttement fail? Why value is not optional? Can someone explain? Thanks.
In addition to the cases you mentioned, you can call try at
top-level code. Here is a simple self-contained example:
// main.swift:
enum MyError : Error {
case failed
}
func foo() throws -> Int {
throw MyError.failed
}
defer { print("Good bye.") }
let x = try foo()
print(x)
You can compile and run this as a Xcode "Command Line Project"
or directly from the command line:
$ swiftc main.swift
$ ./main
Good bye.
Fatal error: Error raised at top level: main.MyError.failed: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.74.1/src/swift/stdlib/public/core/ErrorType.swift, line 187
Illegal instruction: 4
The failed try in the top-level code causes the program to
terminate with an error message. Deferred statement (if present) will be executed however.
This is slightly different from using a forced try! statement,
which causes the program to abort as well, but immediately, without executing deferred statements. (This can be relevant if deferred
statements are used to clean-up resources, e.g. remove temporary files).
The error message originates from ErrorType.swift, line 187:
/// Invoked by the compiler when code at top level throws an uncaught error.
#_inlineable // FIXME(sil-serialize-all)
#_silgen_name("swift_errorInMain")
public func _errorInMain(_ error: Error) {
fatalError("Error raised at top level: \(String(reflecting: error))")
}
(also observed in Non Exhaustive List When Handling Errors Inside a Class Function in Swift).
Apparently the top-level code behaves as if embedded in a
do-catch block:
do {
func foo() throws -> Int {
throw NSError(domain: "foo", code: 0, userInfo: nil)
}
defer { print("Good bye.") }
let x = try foo()
} catch {
fatalError("Error raised at top level: \(String(reflecting: error))")
}
A function call marked with try like the one you posted, as you stated, must be located inside a function marked with the keyword throws or a do/catch statement or it will cause a compile error.
On a side note if you want to call a throwing function outside a do/catch or another throwing function you can use try? before your function call to get an optional result that will be nil if something is thrown. There is also the try! variant that assumes nothing will be thrown returning a non-optional result, but if something is thrown the app will crash.
I come from a .NET background, where error handling can be as simple as wrapping a set of statements in a try-catch. For example:
try
{
statement1
statement2
}
catch (ex)
{
log(ex.Message)
}
I'm trying to add error handling in my Swift project, and all the articles I have read so far seem to indicate that error handling requires more work in Swift. In the example above, it seems I would need to know exactly which statement throws an error, and then add a "try" before it. Is it not possible to simply wrap a block of code in a try-catch, and inspect the error that gets thrown?
Nope, you can't just wrap a block of code with try-catch.
First of all, cause not every line of code could produce exceptions. Only functions marked as "throws" could produce exceptions.
For example, you have some function:
deserialise(json: JSON) -> MyObjectType throws
And let this functions throws exceptions of type MyErrorType
That's how you should use it:
....
do {
let deserialisedObject = try deserialise(jsonObject)
... // do whatever you want with deserialised object here
} catch let error as MyErrorType {
... // do whatever you want with error here
}
...
So, as you see, exceptions in swift is not the same thing as exceptions in C++(or other regular language) for .NET
You can use try catch in swift just like you do in .net with this little change in structure,
before that create a enum of exceptions which you want to catch like follows
//exceptions to catch, change as per your need
enum someActionError: Error {
case invalidSelection
case outOfStock
}
//use
do {
try statement1
} catch someActionError.invalidSelection {
print("Invalid Selection.")
} catch someActionError.outOfStock {
print("Out of Stock.")
}
When calling a function in Swift 3 that throws, you have to be exhaustive in catching all possible errors, which often means you have an unnecessary extra catch {} at the end to handle errors that won't happen.
Is it possible to say throws MyErrorType so that the compiler can know you have indeed been exhaustive when you handle all cases from that enumeration?
There's no simple way to be type-safe with thrown errors. Consider this, if the compiler allowed you to specify throws MyErrorType, then it would also have to ensure within that function body that you're not trying a function that could potentially throw a different type outside of a do/catch block. (Well there is but it would add layers of unnecessary complexity). The Swift compiler can already be slow and get stuck in loops when inferring types, inferring Thrown types all the way up a chain of throwing functions could be a nightmare.
The running idea is that for most errors you're going to handle them in a small subset of ways anyhow.
That being said, there's no need for you to add extra catch let error as MyErrorType clauses, you can simply use a switch in a catch block like so:
do {
try something()
} catch let e {
switch e {
case let m as MyErrorType: handleMyError(m)
case let o as OtherErrorType: handleOther(o)
case is ThirdErrorType: print("error \(e)")
default: handleElse(e)
}
}
My suggestion for this problem is instead of throwing an error return a Result type in your function. It would be something like this.
enum MyCustomError: Error {
case genericError
}
func operationThatFails() -> Result<Response, MyCustomError> {
guard requiredConsition() else {
return .failure(.genericError)
}
return Response()
}
Then you can handle the error like this:
let result = operationThatFails()
switch result {
case .success(let value):
// handle success
case .failure(let error):
// handle error
}
This way your error is always type safe
I'm writing a utility function which takes a parameter and always returns a valid non-nil result (notice the returned value is not optional because the possible parameters are all actually hardcoded and valid, so I know the function cannot fail):
func myFunction(param: String) -> NonTrivialObject {...}
Now, during development I want to experiment with possible parameters and, should I make a mistake, I want the function to throw an exception and just crash. I don't want or need to throw Swift errors or catch them, I want to hard-crash and fix the parameter immediately. In Objective C I would just use NSParameterAssert() or do something along these lines:
guard let validatedParam = param where param != nil else {
NSException(...).raise()
return nil
}
// do the actual work and return a non-optional result
However, I cannot return nil because the result is not an optional. Is there a way to somehow tell the compiler that it doesn't need to bother returning anything from the function after an exception is thrown? Or am I doomed to litter my code with unwrapping optionals or try! statements or to return a dummy object just to make the compiler pleased?
You can use Swift assert(_:_file:line:) function as follows
assert(some condition, "Message to display if condition is false (optional)" )
If the condition is verified the app will continue running, otherwise it will terminate
An optional may contain nil, but Swift syntax forces you to safely deal with it using the ? syntax to indicate to the compiler you understand the behavior and will handle it safely.
You can define the function to be a throwing function:
func foo(param: String) throws -> NonTrivialObject {
guard param != nil else {
throw SomeErrorEnum.NilFound
}
doStuff(...)
}
The error enum needs to conform to the ErrorType protocol. The function call is now able to catch errors like this:
do {
try foo(param)
} catch SomeErrorEnum.NilFound {
print("Found nil")
}
In this way the function returns a non-optional but can also throw errors. For more information see: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html
I do not know how do I handle runtime error in swift. I need to check for a parsing error and do something about it. Can anyone help me please?
I have code something like this:
var:SomeObject = parse("some string")
I need to handle any generic error that occurs in runtime.
Thanks!
If the function is yours, then in case of a failure, you can make that function return a nil value.
That way your line of code
var anObj:SomeObject = parse("some string")
would become something like this
var:SomeObject? = parse("some string")
Notice the ? sign. It means that the value in it is Optional. In simple words, it could be some actual value, or it could be nil.
After this function, you should perform a check like
If anObj != nil
{
//do something
}
else
{
//the parse didn't go right, handle the erorr here.
}
Swift 2 adds additional safety to your error checking. You use the throws keyword to specify which functions and methods could throw an error. Then you have the do, try, and catch keywords for when you call something that could throw:
// 1
enum ParseError: ErrorType {
case InvalidValue
}
// 2
func parseWithError(value:String) throws {
if value.count > 0 {
// yeaaa!
} else {
// 3
throw ParseError.InvalidValue
}
}
func parse(value:String) {
// 4
do {
try parseWithError(value)
} catch {
print("Could not parse! :[")
return
}
}
There are a few things to highlight here:
To create an error to throw, simply create an enum that derives from ErrorType.
You need to use the throws keyword to mark any function that can throw an error.
This throws an error, which will be caught in section 4.
Instead of try blocks, which might be familiar from other languages, you wrap any code that can throw an error in a do block. Then, you add the try keyword to each function call that could throw an error.
For more read here or this is the official documentation of Error Handling