Difference between precondition and assert in swift?
assert
is for sanity checks during testing, whereas precondition
is for guarding against things that, if they happen, would mean your program just could not reasonably proceed.
So for example, you might put an assert
on some calculation having sensible results (within some bounds, say), to quickly find if you have a bug. But you wouldn’t want to ship with that, since the out-of-bound result might be valid, and not critical so shouldn’t crash your app (suppose you were just using it to display progress in a progress bar).
On the other hand, checking that a subscript on an array is valid when fetching an element is a precondition
. There is no reasonable next action for the array object to take when asked for an invalid subscript, since it must return a non-optional value.
Full text from the docs (try option-clicking assert
and precondition
in Xcode):
Precondition
Check a necessary condition for making forward progress.
Use this function to detect conditions that must prevent the
program from proceeding even in shipping code.
In playgrounds and -Onone builds (the default for Xcode's Debug
configuration): ifcondition
evaluates to false, stop program
execution in a debuggable state after printingmessage
.In -O builds (the default for Xcode's Release configuration):
ifcondition
evaluates to false, stop program execution.In -Ounchecked builds,
condition
is not evaluated, but the
optimizer may assume that it would evaluate totrue
. Failure
to satisfy that assumption in -Ounchecked builds is a serious
programming error.
Assert
Traditional C-style assert with an optional message.
Use this function for internal sanity checks that are active
during testing but do not impact performance of shipping code.
To check for invalid usage in Release builds; seeprecondition
.
In playgrounds and -Onone builds (the default for Xcode's Debug
configuration): ifcondition
evaluates to false, stop program
execution in a debuggable state after printingmessage
.In -O builds (the default for Xcode's Release configuration),
condition
is not evaluated, and there are no effects.In -Ounchecked builds,
condition
is not evaluated, but the
optimizer may assume that it would evaluate totrue
. Failure
to satisfy that assumption in -Ounchecked builds is a serious
programming error.
When should you use assertions and preconditions and when you can use guard statements, forced unwrapping and error handling?
Errors are a form of flow control, on a par with
if
andwhile
. In particuar, they involve coherent message sending and early exit. The idea is to wind up the current scope immediately and return control to the caller, telling the caller "something went wrong".Assertions are a way to crash immediately.
Thus they belong to completely different conceptual worlds. Errors are for things that can go wrong in real time, from which we need to recover coherently. Assertions are for things that should never go wrong, and about which we feel so strongly that we don't want the program even to be released into the world under these circumstances, and can be used in places where Errors can't be used.
Example from my own code:
final class Board : NSObject, NSCoding, CALayerDelegate {
// ...
fileprivate var xct : Int { return self.grid.xct }
fileprivate var yct : Int { return self.grid.yct }
fileprivate var grid : Grid // can't live without a grid, but it is mutable
// ...
fileprivate lazy var pieceSize : CGSize = {
assert((self.xct > 0 && self.yct > 0), "Meaningless to ask for piece size with no grid dimensions.")
let pieceWidth : CGFloat = self.view.bounds.size.width / (CGFloat(self.xct) + OUTER + LEFTMARGIN + RIGHTMARGIN)
let pieceHeight : CGFloat = self.view.bounds.size.height / (CGFloat(self.yct) + OUTER + TOPMARGIN + BOTTOMMARGIN)
return CGSize(width: pieceWidth, height: pieceHeight)
}()
// ...
}
If pieceSize
is ever called with a zero grid dimension, something is very wrong with my entire program. It's not a matter of testing for a runtime error; the program itself is based on faulty algorithms. That is what I want to detect.
Assert and Precondition parameters in swift
The two parameters are defaulted by swift compiler, so you do not need to supply any values for them. In fact, you shouldn't supply them to avoid incorrect reporting.
Below is an example of how a precondition failure is reported:
Note how the file name MyPlayground.playground
and line number 5
are filled in automatically.
Swift -Ounchecked and Assertions
You should definitely not compile -Ounchecked
for release in order to use precondition
only while testing. Compiling -Ounchecked
also disables checks for things like array out of bounds and unwrapping nil
, that could lead to some pretty nasty production bugs involving memory corruption.
You can control assertion behaviour independently of compiler optimization settings via the -assert-config
argument to swiftc
:
$ cat assert.swift
assert(false, "assertion asserted")
println("made it here...")
$ swiftc -Onone assert.swift; ./assert
assertion failed: assertion asserted: file assert.swift, line 1
Illegal instruction: 4
$ swiftc -O assert.swift; ./assert
made it here...
$ swiftc -O -assert-config Debug assert.swift; ./assert
assertion failed: assertion asserted: file assert.swift, line 1
Illegal instruction: 4
$
It doesn’t look like there’s an Xcode Build Settings toggle for this, but you can add using the “Other Swift Flags” setting.
Testing Swift code with preconditions
You test Swift methods that have preconditions by only testing behavior with inputs that meet those preconditions. The behavior when the precondition is not met is clear without further testing, since preconditions are baked into the standard library.
If you doubt you've correctly specified a precondition, you can pull out the precondition expression into a Boolean function, then test that for correctly discerning valid from invalid inputs.
However, you can create for yourself what you wish for here:
An option to generate exceptions on precondition/assertion failures would do the trick: one would turn it on in debug builds, and turn it off in release builds.
Instead of calling Apple's precondition
, you can write your own function, say, require
. This could be conditionally compiled to act as you wish. You would then use this everywhere you'd otherwise be using precondition
.
What's the difference between these two enum declarations in swift?
There is no difference between the two syntaxes - it is your personal preference to use one vs. the other.
Swift allows you to add separate case
s to a single enum
so that you could group enum
constants with similar associated values, e.g. like the book example of Barcode
:
enum Barcode {
case UPCA(Int, Int, Int, Int)
case QRCode(String)
}
For situations when you define "plain" enum
s with no associated values, either one of these two syntaxes would be a good choice.
Related Topics
Can the Conversion of a String to Data with Utf-8 Encoding Ever Fail
Implementing a Function with a Default Parameter Defined in a Protocol
Sprite Kit Physicsbody.Resting Behavior
How to Detect Absent Network Connection When Setting Firestore Document
Higher Order Function: "Cannot Invoke 'Map' with an Argument List of Type '((_) -> _)'"
Creating a Countableclosedrange<Character>
Non-Strong References Not Working in Playground
Why Does Swift Playground Shows Wrong Number of Executions
How to Make Physics Bodies Stick to Nodes Anchor Points
What Does "Get" Mean in a Protocol's Property Declaration
Declaration Is Only Valid at File Scope (Extension)
Swift: How to Delete Part of Audio
Beginner Swift Sprite Kit - Node Collision Detection Help (Skphysicscontact)
Swift Http Request Use Urlsession