Precondition, Assert, Fatal Error, or Guard in your Swift Code

Similar, but not quite the same

If you want to stop your code from crashing, or if you don’t want a function to complete if it does not have a variable set as you desire. But what about whether you want to perform actions in production or development code?

For the lowdown, read on!

Image for post
Image for post
Photo by Jean Carlo Emer on Unsplash

Difficulty: Beginner | Easy | Normal | Challenging

Prerequisites

Some basic understanding of functions and methods in Swift would be useful (guide HERE).

Terminology

Assert: A function used for internal sanity checks that are active during testing, but are not used in production code

Function: The process of removing unnecessary detail from a problem

Guard: A statement used to transfer program control out of a scope if the condition is not met

Precondition: A function that checks a condition

Scope: The region of a program where a variable is valid

Variable: A named value, that potentially can be changed as the program runs

Precondition

precondition()

Precondition allows us to check a condition before proceeding with code. That is, if a Condition stated evaluates to false the program execution stops.

The one big condition in Precondition (LOLOLOL) is that it works in production code as well as development code. If the chosen Condition stated evaluates to false precondition will stop the executing code, and log a chosen message

func testPrecondition(willRun: Bool) {
precondition(willRun)
print ("The function /(#function) completed")
}
testPrecondition(willRun: true) // The function testPrecondition(willRun:) completed
testPrecondition(willRun: false) // Precondition failed: file PreconditionAssertGuard.playground, line 6

We can also add a message to allow

func testPrecondition(willRun: Bool) {
precondition(willRun, "Must set willRun to true")
print ("The function /(#function) completed")
}
testPrecondition(willRun: true) // The function testPrecondition(willRun:) completed
testPrecondition(willRun: false) // Precondition failed: Must set willRun to true: file PreconditionAssertGuard.playground, line 14

This provides a slightly more verbose version of the function above

preconditionFailure()

preconditionFailure always fails, and is useful for testing

func testPreconditionFailure() {
preconditionFailure("Expected failure")
}
testPreconditionFailure() //Fatal error: Expected failure: file PreconditionAssertGuard.playground, line 23

Assert

assert()

Assert is similar to Precondition, but does not work on production code. This means that it is useful for testing but will never break an application on the store.

Much like Precondition we can have a chosen message that can be logged

func testAssert(willRun: Bool) {
precondition(willRun, "Must set willRun to true")
print ("The function /(#function) completed")
}
testAssert(willRun: true) // The function testAssert(willRun:) completed
testAssert(willRun: false) // Assertion failed: Must set willRun to true: file PreconditionAssertGuard.playground, line 30

assertionFailure()

Much like Precondition there is an assertionFailure type

func testAssertFailure() {
assertionFailure("Expected failure")
}
testAssertFailure() // Fatal error: Expected failure: file PreconditionAssertGuard.playground, line 39

fatalError()

Sometimes it is better to force a crash then allowing an App to run in an unstable state.

fatalError can also display a chosen message that can be logged, but it works for all build configurations. That is, a fatalError always produces an error. Therefore fatalError is much like assertFailure but works in development and production code.

func testFatalError() {
fatalError("This is a failure")
}
testFatalError() // Fatal error: This is a failure: file PreconditionAssertGuard.playground, line 46

A real-world example for this might be writing a file. If the file is not written we don’t go through the process of trying to read the file and then crashing, we might as well just crash if writing the file ended in failure.

guard

Guard works slightly differently. It defines different paths through the code depending on whether the expression resolves to true or false. Because we are dealing with code paths here,

func testGuard(willRun: bool) {
guard willRun else {return}
print ("runs")
}
testGuard(willRun: true) // The function testGuard(willRun:) completed
testGuard(willRun: false) // No output

Conclusion:

There are clear differences between guard , precondition , preconditionFailure , assert , assertionFailure , and fatalError exist because you need to choose the correct tool for your needs.

Do you want a crash in development so you can test easily, and in production you want the code to try to complete despite the fact it is missing some important data?

Only you, master, can decide.

Extend your knowledge

The Twitter contact:

Any questions? You can get in touch with me here

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store