Description
Calling FirebaseApp.configure() changes the locale that Foundation's current Calendar resolves to. Before the call, Calendar.current.locale matches the device's locale (en_CA for most of my testing, but also observed with other locales). After the call, it becomes en_001 (English – World), which silently discards the user's Settings → General → Language & Region overrides — most visibly First Day of Week.
Notably, Locale.current is not affected. It remains en_CA throughout. Only the Calendar's locale changes, leaving Foundation in a self-inconsistent state where Locale.current and Calendar.current.locale disagree.
Both Calendar.current and Calendar.autoupdatingCurrent are affected.
Environment
Physical test device (iPad (A16) running iPadOS 26.6.1) set to Region: Canada (en_CA), with Settings → General → Language & Region → First Day of Week set to Wednesday (a non-default value, chosen so the regression is unambiguous — en_CA defaults to Sunday/1 and en_001 defaults to Monday/2, so a value of 4 can't be confused with either default). The issue also occurs with the default Sunday first day of week.
Also reproducible in a simulator.
Expected
FirebaseApp.configure() should have no effect on locale or calendar resolution.
Calendar.current.locale should remain en_CA and firstWeekday should remain 4
(the user's configured value).
Actual
Immediately after FirebaseApp.configure() returns, the calendar's locale is
en_001 and firstWeekday has reset to 2, discarding the user's setting.
|
before configure() |
after configure() |
Locale.current |
en_CA |
en_CA ✅ |
Calendar.current.locale |
en_CA |
en_001 ❌ |
Calendar.current.firstWeekday |
4 |
2 ❌ |
Calendar.autoupdatingCurrent.locale |
en_CA |
en_001 ❌ |
Calendar.autoupdatingCurrent.firstWeekday |
4 |
2 ❌ |
Impact
This is process-wide and permanent for the lifetime of the app, and it is silent — there is no error or warning. Any code relying on the current calendar is affected: calendar/date-picker UI renders the wrong first day of week, and every DateFormatter that hasn't had an explicit locale/calendar assigned inherits the wrong values. Users who have deliberately configured their regional settings see them ignored.
Important caveat: observer effect when investigating this
Reading Locale.current forces Foundation to resolve and cache locale state for the remainder of the process. If anything reads it before FirebaseApp.configure() runs, the corruption does not occur — the early read masks the bug entirely.
This makes the issue easy to accidentally "fix" while instrumenting it: adding a log statement before configure() to investigate causes the symptom to disappear. Any diagnostic must read the calendar/locale exactly once, after configure(), in a fresh process.
(This also means a one-line _ = Locale.current before configure() works as a
functional workaround, which is what we're currently using.)
Reproducing the issue
Device setup (required)
- Set the device Region to a region with a locale where the day of the week doesn't start on Monday — we used Canada (
en_CA), but also reproducible in en_US and other languages.
- Settings → General → Language & Region → First Day of Week → Wednesday. (Deliberately non-default:
en_CA defaults to Sunday/1 and en_001 defaults to Monday/2, so observing 4 vs 2 is unambiguous.)
Critical: the observer effect
Reading Locale.current forces Foundation to resolve and cache locale state for the rest of the process, which masks this bug entirely. To reproduce, the calendar must be read exactly once, after FirebaseApp.configure(), in a fresh process.
Two consequences:
- Do not add any logging before
configure() "to compare" — doing so makes the symptom disappear.
- Each data point requires its own cold launch. You cannot log before and after in the same run and get a valid "after" reading.
Steps
In application(_:didFinishLaunchingWithOptions:), with no prior read of Locale.current or Calendar anywhere earlier in the launch path:
FirebaseApp.configure()
print("locale: \(Locale.current)")
print("calendar: \(Calendar.current)")
Cold-launch on a device configured as above.
Expected: calendar locale en_CA, firstWeekday: 4
Actual: calendar locale en_001, firstWeekday: 2 — while Locale.current
correctly remains en_CA
To confirm the boundary, run again in a fresh process with the two prints moved to immediately before FirebaseApp.configure() instead. That run reports:
en_CA / firstWeekday: 4.
About a minimal reproducible example
We were unable to isolate this into a minimal app, despite substantial effort. In our production app it reproduces 100% of the time, and bisecting the launch sequence one checkpoint per cold launch places the transition precisely at FirebaseApp.configure() — the checkpoint immediately before it reports en_CA/4,
and the checkpoint immediately after reports en_001/2, with no other code executing between them.
A fresh single-view app calling FirebaseApp.configure() does not reproduce it. We built one and progressively made it match our production app, testing on the same physical device after each change. None of the following reproduced the bug:
- Linking FirebaseCore + FirebaseAnalytics + FirebaseCrashlytics (exactly matching our app's Firebase product set)
- Identical resolved versions of every transitive dependency (GoogleUtilities 8.1.2, GoogleDataTransport 10.1.1, GoogleAppMeasurement 12.17.0, grpc-binary 1.69.1, nanopb, abseil-cpp-binary, promises, etc.)
- Matching bundle ID and the same GoogleService-Info.plist
- Matching CFBundleDevelopmentRegion / CFBundleLocalizations, plus physically shipping the same 7 .lproj localizations
- Classic UIKit lifecycle (main.swift → UIApplicationMain → AppDelegate → UIWindowSceneDelegate) rather than the SwiftUI App lifecycle
- Sleeping the main thread 1s and 5s after configure() before reading, to rule out a race with Analytics' asynchronous startup
We also confirmed FirebaseAppDelegateProxyEnabled = NO does not prevent the bug
in the production app, ruling out GULAppDelegateSwizzler.
This suggests the trigger depends on something about the larger binary — our app links a large number of frameworks and Swift packages — rather than on the configure() call in isolation. We're happy to run further diagnostics on the real app if you can suggest what would be most useful to capture.
Firebase SDK Version
12.17.0
Xcode Version
26.6
Installation Method
Swift Package Manager
Firebase Product(s)
Crashlytics, Analytics
Targeted Platforms
iOS
Relevant Log Output
[Firebase/Crashlytics] Version 12.17.0
locale after firebase: en_US (autoupdating en_US)
calendar after firebase: gregorian (gregorian) locale: en_001 time zone: America/Vancouver firstWeekday: 2 minDaysInFirstWeek: 1
If using Swift Package Manager, the project's Package.resolved
Filtered to the Firebase dependency graph only — the full file contains internal private repository URLs. All 13 Firebase/Google-related pins are included verbatim; the omitted entries are unrelated third-party and internal packages.
Expand Package.resolved (Firebase graph)
{
"pins": [
{
"identity": "abseil-cpp-binary",
"kind": "remoteSourceControl",
"location": "https://github.com/google/abseil-cpp-binary.git",
"state": {
"revision": "bbe8b69694d7873315fd3a4ad41efe043e1c07c5",
"version": "1.2024072200.0"
}
},
{
"identity": "app-check",
"kind": "remoteSourceControl",
"location": "https://github.com/google/app-check.git",
"state": {
"revision": "3e33dd27dd4c69bd81c7c81fe61d8ccf58846902",
"version": "11.3.1"
}
},
{
"identity": "firebase-ios-sdk",
"kind": "remoteSourceControl",
"location": "https://github.com/firebase/firebase-ios-sdk.git",
"state": {
"revision": "33a468adfdb75b53f05a37e7c886ca7c962b5c17",
"version": "12.17.0"
}
},
{
"identity": "google-ads-on-device-conversion-ios-sdk",
"kind": "remoteSourceControl",
"location": "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk",
"state": {
"revision": "dc39082d8881109d35b94b1c122164c0e8d08a55",
"version": "3.6.1"
}
},
{
"identity": "googleappmeasurement",
"kind": "remoteSourceControl",
"location": "https://github.com/google/GoogleAppMeasurement.git",
"state": {
"revision": "fceaffa07d22dcd5624d3639fd970351a4a5ad8c",
"version": "12.17.0"
}
},
{
"identity": "googledatatransport",
"kind": "remoteSourceControl",
"location": "https://github.com/google/GoogleDataTransport.git",
"state": {
"revision": "ba3358d3c3dbae8ef230b58a46b97ad65e84e974",
"version": "10.1.1"
}
},
{
"identity": "googleutilities",
"kind": "remoteSourceControl",
"location": "https://github.com/google/GoogleUtilities.git",
"state": {
"revision": "9f183ae842be978784f2963a343682e0c46d8fb3",
"version": "8.1.2"
}
},
{
"identity": "grpc-binary",
"kind": "remoteSourceControl",
"location": "https://github.com/google/grpc-binary.git",
"state": {
"revision": "75b31c842f664a0f46a2e590a570e370249fd8f6",
"version": "1.69.1"
}
},
{
"identity": "gtm-session-fetcher",
"kind": "remoteSourceControl",
"location": "https://github.com/google/gtm-session-fetcher.git",
"state": {
"revision": "724a52eea6329b7e12d3ad8300d76ca9f3895fcc",
"version": "5.3.1"
}
},
{
"identity": "interop-ios-for-google-sdks",
"kind": "remoteSourceControl",
"location": "https://github.com/google/interop-ios-for-google-sdks.git",
"state": {
"revision": "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe",
"version": "101.0.0"
}
},
{
"identity": "leveldb",
"kind": "remoteSourceControl",
"location": "https://github.com/firebase/leveldb.git",
"state": {
"revision": "a0bc79961d7be727d258d33d5a6b2f1023270ba1",
"version": "1.22.5"
}
},
{
"identity": "nanopb",
"kind": "remoteSourceControl",
"location": "https://github.com/firebase/nanopb.git",
"state": {
"revision": "3851d94a41890dea16dc3db34caf60e585cb4163",
"version": "2.30910.1"
}
},
{
"identity": "promises",
"kind": "remoteSourceControl",
"location": "https://github.com/google/promises.git",
"state": {
"revision": "f4a19a3c313dc2616c70bb49d29a799fb16be837",
"version": "2.4.1"
}
}
],
"version": 3
}
</details>
### If using CocoaPods, the project's Podfile.lock
<!--- Look below for instructions on how to share your Podfile.lock. --->
<details>
<summary>Expand <code>Podfile.lock</code> snippet</summary>
<br>
```yml
Replace this line with the contents of your Podfile.lock!
Description
Calling
FirebaseApp.configure()changes the locale that Foundation's currentCalendarresolves to. Before the call,Calendar.current.localematches the device's locale (en_CAfor most of my testing, but also observed with other locales). After the call, it becomesen_001(English – World), which silently discards the user's Settings → General → Language & Region overrides — most visibly First Day of Week.Notably,
Locale.currentis not affected. It remainsen_CAthroughout. Only theCalendar's locale changes, leaving Foundation in a self-inconsistent state whereLocale.currentandCalendar.current.localedisagree.Both
Calendar.currentandCalendar.autoupdatingCurrentare affected.Environment
Physical test device (iPad (A16) running iPadOS 26.6.1) set to Region: Canada (
en_CA), with Settings → General → Language & Region → First Day of Week set to Wednesday (a non-default value, chosen so the regression is unambiguous —en_CAdefaults to Sunday/1 anden_001defaults to Monday/2, so a value of 4 can't be confused with either default). The issue also occurs with the default Sunday first day of week.Also reproducible in a simulator.
Expected
FirebaseApp.configure()should have no effect on locale or calendar resolution.Calendar.current.localeshould remainen_CAandfirstWeekdayshould remain 4(the user's configured value).
Actual
Immediately after
FirebaseApp.configure()returns, the calendar's locale isen_001andfirstWeekdayhas reset to 2, discarding the user's setting.configure()configure()Locale.currenten_CAen_CA✅Calendar.current.localeen_CAen_001❌Calendar.current.firstWeekdayCalendar.autoupdatingCurrent.localeen_CAen_001❌Calendar.autoupdatingCurrent.firstWeekdayImpact
This is process-wide and permanent for the lifetime of the app, and it is silent — there is no error or warning. Any code relying on the current calendar is affected: calendar/date-picker UI renders the wrong first day of week, and every
DateFormatterthat hasn't had an explicitlocale/calendarassigned inherits the wrong values. Users who have deliberately configured their regional settings see them ignored.Important caveat: observer effect when investigating this
Reading
Locale.currentforces Foundation to resolve and cache locale state for the remainder of the process. If anything reads it beforeFirebaseApp.configure()runs, the corruption does not occur — the early read masks the bug entirely.This makes the issue easy to accidentally "fix" while instrumenting it: adding a log statement before
configure()to investigate causes the symptom to disappear. Any diagnostic must read the calendar/locale exactly once, afterconfigure(), in a fresh process.(This also means a one-line
_ = Locale.currentbeforeconfigure()works as afunctional workaround, which is what we're currently using.)
Reproducing the issue
Device setup (required)
en_CA), but also reproducible inen_USand other languages.en_CAdefaults to Sunday/1 anden_001defaults to Monday/2, so observing4vs2is unambiguous.)Critical: the observer effect
Reading
Locale.currentforces Foundation to resolve and cache locale state for the rest of the process, which masks this bug entirely. To reproduce, the calendar must be read exactly once, afterFirebaseApp.configure(), in a fresh process.Two consequences:
configure()"to compare" — doing so makes the symptom disappear.Steps
In
application(_:didFinishLaunchingWithOptions:), with no prior read ofLocale.currentorCalendaranywhere earlier in the launch path:Cold-launch on a device configured as above.
Expected: calendar locale en_CA, firstWeekday: 4
Actual: calendar locale en_001, firstWeekday: 2 — while Locale.current
correctly remains en_CA
To confirm the boundary, run again in a fresh process with the two prints moved to immediately before FirebaseApp.configure() instead. That run reports:
en_CA / firstWeekday: 4.
About a minimal reproducible example
We were unable to isolate this into a minimal app, despite substantial effort. In our production app it reproduces 100% of the time, and bisecting the launch sequence one checkpoint per cold launch places the transition precisely at
FirebaseApp.configure()— the checkpoint immediately before it reports en_CA/4,and the checkpoint immediately after reports en_001/2, with no other code executing between them.
A fresh single-view app calling
FirebaseApp.configure()does not reproduce it. We built one and progressively made it match our production app, testing on the same physical device after each change. None of the following reproduced the bug:We also confirmed FirebaseAppDelegateProxyEnabled = NO does not prevent the bug
in the production app, ruling out GULAppDelegateSwizzler.
This suggests the trigger depends on something about the larger binary — our app links a large number of frameworks and Swift packages — rather than on the configure() call in isolation. We're happy to run further diagnostics on the real app if you can suggest what would be most useful to capture.
Firebase SDK Version
12.17.0
Xcode Version
26.6
Installation Method
Swift Package Manager
Firebase Product(s)
Crashlytics, Analytics
Targeted Platforms
iOS
Relevant Log Output
[Firebase/Crashlytics] Version 12.17.0 locale after firebase: en_US (autoupdating en_US) calendar after firebase: gregorian (gregorian) locale: en_001 time zone: America/Vancouver firstWeekday: 2 minDaysInFirstWeek: 1If using Swift Package Manager, the project's Package.resolved
Filtered to the Firebase dependency graph only — the full file contains internal private repository URLs. All 13 Firebase/Google-related pins are included verbatim; the omitted entries are unrelated third-party and internal packages.
Expand
Package.resolved(Firebase graph){ "pins": [ { "identity": "abseil-cpp-binary", "kind": "remoteSourceControl", "location": "https://github.com/google/abseil-cpp-binary.git", "state": { "revision": "bbe8b69694d7873315fd3a4ad41efe043e1c07c5", "version": "1.2024072200.0" } }, { "identity": "app-check", "kind": "remoteSourceControl", "location": "https://github.com/google/app-check.git", "state": { "revision": "3e33dd27dd4c69bd81c7c81fe61d8ccf58846902", "version": "11.3.1" } }, { "identity": "firebase-ios-sdk", "kind": "remoteSourceControl", "location": "https://github.com/firebase/firebase-ios-sdk.git", "state": { "revision": "33a468adfdb75b53f05a37e7c886ca7c962b5c17", "version": "12.17.0" } }, { "identity": "google-ads-on-device-conversion-ios-sdk", "kind": "remoteSourceControl", "location": "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk", "state": { "revision": "dc39082d8881109d35b94b1c122164c0e8d08a55", "version": "3.6.1" } }, { "identity": "googleappmeasurement", "kind": "remoteSourceControl", "location": "https://github.com/google/GoogleAppMeasurement.git", "state": { "revision": "fceaffa07d22dcd5624d3639fd970351a4a5ad8c", "version": "12.17.0" } }, { "identity": "googledatatransport", "kind": "remoteSourceControl", "location": "https://github.com/google/GoogleDataTransport.git", "state": { "revision": "ba3358d3c3dbae8ef230b58a46b97ad65e84e974", "version": "10.1.1" } }, { "identity": "googleutilities", "kind": "remoteSourceControl", "location": "https://github.com/google/GoogleUtilities.git", "state": { "revision": "9f183ae842be978784f2963a343682e0c46d8fb3", "version": "8.1.2" } }, { "identity": "grpc-binary", "kind": "remoteSourceControl", "location": "https://github.com/google/grpc-binary.git", "state": { "revision": "75b31c842f664a0f46a2e590a570e370249fd8f6", "version": "1.69.1" } }, { "identity": "gtm-session-fetcher", "kind": "remoteSourceControl", "location": "https://github.com/google/gtm-session-fetcher.git", "state": { "revision": "724a52eea6329b7e12d3ad8300d76ca9f3895fcc", "version": "5.3.1" } }, { "identity": "interop-ios-for-google-sdks", "kind": "remoteSourceControl", "location": "https://github.com/google/interop-ios-for-google-sdks.git", "state": { "revision": "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", "version": "101.0.0" } }, { "identity": "leveldb", "kind": "remoteSourceControl", "location": "https://github.com/firebase/leveldb.git", "state": { "revision": "a0bc79961d7be727d258d33d5a6b2f1023270ba1", "version": "1.22.5" } }, { "identity": "nanopb", "kind": "remoteSourceControl", "location": "https://github.com/firebase/nanopb.git", "state": { "revision": "3851d94a41890dea16dc3db34caf60e585cb4163", "version": "2.30910.1" } }, { "identity": "promises", "kind": "remoteSourceControl", "location": "https://github.com/google/promises.git", "state": { "revision": "f4a19a3c313dc2616c70bb49d29a799fb16be837", "version": "2.4.1" } } ], "version": 3 } </details> ### If using CocoaPods, the project's Podfile.lock <!--- Look below for instructions on how to share your Podfile.lock. ---> <details> <summary>Expand <code>Podfile.lock</code> snippet</summary> <br> ```yml Replace this line with the contents of your Podfile.lock!