Hmm... I can't help but think that with that logic, it seems like you should be asking "what's the point of the message ever, even in debug builds, since you're just going to debug it anyway?"
There are two major reasons:
1) I can clearly see what condition caused the crash, and dynamic related info, in the release build's crash log without having to jump through umpteen different hoops:
- Copy the symbol address in the crash log
- Copy the stride offset for the correct library for that address
- Go into Xcode
- Open the organizer
- Find the right build archive
- Go through the hassle of revealing the archive in Finder and navigating through 4 layers of folders to find the correct dsym file
- Open Terminal
- Lookup what atos's command options are again
- Stick in the path to the dsym and the addresses
- Get the file and line number from atos
- Go to my SCM, Checkout the right build (because how else are line numbers going to line up?)
- Find the right file
- Jump to the given line number
... and do that for each crash report. That's an insanely inefficient workflow.
(Tools can be made and do exist to make at least some of that simpler, but not everyone has the ability or the desire to make their own, or even use someone else's.)
I want to see the message and file etc in the crash log itself, without having to do aaaaaall of that work, much like an assert does in C/Obj-C already. The precondition message combined with the stack frame will uniquely point to exactly which precondition/assert failed, which means the exact line number is irrelevant. Unlike having to use atos to convert the address into a line number (which I swear is very often wrong by one line number), and being required to use scm to checkout the old build just so I can be (almost) sure which precondition failed, if the message is in the crash log, it's simply plain as day and I can look at it in the current version of the code, and be absolutely 100% certain I'm looking at the precondition that failed. It's vastly more convenient.
2) The second major reason is that the message parameter allows the message in the crash log to contain other related runtime values, which can help explain how to reproduce the bug. It's like focused site-of-the-crash logging. In Obj-C I've made very effective use of:
if (!precondition) {
StickAStringInTheCrashLog([NSString stringWithFormat:@"....", a, b, c]);
assert(precondition);
}
where the string gets inserted into "Application Specific Info" via use of:
static char *__crashreporter_info__ = nil;
asm(".desc ___crashreporter_info__, 0x10");
This has let me capture related values for hard-to-track assertion failures which give insight into how to recreate the conditions that led to the failure. In Obj-C it's necessary to use that entire if-statement because when an assert() fails the expression is printed literally in the log, meaning it's impossible to include actual values. In Swift, however, we are blessed with the behavior that precondition(), upon failure, will actually print the message parameter's string value, so that it can include dynamic values. eg:
func foo(x: Int) {
precondition(x < 3, "x is \(x) -- expected < 3")
...
}
Without including the message in the crash log in release builds, I can't know what the value of x was, which could be really really important. Maybe I can only possibly imagine that the unexpected value is 4, but for a particular user I can see directly in the crash log that the value is 5, which turns out to be mind blowingly useful information. Generally speaking, without introducing a dedicated log file or asking the user to go dig around in Console.app ——— both of which requires actual contact info for and participation from my user ——— I may never get this info, and related to a specific file. But if the message is in the crash log, which even anonymously is submitted to me, now I have it.
*Extremely* useful.
I'd be careful here—the contents of such messages may be a secrecy leak (not to mention code size bloat). I think it's probably still the right thing to do even in Release builds, but it's not obvious. (I guess we already have this behavior for fatalError, as you say.)