swiftdebuggingerror-handlingassertionsforced-unwrapping

When should you use assertions and preconditions and when you can use guard statements, forced unwrapping and error handling?


I've already read Difference between “precondition” and “assert” in swift. But still can't draw a clear line between the (different ways of unwrapping ie guard & ! + error handling) vs assertions.

If I want my application to no longer work, can't I just force unwrap something and substitute as for a precondition?

  1. Is it because we want to stop/exit the app and basically don't want any control flow or state changing and therefore we use asserts/preconditions which also happens to come with easy logging of human readable messages (helps us to not constantly write prints)?
  2. Things that we use asserts for have are vital, guard statements are eventually a control flow system that even if your function returns early doesn't necessarily mean your app should crash.

And if it's anything beyond nils like you want a String and the user is giving you an Int then you can use error handling.

EDIT:

I'm not after opinions, I'm asking this only to understand what convenience assertions provide over the mentioned alternatives. The numbered list is the core of my question.


Solution

  • 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.