swiftenvironment-variablestimezoneenvironmentbug-reporting

Investigating a Crash Report: .environment .timeZone


I have a crash report and I can’t figure out how to fix it, probably the main issue is that it's the first time I receive a crash report so I‘m not that expert on using it! (…so please, be kind!)

Basically it's a bug that I can't replicate but Apple tells me that there're two iPhones around the world with this issue.

That’s the crash report: enter image description here

The red line brings to the .environment line in this code

@AppStorage("isLocalTime") var isLocalTime: Bool = false

HStack {
    DatePicker(selection: $selectedDate, label: { more code })
    Text(isLocalTime ? "LT" : "UTC")
        .frame(width: 44)
}
.environment(\.timeZone, TimeZone(abbreviation: isLocalTime ? TimeZone.current.abbreviation()! : "UTC")!)
.datePickerStyle(.automatic)

It is a line who sets the timezone of a date picker on device local time or UTC, according to a bool setting.

Luckily, I’m in contact with one of the users who has the issue and she reported me that, as soon as she launch the screen, the app crashes. I tried to set her timezone but that’s not the issue as it works smooth for me, what can be wrong? How can I replicate this bug?

Now that I'm writing here, I realised that the incriminated line should be below the DatePicker's code but, right now, it works anyway, so it should not be the cause of the bug I think...


Solution

  • abbreviation() is locale-sensitive - it will return different abbreviations depending on the region settings of your device. On the other hand, init(abbreviation:) just looks up the abbreviation in the TimeZone.abbreviationDictionary.

    See also: 1 and 2.

    It is possible that abbreviation() returns an abbreviation that is not in TimeZone.abbreviationDictionary, causing the init(abbreviation:) to fail.

    In any case though, using the abbreviation is a very wrong way to round-trip a timezone. The round-trip fails most of the time, because the same abbreviation can correspond to multiple different timezones (See 2). You should just write

    isLocalTime ? TimeZone.current : TimeZone.gmt
    

    To reproduce the crash, simply look through this list and find an abbreviation that is not in TimeZone.abbreviationDictionary. The first one I found is HDT, corresponding to America/Adak, which is somewhere in Alaska. Set the region of the device to United States, and set the current time to some time in summer (since this is the abbreviation for daylight saving time). Then,

    TimeZone(abbreviation: TimeZone(identifier: "America/Adak")!.abbreviation()!)!
    

    will crash because TimeZone.init(abbreviation:) returns nil.