Files
safe-wallet/docs/solutions/workflow-issues/direct-native-file-edits-in-expo-project-20260319.md
74e410bb67 chore(mobile): upgrade Tamagui from v1 to v2 (#7497)
* chore(mobile): upgrade Tamagui from v1 to v2

Upgrade all @tamagui/* packages from v1 (^1.126.3) to v2 (2.0.0-rc.26).
Replace deprecated @tamagui/animations-moti with animations-reanimated.
Rewrite SafeButton to use v2 compound component pattern (Button.Text)
with correct font weight, disabled state, and icon coloring.
Patch @tamagui/image for style override bug (upstream fix merged but
not yet on npm: https://github.com/tamagui/tamagui/pull/3966).

* fix(mobile): resolve Tamagui tokens in Loader before passing to SVG

The Loader component passes color directly to react-native-progress
(CircleSnail), which uses react-native-svg internally. SVG doesn't
understand Tamagui theme tokens like "$color", causing repeated
"not a valid color or brush" console warnings.

* fix(mobile): center nav bar title in scrollable headers

Remove flex: 1 from headerTitle Animated.View so the native stack
header centers the title element itself. Add headerTitleAlign: 'center'
and match right spacer width (40px) to the back button circle.

* fix(mobile): fix Tamagui v2 component API breakages

- SafeButton: rewrite with Button.Text compound pattern, add textColor
  prop for forced light-mode overrides, restore fontWeight 700 default
- SafeInput: replace removed `editable` prop with `readOnly`, use
  GetProps<StyledInput> to fix type conflicts, cast theme tokens
- SafeCard: use `src` instead of `source`, `objectFit` instead of
  `resizeMode` for Tamagui v2 Image
- ParametersButton: migrate from raw Button to SafeButton
- Badge: fix dark mode background token (backgroundMain → borderLight)
- Raw Buttons: move fontWeight to Button.Text in ImportSigner/Ledger
- Onboarding: migrate color prop to textColor for dark mode fix
- Assets errors: use secondary variant + textColor instead of inline bg

* fix(mobile): preserve element children and text-button accent color in SafeButton

- Only wrap string/number children in Button.Text; render element
  children (ActivityIndicator, Loader) directly to avoid invalid
  text-child nesting on native.
- Use theme.primary for the text variant so text-style buttons
  retain their accent color instead of blending with body text.

* fix(mobile): replace raw Tamagui Button with SafeButton in ImportSuccess

The Copy button on the signer import success screen was broken after the
Tamagui v2 upgrade because it used a raw Tamagui Button. Replace it with
SafeButton (secondary, $sm) for consistent styling and v2 compatibility.

* fix(mobile): fix Jest transform config for Tamagui v2 and update snapshots

Add tamagui and @tamagui/* to transformIgnorePatterns so Jest can parse
the ESM exports in Tamagui v2. Update 22 snapshots reflecting the new
style output and remove stale fontFamily assertion in DataRow test.

---------

Co-authored-by: Hanzo Dev <[email protected]>
2026-03-20 16:39:59 +01:00

3.8 KiB

module, date, problem_type, component, symptoms, root_cause, resolution_type, severity, tags
module date problem_type component symptoms root_cause resolution_type severity tags
Mobile 2026-03-19 workflow_issue development_workflow
Podfile modified directly but changes lost on next prebuild/native regeneration
Expo plugin config.ts updated but dist/ not rebuilt, so changes not applied
MMKV pod pinned to master in Podfile instead of through Expo config plugin
missing_workflow_step workflow_improvement high
expo
podfile
native-generation
expo-plugins
build-step
ios
mobile

Direct Native File Edits in Expo Project

Problem

During an upgrade of react-native-quick-crypto and react-native-mmkv, the iOS Podfile was modified directly to:

  1. Remove a pod 'MMKV' line
  2. Add a post_install block with MMKV_IOS_EXTENSION preprocessor macro
  3. Change the MMKV pod version pin

These changes were made to the generated apps/mobile/ios/Podfile rather than through the Expo config plugin that generates it (expo-plugins/notification-service-ios).

Additionally, when the Expo plugin source (plugin/config.ts) was correctly updated, the build step (yarn build in the plugin directory) was not run, so the compiled output in dist/ was stale and the changes were not applied during the next prebuild.

Root Cause

Two workflow gaps:

  1. Expo continuously regenerates native files. The ios/ and android/ directories are generated artifacts in an Expo managed/custom dev client workflow. Direct edits to Podfile, AppDelegate, Info.plist, etc. will be overwritten on the next expo prebuild. All native configuration must flow through Expo config plugins or app.config.ts.

  2. Expo plugins with a build step require compilation. The notification-service-ios plugin has a TypeScript source in plugin/ and compiled output in dist/. The app.plugin.js entry point loads from dist/. Editing plugin/config.ts without running yarn build (or the plugin's build script) means the changes never reach the compiled output that Expo actually reads.

Solution

Rule 1: Never directly edit generated native files

  • apps/mobile/ios/Podfile - generated, edit via Expo plugins
  • apps/mobile/ios/*.pbxproj - generated, edit via Expo plugins
  • apps/mobile/android/build.gradle - generated, edit via Expo plugins

Instead, modify the Expo config plugin that generates the content:

# The Podfile content for the notification extension is templated here:
expo-plugins/notification-service-ios/plugin/config.ts  # PODFILE_MODIF_NEEDED constant

# NOT here:
apps/mobile/ios/Podfile  # This is a generated output

Rule 2: Always run the plugin build step after editing plugin source

# After editing any file in expo-plugins/notification-service-ios/plugin/
cd expo-plugins/notification-service-ios
yarn build  # Compiles TS and copies ios-notification-service-files/ to dist/

# Then regenerate native project
cd apps/mobile
npx expo prebuild --clean  # Regenerates ios/ and android/ from plugins

The build script from package.json:

"build": "rm -rf dist && tsc && cp -a ./ios-notification-service-files dist/ios-notification-service-files/"

This copies both the compiled TypeScript AND the Swift/plist template files into dist/.

Prevention

Before modifying any native iOS/Android file in an Expo project:

  1. Ask: "Is this file generated?" If it's under ios/ or android/, it almost certainly is.
  2. Find the config plugin that generates or modifies it. Search expo-plugins/ and app.config.ts.
  3. Edit the plugin source, not the generated output.
  4. Run the plugin build step if the plugin has one (yarn build).
  5. Run expo prebuild to verify changes are applied correctly.
  • expo-plugins/notification-service-ios/ - The Expo plugin for iOS notification service extension
  • apps/mobile/ios/Podfile - Generated Podfile (do not edit directly)