Skip to content

[No QA] Fix stale artifacts cache on iOS - #99185

Draft
mateuuszzzzz wants to merge 2 commits into
Expensify:mainfrom
software-mansion-labs:fix-stale-artifacts-cache-on-ios
Draft

[No QA] Fix stale artifacts cache on iOS#99185
mateuuszzzzz wants to merge 2 commits into
Expensify:mainfrom
software-mansion-labs:fix-stale-artifacts-cache-on-ios

Conversation

@mateuuszzzzz

@mateuuszzzzz mateuuszzzzz commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

CocoaPods caches our prebuilt React Native artifacts by their source URL. That URL carries only the
plain react-native version, so a new patches version lands at the same path and CocoaPods keeps the
old extraction. It may not even re-read the podspec that downloads the tarballs. The app then
silently builds against a stale artifact.

Upstream RN has the same issue with Debug and Release artifacts (the cache does not distinguish
them either) and solves it outside CocoaPods. Its [RNCore] build phase re-extracts the framework
whenever the .last_build_configuration marker does not match the current configuration. This PR
extends that pattern to the patches version.

During pod install, when the resolved version does not match the .artifacts-version stamp of
the tarballs in Pods, we drop CocoaPods' memoized podspec with sandbox.remove_local_podspec. This
forces a re-evaluation of the podspec, which re-runs podspec evaluation and the dSYM merge. The re-read
podspec is byte-identical, so Podfile.lock is unaffected.

During the build, a small prelude prepended into the [RNCore] phase compares the tarballs' stamp
with the extracted framework's stamp. On mismatch it writes stale into
.last_build_configuration, and RN's own script re-extracts from the fresh tarballs right after.
This reuses the upstream extraction machinery instead of duplicating it. It lives inside that phase
because CocoaPods sorts phases by name on save, so a separate phase could not run first.

The change covers patches version changes, the RCT_SYMBOLICATE_PREBUILT_FRAMEWORKS toggle, warm
sandboxes, removed Pods and interrupted builds. Debug and Release switching is untouched. When
nothing changed, both pieces are no-ops costing a couple of file reads.


Why this approach instead of a solution where we make CocoaPods differentiate between our patched versions? That would require significantly more effort and more patches to React Native internals. I’d prefer to keep our patched artifacts as close to upstream as possible to minimize the maintenance cost.

Fixed Issues

$ #99187
PROPOSAL:

MOBILE-EXPENSIFY: https://github.com/Expensify/Mobile-Expensify/pull/14066

Tests

Prerequisites: HybridApp iOS dev setup, npm i done, gh auth login (Maven access), with successful pod install + build on this branch with the latest artifacts version (latest main on both repos should be set).

1. Note the current artifact version

  • Run cat Mobile-Expensify/ios/Pods/ReactNativeCore-artifacts/.artifacts-version and note the version (e.g. 0.86.0-5) — referred to as <current> below

2. Force an older artifact version

  • In scripts/artifacts-utils/lib/artifactsResolver.ts, add as the first line of findMatchingArtifactsVersion:
    return '0.86.0-0'; // TEST ONLY — remove after

3. Run pod install and verify it invalidates

  • Run cd Mobile-Expensify/ios && bundle exec pod install
  • Verify the output contains [PatchedArtifacts] Using patched react-native artifacts: react-hybrid:0.86.0-0
  • Verify the output contains [PatchedArtifacts] Artifacts changed to 0.86.0-0; the React-Core-prebuilt podspec will be re-evaluated.
  • Verify cat Pods/ReactNativeCore-artifacts/.artifacts-version prints 0.86.0-0
  • Verify git -C .. diff iOS/Podfile.lock shows no new changes

4. Build and verify the extraction log

  • Build the app via Xcode to see detailed logs
  • In the build log, verify the line: [PatchedArtifacts] Extracted prebuilt React Core is '<current>', tarballs are '0.86.0-0' — marking for re-extraction.
  • Verify it is immediately followed by react-native's Extracting the tarball to temp dir …
  • Verify the build succeeds
  • Verify cat Pods/React-Core-prebuilt/.patched-version prints 0.86.0-0

5. Verify the no-op fast path

  • Build again without any changes
  • Verify the marking for re-extraction line does NOT appear (silent = versions match)
  • Verify there is no tarball extraction for React-Core-prebuilt

6. Revert and verify the round trip

  • Remove the hardcoded line from artifactsResolver.ts
  • Run cd Mobile-Expensify/ios && bundle exec pod install and verify the output contains Artifacts changed to <current> …
  • Build once more and verify the log line:
    … is '0.86.0-0', tarballs are '<current>' — marking for re-extraction. followed by the extraction
  • Verify cat Pods/React-Core-prebuilt/.patched-version is back to <current>
  • Verify app on works without crashing

Byte-level checks

  • Run dwarfdump --uuid Mobile-Expensify/ios/Pods/React-Core-prebuilt/React.xcframework/ios-arm64_x86_64-simulator/React.framework/React
  • Run dwarfdump --uuid <DerivedData>/Build/Products/Debug-iphonesimulator/Expensify.app/Frameworks/React.framework/React
  • Verify the arm64 UUIDs are equal and match the resolved version (0.86.0-0: 6F445E91-…, 0.86.0-5: 7AFB6212-…)

Note: versions -3/-4/-5 ship an identical iOS debug binary, so the optional UUID checks only discriminate with a genuinely different version like 0.86.0-0. Steps 3–6 (logs + stamps) work with any older version.
Note2: possibly on build using old artifact the app will crash due to wrong ABI. On the latest artifact version the app should work as expected.

Offline tests

QA Steps

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

@mateuuszzzzz mateuuszzzzz changed the title Fix stale artifacts cache on iOS [No QA] Fix stale artifacts cache on iOS Aug 21, 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.

1 participant