Skip to content

Convert iOS native module objects to JS with fewer lookups and retains - #59450

Open
hryhoriiK97 wants to merge 1 commit into
react:mainfrom
hryhoriiK97:ios-turbomodule-faster-object-conversion
Open

hryhoriiK97 wants to merge 1 commit into
react:mainfrom
hryhoriiK97:ios-turbomodule-faster-object-conversion

Conversation

@hryhoriiK97

Copy link
Copy Markdown
Contributor

Summary:

TurboModuleConvertUtils::convertObjCObjectToJSIValue converts native values to JS for TurboModule return values, promise results, callback arguments and codegen event emitters.

Current behavior. For an NSDictionary, it enumerates the keys and then looks up each value again with value[k]. Each key becomes a jsi::String, which the runtime then turns into a property name. Each nested value is retained and released at every level of the recursion. Short strings that CoreFoundation stores inside the pointer have no character storage, so they are copied into a heap-allocated std::u16string.

With this change.

  • Dictionaries read their keys and values at once with getObjects:andKeys:count:, and arrays with getObjects:range:. The objects are retained once, in a vector, while they are converted, because JS code that runs while properties are set could change the container. Below that, the conversion doesn't retain each value again.
  • Keys are created as property names directly, with jsi::PropNameID::forAscii or forUtf16.
  • Strings of up to 64 characters without character storage are copied to the stack instead of the heap. ASCII ones are created as ASCII.

JS receives the same values as before.

Changelog:

[IOS] [CHANGED] - Objects returned from native modules are converted to JS with fewer lookups and retains

Test Plan:

  • clang-format --dry-run --Werror on the changed file passes.
  • RNTester built with xcodebuild in Release and run on a physical iPhone 15 Pro (iOS 26.6.1).
  • Same output. A test payload converted with this change and with the current code, in the same build switched by a launch argument, gave byte-identical JS values, compared deeply in JS. The payload had:
    • empty, NUL, BOM, emoji, accented, CJK and lone-surrogate strings, and a mutable string;
    • booleans, integers of several sizes, floats, NaN and NSDecimalNumber;
    • NSNull, NSDate and NSURL values;
    • nested arrays and dictionaries.
  • Measurement. RNTester Playground calling NativeSampleTurboModule methods in a loop. The calls include the unchanged conversion of the arguments from JS. The two arms are the same binary with the conversion switched by a launch argument, in alternating order: 8 launches without and 4 with this change. The presses were driven by Argent on the physical iPhone. iPhone 15 Pro, Release, medians per call:
Call before this PR change
getObject, 20 keys 9.34 µs 8.43 µs −10%
getObject, nested object like a keyboard event 6.69 µs 6.00 µs −10%
getObject, 3 keys 2.79 µs 2.66 µs −5%
getValue (returns an object) 2.96 µs 2.79 µs −6%

The ranges of the two arms are separate for the first two rows and overlap for the last two. Calls that don't return objects are the same within noise.

🤖 Generated with Claude Code

TurboModule return values, promise results, callback arguments and codegen
events are converted by convertObjCObjectToJSIValue. Dictionaries now read
their keys and values at once instead of looking up each key again, keys
are created as property names directly, nested values are retained once
per container instead of once per call, and short strings without character
storage are copied to the stack instead of the heap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 24, 2026
@facebook-github-tools facebook-github-tools Bot added p: Software Mansion Partner: Software Mansion Partner labels Oct 24, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Oct 24, 2026
@github-actions

Copy link
Copy Markdown

Visual regression

No visual differences at 618107cc5d73.

android: no capture

This branch has not been deployed

No deployments
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. p: Software Mansion Partner: Software Mansion Partner 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.

1 participant