4

Debugging a subtle Swift bug that will make you facepalm

 3 years ago
source link: https://www.jessesquires.com/blog/2018/11/07/debugging-subtle-swift-bug-facepalm/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

07 Nov 2018

software-dev    debugging, ios, swift, xcode

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:

func toDictionary() -> [String: Any] {
    var dict: [String: Any] = [:]

    // code setting other keys and values...

    dict[JSONKeys.dateClosed] = self.dateClosed?.toMongoDate

    return dict
}

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 String constant.

But what’s the bug? Let’s look at the definition of toMongoDate (also some legacy code).

extension Date {
    func toMongoDate() -> [String: Any] {
        // return date in expected mongo date format
    }
}

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.

Much faceplam.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK