From e39dd19fce7c9219dce1ea85f3086a65f6918ead Mon Sep 17 00:00:00 2001
From: ieow <4881057+ieow@users.noreply.github.com>
Date: Tue, 24 Mar 2026 14:44:57 +0000
Subject: [PATCH] chore(runway): cherry-pick feat: legacy-ios-feature-flag
cp-7.71.0 (#27848)
## **Description**
Support webcredential for ios google login
Part 2/4 - Add feature flag
This pr add feature flag for the ios google login
PR list
Part 1/ 4 - https://github.com/MetaMask/metamask-mobile/pull/27741
Part 2/ 4 - https://github.com/MetaMask/metamask-mobile/pull/27848
Part 3/ 4 - https://github.com/MetaMask/metamask-mobile/pull/27850
Part 4/ 4 - TBA
## **Changelog**
CHANGELOG entry: added legacyIosGoogleConfigEnabled feature flag
## **Related issues**
Fixes:
## **Manual testing steps**
```gherkin
Feature: my feature name
Scenario: user [verb for user action]
Given [describe expected initial app state]
When user [verb for user action]
Then [describe expected outcome]
```
## **Screenshots/Recordings**
### **Before**
### **After**
## **Pre-merge author checklist**
- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
---
> [!NOTE]
> **Low Risk**
> Low risk: adds a new remote feature flag and selector with env
override, without changing authentication flow yet; main risk is
misconfiguration since the selector defaults to enabled.
>
> **Overview**
> Adds a new remote feature flag, `legacyIosGoogleConfigEnabled`,
including registry metadata and a dedicated selector
`selectLegacyIosGoogleConfigEnabled` (defaulting to `true`) that can be
force-overridden via `MM_LEGACY_IOS_GOOGLE_CONFIG_ENABLED`.
>
> Includes unit tests covering default/remote/env override behavior, and
updates `babel.config.tests.js` to avoid inlining env vars for the new
selector and its tests.
>
> Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ca7e8132c44d06ba009029b1f83eb4412e10006f. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).
---
app/constants/featureFlags.ts | 1 +
.../legacyIosGoogleConfig/index.test.ts | 56 +++++++++++++++++++
.../legacyIosGoogleConfig/index.ts | 26 +++++++++
babel.config.tests.js | 2 +
tests/feature-flags/feature-flag-registry.ts | 8 +++
5 files changed, 93 insertions(+)
create mode 100644 app/selectors/featureFlagController/legacyIosGoogleConfig/index.test.ts
create mode 100644 app/selectors/featureFlagController/legacyIosGoogleConfig/index.ts
diff --git a/app/constants/featureFlags.ts b/app/constants/featureFlags.ts
index c443b3f9fd6..a439f406657 100644
--- a/app/constants/featureFlags.ts
+++ b/app/constants/featureFlags.ts
@@ -15,6 +15,7 @@ export enum FeatureFlagNames {
tokenDetailsV2Buttons = 'tokenDetailsV2Buttons',
tokenDetailsV2ButtonLayout = 'tokenDetailsV2ButtonLayout',
complianceEnabled = 'complianceEnabled',
+ legacyIosGoogleConfigEnabled = 'legacyIosGoogleConfigEnabled',
}
export const DEFAULT_FEATURE_FLAG_VALUES: Partial<
diff --git a/app/selectors/featureFlagController/legacyIosGoogleConfig/index.test.ts b/app/selectors/featureFlagController/legacyIosGoogleConfig/index.test.ts
new file mode 100644
index 00000000000..3a507ff875a
--- /dev/null
+++ b/app/selectors/featureFlagController/legacyIosGoogleConfig/index.test.ts
@@ -0,0 +1,56 @@
+import {
+ DEFAULT_LEGACY_IOS_GOOGLE_CONFIG_ENABLED,
+ selectLegacyIosGoogleConfigEnabled,
+} from '.';
+import { FeatureFlagNames } from '../../../constants/featureFlags';
+
+describe('Legacy iOS Google Config Feature Flag Selector', () => {
+ const originalEnv = process.env.MM_LEGACY_IOS_GOOGLE_CONFIG_ENABLED;
+
+ beforeEach(() => {
+ delete process.env.MM_LEGACY_IOS_GOOGLE_CONFIG_ENABLED;
+ });
+
+ afterAll(() => {
+ if (originalEnv === undefined) {
+ delete process.env.MM_LEGACY_IOS_GOOGLE_CONFIG_ENABLED;
+ return;
+ }
+
+ process.env.MM_LEGACY_IOS_GOOGLE_CONFIG_ENABLED = originalEnv;
+ });
+
+ it('returns the default value when the remote flag is missing', () => {
+ const result = selectLegacyIosGoogleConfigEnabled.resultFunc({});
+
+ expect(result).toBe(DEFAULT_LEGACY_IOS_GOOGLE_CONFIG_ENABLED);
+ });
+
+ it('returns the remote flag value when present', () => {
+ const result = selectLegacyIosGoogleConfigEnabled.resultFunc({
+ [FeatureFlagNames.legacyIosGoogleConfigEnabled]: false,
+ });
+
+ expect(result).toBe(false);
+ });
+
+ it('allows the local env var to force enable the legacy config', () => {
+ process.env.MM_LEGACY_IOS_GOOGLE_CONFIG_ENABLED = 'true';
+
+ const result = selectLegacyIosGoogleConfigEnabled.resultFunc({
+ [FeatureFlagNames.legacyIosGoogleConfigEnabled]: false,
+ });
+
+ expect(result).toBe(true);
+ });
+
+ it('allows the local env var to force disable the legacy config', () => {
+ process.env.MM_LEGACY_IOS_GOOGLE_CONFIG_ENABLED = 'false';
+
+ const result = selectLegacyIosGoogleConfigEnabled.resultFunc({
+ [FeatureFlagNames.legacyIosGoogleConfigEnabled]: true,
+ });
+
+ expect(result).toBe(false);
+ });
+});
diff --git a/app/selectors/featureFlagController/legacyIosGoogleConfig/index.ts b/app/selectors/featureFlagController/legacyIosGoogleConfig/index.ts
new file mode 100644
index 00000000000..be844dc3bd6
--- /dev/null
+++ b/app/selectors/featureFlagController/legacyIosGoogleConfig/index.ts
@@ -0,0 +1,26 @@
+import { hasProperty } from '@metamask/utils';
+import { createSelector } from 'reselect';
+import { FeatureFlagNames } from '../../../constants/featureFlags';
+import { getFeatureFlagValue } from '../env';
+import { selectRemoteFeatureFlags } from '..';
+
+export const DEFAULT_LEGACY_IOS_GOOGLE_CONFIG_ENABLED = true;
+
+export const selectLegacyIosGoogleConfigEnabled = createSelector(
+ selectRemoteFeatureFlags,
+ (remoteFeatureFlags) => {
+ const remoteValue = hasProperty(
+ remoteFeatureFlags,
+ FeatureFlagNames.legacyIosGoogleConfigEnabled,
+ )
+ ? Boolean(
+ remoteFeatureFlags[FeatureFlagNames.legacyIosGoogleConfigEnabled],
+ )
+ : DEFAULT_LEGACY_IOS_GOOGLE_CONFIG_ENABLED;
+ return getFeatureFlagValue(
+ // Use direct env access so Babel can inline this value in app builds.
+ process.env.MM_LEGACY_IOS_GOOGLE_CONFIG_ENABLED,
+ remoteValue,
+ );
+ },
+);
diff --git a/babel.config.tests.js b/babel.config.tests.js
index bf4dfe1bd1f..292e3151ff1 100644
--- a/babel.config.tests.js
+++ b/babel.config.tests.js
@@ -37,6 +37,8 @@ const newOverrides = [
'app/components/UI/Ramp/hooks/useRampTokens.test.ts',
'app/components/Views/confirmations/hooks/pay/useTransactionPayWithdraw.ts',
'app/components/Views/confirmations/hooks/pay/useTransactionPayWithdraw.test.ts',
+ 'app/selectors/featureFlagController/legacyIosGoogleConfig/index.ts',
+ 'app/selectors/featureFlagController/legacyIosGoogleConfig/index.test.ts',
'app/util/environment.ts',
'app/util/environment.test.ts',
'app/core/Engine/controllers/rewards-controller/utils/rewards-api-url.ts',
diff --git a/tests/feature-flags/feature-flag-registry.ts b/tests/feature-flags/feature-flag-registry.ts
index bb05b3aa658..2c696a8020c 100644
--- a/tests/feature-flags/feature-flag-registry.ts
+++ b/tests/feature-flags/feature-flag-registry.ts
@@ -2829,6 +2829,14 @@ export const FEATURE_FLAG_REGISTRY: Record = {
status: FeatureFlagStatus.Active,
},
+ legacyIosGoogleConfigEnabled: {
+ name: 'legacyIosGoogleConfigEnabled',
+ type: FeatureFlagType.Remote,
+ inProd: true,
+ productionDefault: true,
+ status: FeatureFlagStatus.Active,
+ },
+
metalCardCheckoutEnabled: {
name: 'metalCardCheckoutEnabled',
type: FeatureFlagType.Remote,