Skip to content

[iOS] Turbo module: Fixes dictionary stripped out when value is null #51103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

zhongwuzw
Copy link
Contributor

@zhongwuzw zhongwuzw commented May 5, 2025

Summary:

Fixes #51083. Turbo stripped out the dictionary when the value is null. The old architecture transforms null to NSNull. The null seems useful in cases like #51803 for removing the storage of the key. @cipolleschi can you please help to review?

Changelog:

[IOS] [FIXED] - Turbo module: Fixes dictionary stripped out when value is null

Test Plan:

Repro please see #51083.

@facebook-github-bot facebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. labels May 5, 2025
@facebook-github-bot
Copy link
Contributor

@philIip has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@cipolleschi
Copy link
Contributor

Hi @zhongwuzw! thanks for the PR.
I'm not sure we can land this. I know that this is a semantic change between the Old and the New Architecture. However this might be a deliberate design decision, made before me joining the team. I'll have to investigate.

Internally, we have several pieces of code that relies on the null values being removed from the dictionary, so by landing this, we are going to break that code. Similarly, we might be breaking New Arch libraries that do not expect receiving NSNull.

@zhongwuzw
Copy link
Contributor Author

Hi @zhongwuzw! thanks for the PR.

I'm not sure we can land this. I know that this is a semantic change between the Old and the New Architecture. However this might be a deliberate design decision, made before me joining the team. I'll have to investigate.

Internally, we have several pieces of code that relies on the null values being removed from the dictionary, so by landing this, we are going to break that code. Similarly, we might be breaking New Arch libraries that do not expect receiving NSNull.

@cipolleschi Thanks for the review. If we don't support null vaule of dictionary, we should seems make the native module like setting module add a removeKey api to not passed dictionary?

@javache
Copy link
Member

javache commented May 6, 2025

Thanks for fixing this! I think this absolutely needs a unit test to show how the behaviour changes before/after.

@@ -639,6 +639,10 @@ TraceSection s(
// Message dispatch logic from old infra
id (*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend;
id convertedObjCArg = convert([RCTConvert class], rctConvertSelector, objCArg);

if (objCArg == [NSNull null]) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be looking at convertedObjCArg instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhongwuzw: that doesn't seem right. RCTInteropTurboModule looks at arg, which is the return value of the RCTConvertTo call (we should probably use that RCTConvertTo helper here too!) - so the equivalent would be to use convertedObjCArg here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, you're right. 👍 updated.

@cipolleschi
Copy link
Contributor

As @javache was mentioning, we discussed this internally and we want to pull in the fix.
However, this change might break quite a few stuff, so we need to be cautious about how we do it.
for sure we will need:

  1. a unit test
  2. a feature flag, that we can use to revert to the previous behavior in case too many things start to go very bad. We have a good infrastructure for feature flags: you can modify the featureflag.config.js to add it. Then yarn featureflags --update should generate all the required files. Then you should import the feature flag and use it.

Do you mind applying these changes?

@zhongwuzw
Copy link
Contributor Author

@cipolleschi Got it. I'll try to do them.

@cipolleschi
Copy link
Contributor

I tried to apply this fix to a test that I was doing for #51083, but it does not fixes.
I was using this repo as test bed: https://github.com/cipolleschi/TMDroppingNull

It contains both a legacy native module and a turbomodule.

The expectation is that both should receive

{
  "key": "myKey",
  "value": "<null>"
}

when pressing the Save Null button in the UI

@zhongwuzw
Copy link
Contributor Author

zhongwuzw commented May 11, 2025

@cipolleschi Hi, I tried, seems I received the null value.
LegacyNativeModule:
image

turbomodule;
image

BTW, can you help me how to write a test? I don't know wether unit test needs some env requirements? Seems I only see the old bridge related unit test. I used RNTester RNTesterUnitTests schema.

id objCArg = convertJSIValueToObjCObject(runtime, arg, jsInvoker_);
BOOL enableModuleArgumentNSNullConversionIOS = ReactNativeFeatureFlags::enableModuleArgumentNSNullConversionIOS();
id objCArg = TurboModuleConvertUtils::convertJSIValueToObjCObject(
runtime, arg, jsInvoker_, enableModuleArgumentNSNullConversionIOS);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we passing enableModuleArgumentNSNullConversionIOS to the convertJSIValueToObjCObject function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We enable useNull only if we set enableModuleArgumentNSNullConversionIOS to true to convert null to NSNull. If we don't enable enableModuleArgumentNSNullConversionIOS, everything remains the same as original.

@cipolleschi
Copy link
Contributor

cipolleschi commented May 14, 2025

~~I tried again with the repo I prepared and I don't get the same result as you. 😰 ~~

My bad, I realized that I applied badly your suggestion.


For the unit test, I'm not sure we have something set up in OSS. I'll have to investigate a bit and get back to you.

@javache
Copy link
Member

javache commented May 15, 2025

I'm adding a test in #51370 - which we should be able to use verify this changes fixes it once you enable the feature flag.

@javache
Copy link
Member

javache commented May 20, 2025

Can you update the test and add a variant that shows the behaviour you get when overriding the feature flag?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[iOS] Settings no longer handles setting key to null
4 participants