The other day I was debugging a crash in a UI test for an open pull request at work. The bug turned out to be extremely subtle and difficult to notice. I spent way too much time staring at the changes, trying to understand what was wrong. Let’s see if you can spot the error.
Here’s the problematic line:
The details here don’t matter. This is some legacy JSON serialization code, predating the introduction of
Codable (SE-0166 and SE-0167). This function serializes the object to a JSON dictionary,
self.dateClosed is a
Date type, and
JSONKeys.dateClosed is a
But what’s the bug? Let’s look at the definition of
toMongoDate (also some legacy code).
Seems fine, right? Everything compiles. There’s no issue with putting a
[String: Any] dictionary as the value in another
[String: Any] dictionary.
Any can be any type. But that’s what the problem turned out to be.
Let’s look at that line again:
self.dateClosed?.toMongoDate. This returns the function
toMongoDate. That is, the reference type
() -> [String: Any] — not the result of calling the function. I forgot the parentheses
(). That line should read
self.dateClosed?.toMongoDate(). However, this worked and the compiler did not complain because functions are first class types, and setting a function as the value of a
[String: Any] dictionary is valid. This is clearly an argument for adopting
Codable, which would have prevented this mistake.
What’s worse: this exact error has happened in our code base in at least one other scenario. It’s so easy to overlook.