Repository navigation
Conversation
`+[GULAppEnvironmentUtil isAppStoreReceiptSandbox]` read `-[NSBundle appStoreReceiptURL]` on every call. That property makes a synchronous XPC call to the LaunchServices daemon, which can block the calling thread for seconds when the daemon is busy. `+isFromAppStore` repeated the call because it only cached `IsAppEncrypted()`. Firebase calls these from the main thread (heartbeat user agent, Messaging APNs token handling), which caused hangs and watchdog terminations. Compute both values once per process with `dispatch_once`. Their inputs (receipt type, bundle contents, Mach-O encryption info, and the Info.plist opt-out key) can't change while the app is running. The original bodies move unchanged into file-local helpers. Part of firebase/firebase-ios-sdk#16726
This was referenced Oct 3, 2026
Merged
paulb777
marked this pull request as ready for review
October 5, 2026 22:28
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.
Summary
Cache
+[GULAppEnvironmentUtil isAppStoreReceiptSandbox]and+[GULAppEnvironmentUtil isFromAppStore]for the lifetime of the process.Part of firebase/firebase-ios-sdk#16726.
Why
firebase/firebase-ios-sdk#16726 reports main-thread hangs in production, some
ending in watchdog terminations, on two Firebase paths that both end in
-[NSBundle appStoreReceiptURL]. That property is not a file lookup. It calls+[LSBundleProxy bundleProxyForCurrentProcess], which ends inxpc_connection_send_message_with_reply_sync, so the caller waits for theLaunchServices daemon. That can take seconds when the daemon is busy.
Before this change,
isAppStoreReceiptSandboxhad no cache.isFromAppStorecached only
IsAppEncrypted(). Every call from every caller (GULLogger,FirebaseCore's user agent, Messaging, Auth, Performance) repeated the XPC round
trip. All inputs (receipt type, bundle contents, Mach-O encryption info, and the
FirebaseAppStoreReceiptURLCheckEnabledInfo.plist key) are fixed for the lifeof the process.
Changes
file-local
IsFromAppStore()andIsAppStoreReceiptSandbox()helpers. Thepublic class methods wrap them in
dispatch_once.IsFromAppStore()still calls the now-cached+isAppStoreReceiptSandbox, sothe receipt URL is read at most once per process across both APIs.
dispatch_oncearoundIsAppEncrypted().Notes for reviewers
off the calling thread. The Firebase PRs move the callers off the main
thread (links below).
dispatch_oncecallers that arrive while the first computation is runningwait for it to finish. Previously they would each have made their own XPC
call.
Testing
testAppStoreReceiptURLIsReadOnlyOnce: after a warm-up call, it swizzles-[NSBundle appStoreReceiptURL]with a counter and asserts that neither APIreads it again. A sanity check confirms the swizzle intercepts reads. The test
fails on
main(receiptURLReadCountis 2) and passes with this change.xcodebuild -scheme GoogleUtilities-Package -destination 'platform=macOS' test: all suites pass (160UtilitiesUnittests plus the Swift and ObjCimport tests).
Related PRs