Skip to content

Fix launch crash on non-string Nightscout treatment timestamps - #759

Open
zersoft wants to merge 1 commit into
loopandlearn:devfrom
zerloop:fix/treatment-timestamp-crash
Open

zersoft wants to merge 1 commit into
loopandlearn:devfrom
zerloop:fix/treatment-timestamp-crash

Conversation

@zersoft

@zersoft zersoft commented Sep 26, 2026 •

Copy link
Copy Markdown

Summary

Loop Follow 7.1.0 (60) aborts on every launch when Nightscout returns a treatment whose timestamp field is present but not a string (JSON null or an epoch number). The bolus/SMB/carb/note/stats parsers check != nil and then as! String, so NSNull or NSNumber passes the nil check and traps in swift_dynamicCast on the main queue. The crash repeats on every launch as long as the record is inside the download window.

Crash signature (macOS 27, Designed for iPad build, also reproducible on iOS):

swift::swift_dynamicCastFailure → swift_dynamicCast → Loop Follow (processNS* via updateTreatments, DispatchQueue.main.async)
x24 = "timestam" (ASCII), x28 = value witness table for Date

Changes

  • Add NightscoutUtils.treatmentDate(from:): uses timestamp, falls back to created_at, accepts ISO 8601 strings and epoch milliseconds/seconds, returns nil (entry skipped) for anything else.
  • Use it in Treatments/Bolus.swift, SMB.swift, Carbs.swift, Notes.swift and the three parsers in Stats/StatsDataFetcher.swift, replacing the forced casts.
  • Replace the remaining forced casts of the same class with conditional binding: recommendedTempBasal.timestamp in DeviceStatusLoop/DeviceStatusOpenAPS, predicted.values / prediction.last! in DeviceStatusLoop, and as! TimeInterval / as! String in SAge, CAge, IAge.

Testing

  • New Tests/NightscoutTreatmentDateTests.swift (5 cases: string, created_at fallback, null fallback, numeric ms/s, unsupported values → nil).
  • Full Tests target: 102 passed, 0 failed (iOS 27 simulator).
  • SwiftFormat --lint clean.
  • Built and uploaded to TestFlight via the fork's build workflow (CI green). Real-device confirmation against the Nightscout instance that triggered the crash is pending and will be reported here.

🤖 Generated with Claude Code

Treatment parsers force-cast `timestamp`/`created_at` to String after a
`!= nil` check. A treatment carrying a JSON null or numeric timestamp
passes the nil check and traps in swift_dynamicCast on the main queue,
so the app aborts on every launch while that record is within the
download window.

Add NightscoutUtils.treatmentDate(from:) which accepts ISO 8601 strings,
epoch milliseconds/seconds, and falls back to created_at on null; use it
in the bolus, SMB, carb, note and stats parsers. Replace the remaining
forced casts in device status, SAge, CAge and IAge handling with
conditional binding so malformed data is skipped instead of crashing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@zersoft

zersoft commented Sep 26, 2026

Copy link
Copy Markdown
Author

Real-device confirmation: the TestFlight build with this fix (fork build of 7.1.0) launches and runs normally on the Mac and Nightscout instance that reproduced the crash on every launch before. No further crashes observed.

@bjorkert bjorkert left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Thanks for your contribution!

zersoft added a commit to zerloop/LoopFollow that referenced this pull request Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants