Failing fast is the decision to abort a running application when the application encounters a bad state. The decision, however, to abort a running application is often difficult. This post does not attempt to convince you to adopt a fail fast policy. Instead this post shows a way of using simple macros to make it easy to abort an iOS app with crash report logging.
Assert Expression
The BTSAssert
macro aborts the app if the given expression evaluates to false. The macro performs the following:
- evaluates the expression
- if true, nothing happens
- if false
- log a message
- abort the app using
abort()
This code is always on for debug and release versions. Why? Because bugs still exist in production/ release code, so we still want to fail-fast. The runtime cost is minimal.
#define BTSAssert(expression, ...) \
do { \
if (!(expression)) { \
NSString *__BTSAssert_log_string = [NSString stringWithFormat:@"Assertion failure: %s. %@", #expression, [NSString stringWithFormat:@"" __VA_ARGS__]]; \
__BTS_LOG(@"%@", __BTSAssert_log_string); \
abort(); \
} \
} while(0)
Example Usages:
BTSAssert(someCounter >= 5);
BTSAssert(someCounter >= 5, @"Counter is wrong.");
BTSAssert(someCounter >= 5, @"Counter is wrong: %d", someCounter);
Force Abort
The BTSFail
macro aborts the app. The macro performs the following:
- log a message
- abort the app using
abort()
This code is always on for debug and release versions. Why? Because bugs still exist in production/ release code, so we still want to fail-fast. The runtime cost is minimal.
#define BTSFail(...) \
do { \
NSString *__BTSFail_log_string = [NSString stringWithFormat:@"%@", [NSString stringWithFormat:@"" __VA_ARGS__]]; \
__BTS_LOG(@"%@", __BTSFail_log_string); \
abort(); \
} while(0)
Example Usages:
BTSFail();
BTSFail(@"Counter is wrong.");
BTSFail(@"Counter is wrong: %d", someCounter);
Intro To Crashlytics
Crashlytics is a very simple crash reporting tool for iOS and Android. The __BTS_LOG
macro used in BTSAssert
and BTSFail
enables logging to the Crashlytics API for release builds. Logging to Crashlytics, in my opinion, only makes sense for release builds. There are numerous reasons why. One reason is because there is no need to link Crashlytics with OCUnit test targets. Debug builds log to NSLog
.
Crash Report Logging
#ifdef DEBUG
#define __BTS_LOG(__FORMAT__, ...) NSLog((__FORMAT__), ##__VA_ARGS__);
#else
#define __BTS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#endif
The CLSLog
is part of the Crashlytics logging API. Crashlytics submits log statements with an iOS crash report. This means that Crashlytics combines a symbolicated crash report with the programmer provided abort message. Very nice!
Summary
- You should adopt a fail fast policy.
- You should write unit tests.
- You should try Crashlytics.