Fix locale-dependent inbox date serialization breaking the inbox API - #8
Open
fa0311 wants to merge 2 commits into
Open
Fix locale-dependent inbox date serialization breaking the inbox API#8fa0311 wants to merge 2 commits into
fa0311 wants to merge 2 commits into
Conversation
…lt-tolerant Inbox message dates were serialized with locale- and timezone-sensitive formatters on both platforms and parsed with an unguarded DateTime.parse on the Dart side, so a single unexpected date string or value could break the entire inbox API. iOS (InboxUtility.m): - The NSDateFormatter had no locale or timezone. Per Apple QA1480, a device with a 12-hour clock rewrites the "HH" pattern, producing strings like "2026-05-02 3:56:00 AM" (the inconsistent sendDateUtc format reported in PR salesforce-marketingcloud#7), and non-Gregorian/non-Latin-digit locales produce strings Dart cannot parse at all. The formatter now uses en_US_POSIX, UTC and a fixed ISO-8601 pattern (yyyy-MM-dd'T'HH:mm:ss'Z'), shared via dispatch_once. - convertDatesInMessage now converts every top-level NSDate in the message instead of a hardcoded three-key whitelist, so a date field added by a future SDK version cannot reach NSJSONSerialization unconverted (the crash class behind PR salesforce-marketingcloud#4's endDateUtc report). - convertDictionaryToJSONString and convertCustomObjectInMessage now check [NSJSONSerialization isValidJSONObject:] first; a non-JSON-safe message degrades to nil (skipped by callers) instead of throwing an uncatchable NSInvalidArgumentException and crashing the app. Android (InboxUtils.kt): - SimpleDateFormat had no Locale or TimeZone, so *Utc fields carried device-local wall time, and locales with non-Latin digits (ar, fa, bn) or a Buddhist calendar (th) emitted strings DateTime.parse rejects, breaking getMessages() entirely. It now uses Locale.US, UTC and the same ISO-8601 pattern as iOS so both platforms emit identical strings. Dart: - InboxMessage.fromJson now parses dates leniently: DateTime.tryParse first, then a fallback for the legacy formats already in the wild ("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm:ss.SSS" and the 12-hour "yyyy-MM-dd h:mm:ss a Z" iOS variant), returning null instead of throwing on unparseable values. - _parseMessages and the onInboxMessagesChanged handler now wrap per-message parsing in try/catch, so one malformed message is logged and skipped instead of failing the whole getMessages/getReadMessages/getUnreadMessages/getDeletedMessages Future or silently suppressing inbox listeners. Adds unit tests covering all supported date formats (including 12-hour AM/PM edge cases and offsets) and the malformed-message-skip behavior for both the fetch and listener paths. Fixes the root cause behind upstream PRs salesforce-marketingcloud#7 and salesforce-marketingcloud#4.
nakasyou
approved these changes
Jul 19, 2026
nakasyou
approved these changes
Jul 19, 2026
…yParse The rescue regex only recovered English AM/PM strings, while the same legacy iOS formatter emitted locale-dependent forms (Japanese 午前/午後, non-Latin digits, non-Gregorian years) that can never be enumerated exhaustively. Those strings crashed the old unguarded DateTime.parse anyway, so returning null for every pre-ISO string is not a regression, and every format both native bridges actually emit today (ISO-8601 UTC, plus the legacy 24-hour space-separated forms) is already valid DateTime.tryParse input.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The inbox date formatters on both platforms depend on the device locale and timezone. On an iPhone set to a 12-hour clock the formatter emits
2026-05-02 3:56:00 AM(Apple QA1480), whichDateTime.parserejects. Parsing runs in a single.map()over the whole list, so one bad date makesgetMessages()and the other inbox getters throw, and inbox listeners silently stop firing. Android has the same defect with non-Latin-digit locales (ar, th, ...), and its*Utcfields actually carry local wall time.Both bridges now emit
yyyy-MM-dd'T'HH:mm:ss'Z'with a fixed locale and UTC, and the Dart side parses leniently (keeping a fallback for the old formats) and skips a malformed message instead of failing the whole list.Related to #7, which fixes the iOS formatter the same way but doesn't touch the Android side or the all-or-nothing parsing. This also makes the
endDateUtcfix from #4 future-proof: every NSDate in the payload gets converted, and NSJSONSerialization is guarded so an unconverted value can no longer crash the app.11 of the new tests fail on main as it is today.