Error Handling

From Coder Merlin
Revision as of 04:51, 12 October 2021 by Paolo-besabella (talk | contribs)
Within these castle walls be forged Mavens of Computer Science ...
— Merlin, The Coder

Curriculum[edit]

ExercisesIcon.png
 Coder Merlin™  Computer Science Curriculum Data

Unit:

Experience Name: Error Handling ()

Next Experience: ()

Knowledge and skills: noneSome use of "" in your query was not closed by a matching "".

Topic areas: none

Classroom time (average):

Study time (average):

Successful completion requires knowledge: none

Successful completion requires skills: none

Error Handling[edit]

The process of finding and fixing errors in your code is called error handling in Swift, whereas it is often referred to as exception handling in most programming languages.

There are times when successful execution or a beneficial output is not assured. If there isn't any data to return, optionals are used to indicate that fact; however, when an operation fails, it's often useful to understand what went wrong so your code may react appropriately.

Consider the challenge of reading and processing data from a file on disk, for example. There are several ways this operation might fail, including the file not existing at the correct path, the file not having read permissions, or the file is encoded in an incompatible format. Differentiating among these different situations allows a program to address some issues and notify users about any they can't resolve.

Things to Learn[edit]

In this tutorial, you will learn the following concepts:

  • How to define custom errors that can be thrown, caught, and handled.
  • The difference between throws and propagating errors.
  • What the try, throw, catch, and do keywords are for.

Before diving into Swift's error handling model in detail (the focus of this tutorial), it helps to understand what an error is at a more abstract level. This article discusses how to model recoverable errors as values—specifically as instances of Swift's ErrorType enum type—and provides an introduction to throwing and catching errors using both synchronous and asynchronous APIs, then concludes with some guidance on using convention over configuration when writing your own error types.

Throwing Errors[edit]

In Swift, error values are represented by types that conform to the Error protocol. This empty protocol implies that a type may be used for error handling.

Enumerations in Apple's Swift programming language are well suited to modeling a set of associated error conditions, with values that may be used to provide more information about the nature of an issue.

enum atmError: Error {
    case insufficientFunds
    case wrongPin
    case invalidTransaction
}

An error is a runtime condition that prevents the normal flow of execution from continuing. To throw an error, you use the throw statement.

enum atmError: Error {
    throw atmError.wrongPin
}

Handling Errors[edit]

When an error is raised, the surrounding code must handle it—for example, by fixing the problem, exploring a different path, or informing the user of the failure.

In Swift, there are four different ways to deal with faults. You may propagate the issue from a function to the code that calls it, utilizing a do-catch construct, taking care of it as an optional value, or making sure that it does not happen again.

When a function fails, it affects the course of your program, so finding locations in your code that might produce problems fast is critical. Use the try? or try! variants of the try keyword to find these locations in your code before a piece of code that may call an error-throwing procedure, method, or initializer. The following paragraphs describe these keywords in further detail.

Throwing Functions[edit]

After the parameters, you put the throws statement in a function's declaration to indicate that it may throw an error. A throwing function is one that has the throws keyword appended to its name. You write the throws keyword before any return arrow if the function returns a value.

When code inside of a throwing function is executed, it propagates any problems that are thrown to the scope from which the call was made.

CoderMerlin™ Code Explorer: W0000 (1) 🟢


Do-Catch Blocks[edit]

You run a block of code in a do-catch statement if an error is detected. When the code in the do clause throws an exception, it's checked against the catch clauses to see which one can handle it. The following is the general form of a do-catch statement:

do {
    try expression
    statements
} catch pattern 1 {
    statements
} catch pattern 2 where condition {
    statements
} catch pattern 3, pattern 4 where condition {
    statements
} catch {
    statements
}
}

You write a pattern after the catch to specify the errors that the clause can handle. If a catch clause does not include a pattern, any exception will be bound to error and interpreted as a local constant named error.

The catch clauses are used to decide whether to allow the propagation of an error. Because the function can throw an error, it is called in a try expression. If an error occurs, control immediately transfers to the catch clauses, which must determine whether the error should be propagated. If no pattern is matched, the exception is caught by the last catch clause and assigned to a local error constantly. The remainder of the do statement's code is run if no error occurred.

The catch clauses don't have to deal with the do clause's numerous possibilities for error. If none of the catch clauses can handle the situation, it will be passed on to the surrounding scope. On the other hand, the generated problem must be addressed by some surrounding scope.

In a non-throwing function, the mistake must be handled by an enclosing do-catch block. Throwing functions require either an enclosing do-catch statement or may be dealt with by another part of your program (typically referred to as a "caller"). If the error is not handled at the top level, you will receive a runtime error.

do {
    //create audio player
    playAudio = try AVAudioPlayer(contentsOf: soundURL)
            
    // play sound
    playAudio?.play()
}
catch {
    // object not created
    print("Audio player creation failed for \(audioFile)")
}
}}

Converting Errors to Optional Values[edit]

You convert a problem to an optional value by using the try? operator. If the try? expression evaluates to nil, so does the value of the statement.

An exception arises if myThrowingFunction() throws an error, in which case x and y are both set to nil. Otherwise, the value of x and y is the same as that of the function's return. Note that since whatever type myThrowingFunction() delivers is optional, so too are x and y. Because the function here returns an integer, therefore x and y are integers.

The try? syntax allows you to write brief error handling code when you want to handle all errors the same way.