If you saw my tweet from earlier today then you’ll already know the punchline to this particular bug report. But for those of you who don’t follow my every dumb online comment, I present to you the strangest corner case I’ve come across in fifteen years of professional development.
The app I work on during my day job was originally written in Swift in 2016, but some of the larger framework components are older Objective-C code dating back to 2012. The major development focus since I joined the project earlier this year has been on rewriting the UI layer. And given all the upheaval that in itself would cause, we’ve left the majority of the lower level code that talks to our API alone. No reason to throw out working, battle-tested code, right?
Anyway, a week or so ago we received a bug report from a customer who said the app crashed shortly after launch every time. The report they submitted was verbatim as follows…
Change the phone reason to Rebecca Stand and the app will crash
That didn’t give us much to go on, and QA wasn’t seeing any crashes on launch in their testing. And the logs coming to us from our crash reporting service didn’t show any bugs consistently crashing early in the app’s lifecycle. So, we filed the ticket away and moved on with the sprint.
Not to be deterred (and I don’t blame them), the customer got in touch with our 800 number and eventually got escalated to the head of QA.
Fast forward to today. He (the QA head) drives two hours outside Boston to visit the customer in person and install a special TestFlight build with extra logging onto their device.
He reproduces the crash. Every. Single. Time.
Being there in person with a debug build, QA is able to email me the log files directly, and every crash points to the same code…
let df = DateFormatter()
let someDate = df.date(from: dateStringFromServer)!
First off, yes. The problem is obviously that the code is force-unwrapping a string coming back from an API request. That’s a Bad Idea.
In mine and my coworkers’ defense, this comes from that older layer of code we inherited from another company. This is nothing we’ve ever really touched because, previously, it had always Just Worked.
Also, in defense of whoever did write the code originally, there was a contract in place with the API that guaranteed the string coming from the server was in the correct format. Sure, it’s still playing fast and loose, but, meh. I’ve certainly done worse things.
But, still. Why wasn’t it crashing for us in our testing? The customer graciously even let us log into the app as them, and we still couldn’t reproduce it on our devices.
I thought back to their original bug report…
Change the phone reason to Rebecca Stand and the app will crash
Who is Rebecca Stand? What is “the phone reason”?
And then I stopped thinking about what line of code was crashing and started thinking more closely about how it could crash.
Apple’s documentation for that method says…
Returns a date representation of a given string interpreted using the receiver’s current settings.
The “receiver’s current settings”.
“Rebecca Stand”.
I called the QA guy still at the customer’s home. “Open up Settings.app and check their date and time settings, language, etc.”
30 seconds of silence and then…
“Phone is set to English. They’re using military time. And, oh. Huh. His phone’s region is…Uzbekistan.”
Phone region. Phone reason.
Uzbekistan. Rebecca Stand.
The customer dictated their bug report to us via email using Siri. They speak English, live outside Boston, but their phone’s region was set to Uzbekistan instead of United States (or anything else more common).
The string coming from the server was the same format it had always been. But this was the first time (to our knowledge) our NSDateFormatter
had tried to parse it as an Uzbekistanian format, couldn’t do it, returned nil
, and crashed.
I’m not sure what the moral of this bug report is. Maybe to always program defensively? To take the time to really listen to your customers?
Anyway. That was my Monday.