Swift Error Handling Best Practices With Xcode Example

 

What is error handling in Swift? Why error handling required explain types of error? What is the error handling? Do catch vs Try Swift? What are the different types of error handling? What is exception handling explain with example?



This Article will cover swift error handling best practices  using Swift and introduce topics such as error typestry catch, try catch exception,,code errors, swift custom error, swift throw, try else, throwing methods and functions, the guard and defer statements and do-catch statements.


swift error description :

There are two sides to handling errors within Swift. The first involves triggering (or throwing) an error when the desired results are not achieved within the method of an iOS app. The second involves catching and handling the error after it is thrown by a method.

When an error is thrown, the error will be of a particular error type which can be used to identify the specific nature of the error and to decide on the most appropriate course of action to be taken. The error type value can be any value that conforms to the ErrorType protocol.

In addition to implementing methods in an app to throw errors when necessary, it is important to be aware that a number of API methods in the iOS SDK (particularly those relating to file handling) will throw errors which will need to be handled within the code of the app.


 Swift Error Types:- 

As an illustrationconsider a method that is required to transfer a file to a remote server. Such a method might fail to transfer the file for a variety of reasons such as there being no network connection, the connection being too slow or the failure to find the file to be transferred. All these possible errors could be represented within an enumeration that conforms to the Error protocol as follows:

enum FileTransferError: Error {
    case noConnection
    case lowBandwidth
    case fileNotFound
}

Once an error type has been declared, it can be used within a method when throwing errors. 

Throwing an Error:-

A method or function declares that it can throw an error using the throws keyword. For example:

func transferFile() throws {
}

In the event that the function or method returns a result, the throws keyword is placed before the return type as follows:

func transferFile() throws -> Bool {
} 

Swift throw errors :

Once a method has been declared as being able to Swift throw errors, code can then be added to throw the errors when they are encountered. This is achieved using the throw statement in conjunction with the guard statement. The following code declares some constants to serve as status values and then implements the guard and throw behavior for the method:

let connectionOK = true
let connectionSpeed = 30.00
let fileFound = false
enum FileTransferError: Error {
    case noConnection
    case lowBandwidth
    case fileNotFound
}
func fileTransfer() throws {
    guard connectionOK else {
        throw FileTransferError.noConnection
    }
    guard connectionSpeed > 30 else {
        throw FileTransferError.lowBandwidth
    }
    guard fileFound else {
        throw FileTransferError.fileNotFound
    }
}

Within the body of the method, each guard statement checks a condition for a true or false result. In the event of a false result, the code contained within the else body is executed. In the case of a false result, the throw statement is used to throw one of the error values contained in the FileTransferError enumeration.

Calling Throwing Methods and Functions:-

Once a method or function is declared as throwing errors, it can no longer be called in the usual manner. Calls

to such methods must now be prefixed by the try statement as follows:

try fileTransfer()

In addition to using the try statement, the call must also be made from within a do-catch statement to catch and handle any errors that may be thrown. Consider, for example, that the fileTransfer method needs to be called from within a method named sendFile. The code within this method might be implemented as follows:

func sendFile() -> String {
    do {try fileTransfer()
    } catch FileTransferError.noConnection {
        return("No Network Connection")
    } catch FileTransferError.lowBandwidth {
        return("File Transfer Speed too Low")
    } catch FileTransferError.fileNotFound {
        return("File not Found")
    } catch {
        return("Unknown error")
    }
    return("Successful transfer")
}

The method calls the fileTransfer method from within a do-catch statement which, in turn, includes catch conditions for each of the three possible error conditions. In each case, the method simply returns a string value containing a description of the error. In the event that no error was thrown, a string value is returned indicating a successful file transfer. Note that a fourth catch condition is included with no pattern matching. This is a “catch all” statement that ensures that any errors not matched by the preceding catch statements are also handled. This is required because do-catch statements must be exhaustive (in other words constructed so as to catch all possible error conditions).

Swift also allows multiple matches to be declared within a single catch statement, with the list of matches separated by commas. For example, a single catch declaration could be used to handle both the noConnection and lowBandwidth errors as follows: 

func sendFile() -> String {
do {try fileTransfer()
} catch FileTransferError.noConnection, FileTransferError.lowBandwidth {
        return("Connection problem")
    } catch FileTransferError.fileNotFound {
        return("File not Found")
    } catch {
        return("Unknown error")
    }
    return("Successful transfer")
}

Accessing the Error Object:-

When a method call fails, it will invariably return an Error object identifying the nature of the failure. A common requirement within the catch statement is to gain access to this object so that appropriate corrective action can be taken within the app code. The following code demonstrates how such an error object is accessed from within a catch statement when attempting to create a new file system directory:

do {try filemgr.createDirectory(atPath: newDir,
                        withIntermediateDirectories: true,
} catch let error { attributes: nil)
print("Error: \(error.localizedDescription)")
}

Disabling Error Catching:-

A throwing method may be forced to run without the need to enclose the call within a do-catch statement by

using the try! statement as follows:

try! fileTransfer

In using this approach we are informing the compiler that we know with absolute certainty that the method call will not result in an error being thrown. In the event that an error is thrown when using this technique, the code will fail with a runtime error. As such, this approach should be used sparingly.

Using the defer Statement:-

The previously implemented sendFile method demonstrated a common scenario when handling errors. Each of the catch clauses in the do-catch statement contained a return statement that returned control to the calling method. In such a situation, however, it might be useful to be able to perform some other task before control is returned and regardless of the type of error that was encountered. The sendFile method might, for example, need to remove temporary files before returning. This behavior can be achieved using the defer statement.

The defer statement allows a sequence of code statements to be declared as needing to be run as soon as the method returns. In the following code, the sendFile method has been modified to include a defer statement:

func sendFile() -> String {
    defer {
        removeTmpFiles()
        closeConnection()
    }
    do {try fileTransfer()
    } catch FileTransferError.NoConnection {
        return("No Network Connection")
    } catch FileTransferError.LowBandwidth {
        return("File Transfer Speed too Low")
    } catch FileTransferError.FileNotFound {
        return("File not Found")
    } catch {
        return("Unknown error")
    }
    return("Successful transfer")
}

With the defer statement now added, the calls to the removeTmpFiles and closeConnection methods will always be made before the method returns, regardless of which return call gets triggered.


Thanks for reading!!

 

------------------------------------------------------

Post a Comment

0 Comments