Skip to content
Open
Show file tree
Hide file tree
Changes from 99 commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
3dfaa1c
feat(storage): move to turbomodules
russellwheatley Jul 2, 2025
9d896d2
chore: convert to TS
russellwheatley Jul 2, 2025
b80b1a7
format
russellwheatley Jul 2, 2025
1431fbf
chore: ignore some things for now
russellwheatley Jul 2, 2025
cff1c5f
chore: update package.json main & types
russellwheatley Jul 2, 2025
abd0073
init with bob builder
russellwheatley Jul 2, 2025
9f30fa8
yarn.lock
russellwheatley Jul 2, 2025
18c0fdf
chore: rm rootDir from tsconfig.json
russellwheatley Jul 2, 2025
800bf1a
test: import as type for tests
russellwheatley Jul 2, 2025
1962777
chore: fix types
russellwheatley Jul 2, 2025
4dd309f
update types
russellwheatley Jul 2, 2025
193aa9b
rm type test
russellwheatley Jul 2, 2025
a49cf27
chore: fix path of type definitions
russellwheatley Jul 2, 2025
6b52129
chore: moving functions to TS
russellwheatley Jul 2, 2025
1214b3e
fix: imports
russellwheatley Jul 3, 2025
34138fa
fix some types
russellwheatley Jul 3, 2025
6802209
update types
russellwheatley Jul 3, 2025
a1cd58a
port over remaining types and delete js files
russellwheatley Jul 3, 2025
bd27a05
remove functions declaration
russellwheatley Jul 3, 2025
3b645b1
chore: create spec for turbomodules
russellwheatley Jul 3, 2025
909b963
rename spec
russellwheatley Jul 3, 2025
98f387c
rm old version of cli
russellwheatley Jul 7, 2025
7bad518
fix: need react-native as dependency for codegen to work
russellwheatley Jul 7, 2025
3ba3a76
update scripts for generating code
russellwheatley Jul 7, 2025
d3ca750
generated code for android & ios
russellwheatley Jul 7, 2025
7eda482
update podspec & config file as per RN docs
russellwheatley Jul 7, 2025
e714ad8
rm generated code
russellwheatley Jul 7, 2025
9f17910
rename to Native prefix otherwise ignored by codegen
russellwheatley Jul 7, 2025
d8ffb33
updated generic types for spec
russellwheatley Jul 7, 2025
4bd4607
latest generated code based on updated spec
russellwheatley Jul 7, 2025
90bbbed
update naming of generated code
russellwheatley Jul 7, 2025
938d1c3
update RNFB functions module
russellwheatley Jul 7, 2025
d4b2541
make file `.mm` & update podspec for functions
russellwheatley Jul 8, 2025
fba772e
podfile.lock
russellwheatley Jul 8, 2025
88eeda8
Merge branch 'main' into functions-turbo
russellwheatley Jul 8, 2025
3218d4d
yarn.lock
russellwheatley Jul 8, 2025
30f5d35
chore: update version
russellwheatley Jul 8, 2025
cdfee94
chore: reverse order of prepare script
russellwheatley Jul 8, 2025
a04eb09
feat(android): make NativeFunctionsModule compatible with existing code
russellwheatley Jul 8, 2025
1ca5cbb
update path
russellwheatley Jul 9, 2025
1ca1da2
move modular/namespaced to same directory to fix TS
russellwheatley Jul 9, 2025
f4014d0
exclude jni from compilation
russellwheatley Jul 9, 2025
459f936
fix: retreive if turbomodule
russellwheatley Jul 16, 2025
471b285
fix: rename native module name
russellwheatley Jul 16, 2025
1bb2088
setup example app
russellwheatley Jul 16, 2025
72ce7a7
update functions spec
russellwheatley Jul 16, 2025
468cdce
regenerated code with args
russellwheatley Jul 16, 2025
6884aee
update android code handling
russellwheatley Jul 16, 2025
65a9dd4
fix: iOS turbomodules implementation
russellwheatley Jul 17, 2025
9b64eac
chore: regenerate code
russellwheatley Jul 17, 2025
b966ffe
attempt at fixing iOS issue
russellwheatley Jul 17, 2025
e75a23f
Merge branch 'main' into functions-turbo
russellwheatley Jul 17, 2025
c01d1ed
yarn.lock
russellwheatley Jul 17, 2025
e657017
podlock
russellwheatley Jul 17, 2025
0b9a679
fix: ios
russellwheatley Jul 17, 2025
771e7cb
fix: ios correct types
russellwheatley Jul 17, 2025
831f2fb
chore: config for turbomodules
russellwheatley Jul 18, 2025
2500468
fix: ios implementation
russellwheatley Jul 18, 2025
3012669
chore: move types
russellwheatley Jul 18, 2025
5751dee
chore: move types back
russellwheatley Jul 18, 2025
184450e
chore: fix lint
russellwheatley Jul 18, 2025
c6a1182
fix: use get for TurboModules
russellwheatley Jul 18, 2025
3a2d634
fix: move back types
russellwheatley Jul 18, 2025
434393e
chore: remove unused js file
russellwheatley Jul 18, 2025
a661db6
chore: finally fix types
russellwheatley Jul 18, 2025
4b8c892
update to typescript
russellwheatley Jul 18, 2025
b8b969d
try updating tsconfig.json
russellwheatley Jul 18, 2025
3380bc9
fix(ios): pass nsnull if no data
russellwheatley Jul 21, 2025
9437462
chore: make var for allArgs, easier to debug
russellwheatley Jul 21, 2025
50bda8a
chore: remove unneeded package
russellwheatley Jul 22, 2025
44c9206
chore: make note about podspec updates
russellwheatley Jul 22, 2025
bcc522f
chore: update android code
russellwheatley Jul 22, 2025
fe6816b
chore: reinsert code comments
russellwheatley Jul 22, 2025
b8c7db7
chore: remove todo
russellwheatley Jul 22, 2025
67cce20
chore: typing improvement
russellwheatley Jul 22, 2025
c8b2683
chore: types
russellwheatley Jul 22, 2025
cf18e11
chore: improve details typing
russellwheatley Jul 22, 2025
699c96b
chore: we now expose everything via index (now modular)
russellwheatley Jul 22, 2025
3bf85d8
chore: improve web typing
russellwheatley Jul 22, 2025
c83c945
yarn.lock
russellwheatley Jul 22, 2025
ba19d1b
fix: allow details to be null
russellwheatley Jul 22, 2025
d44e244
Merge branch 'main' into functions-turbo
russellwheatley Nov 25, 2025
6de6521
yarn.lock
russellwheatley Nov 25, 2025
4d57bb6
fix(ios): update core logic to use sentinel value for preserving `nul…
russellwheatley Nov 28, 2025
84641c1
refactor: rename turbo functions spec
russellwheatley Nov 28, 2025
ab024ee
refactor: remove previous generated code
russellwheatley Nov 28, 2025
665a34a
refactor: generate code for new naming technique
russellwheatley Nov 28, 2025
9222948
refactor: need spec file to be prefixed with "Native"
russellwheatley Nov 28, 2025
ff59bae
refactor: re-run codegen with new name
russellwheatley Nov 28, 2025
2b9571e
refactor: ios renaming types
russellwheatley Nov 28, 2025
e23e657
refactor: need to prefix with Native
russellwheatley Nov 28, 2025
4b4b8ab
refactor: prefix with "Native"
russellwheatley Nov 28, 2025
e41ffee
refactor: android naming
russellwheatley Nov 28, 2025
a107fa9
chore: format build.gradle
russellwheatley Nov 28, 2025
0943a95
fix: improve the pattern matching for the turbo modules methods
russellwheatley Nov 28, 2025
27e6b0e
Merge branch 'main' into functions-turbo
russellwheatley Nov 28, 2025
d5e5df8
chore: lock files
russellwheatley Nov 28, 2025
eb8a458
chore: format
russellwheatley Nov 28, 2025
d7b2646
test: skipping onConfigUpdate tests until they're fixed
russellwheatley Nov 28, 2025
928e2df
fix: storage e2e test breaking because it was being encoded
russellwheatley Dec 1, 2025
b480d6d
chore: format
russellwheatley Dec 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default [
'**/app.playground.js',
'**/type-test.ts',
'packages/**/modular/dist/**/*',
'packages/**/dist/**/*',
'src/version.js',
'packages/**/node_modules/**/*',
'packages/**/plugin/build/**/*',
Expand All @@ -40,6 +41,7 @@ export default [
'**/type-test.ts',
'packages/**/modular/dist/**/*',
'packages/ai/__tests__/test-utils',
'packages/vertexai/__tests__/test-utils',
'packages/vertexai/dist',
'packages/ai/dist',
],
Expand Down
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
"lerna:clean": "lerna clean",
"build:all:clean": "lerna run build:clean",
"build:all:build": "lerna run build",
"test:functions:build": "cd .github/workflows/scripts/functions && yarn build",
"codegen:all": "lerna run codegen",
"lint": "yarn lint:js && yarn lint:android && yarn lint:ios:check",
"lint:js": "eslint packages/* --max-warnings=0",
"lint:android": "(google-java-format --set-exit-if-changed --replace --glob=\"packages/*/android/src/**/*.java\" || (echo \"\n\nandroid formatting error - please re-run\n\n\" && exit 1)) && (git diff --exit-code packages/*/android/src || (echo \"\n\nandroid files changed from linting, please examine and commit result\n\n\" && exit 1))",
"lint:ios:check": "clang-format --glob=\"packages/*/ios/**/*.{h,cpp,m,mm}\" --style=Google -n -Werror",
"lint:ios:fix": "clang-format -i --glob=\"packages/*/ios/**/*.{h,cpp,m,mm}\" --style=Google",
"lint:android": "(find packages/*/android/src -name '*.java' -not -path '*/generated/*' -print0 | xargs -0 google-java-format --set-exit-if-changed --replace || (echo \"\n\nandroid formatting error - please re-run\n\n\" && exit 1)) && (git diff --exit-code packages/*/android/src || (echo \"\n\nandroid files changed from linting, please examine and commit result\n\n\" && exit 1))",
"lint:ios:check": "find packages/*/ios -type f \\( -name '*.h' -o -name '*.cpp' -o -name '*.m' -o -name '*.mm' \\) -not -path '*/generated/*' -print0 | xargs -0 clang-format --style=Google -n -Werror",
"lint:ios:fix": "find packages/*/ios -type f \\( -name '*.h' -o -name '*.cpp' -o -name '*.m' -o -name '*.mm' \\) -not -path '*/generated/*' -print0 | xargs -0 clang-format -i --style=Google",
"lint:markdown": "prettier --check \"docs/**/*.md\"",
"lint:report": "eslint --output-file=eslint-report.json --format=json . --ext .js,.jsx,.ts,.tsx",
"lint:spellcheck": "spellchecker --quiet --files=\"docs/**/*.md\" --dictionaries=\"./.spellcheck.dict.txt\" --reports=\"spelling.json\" --plugins spell indefinite-article repeated-words syntax-mentions syntax-urls frontmatter",
Expand Down Expand Up @@ -71,6 +73,7 @@
"@firebase/rules-unit-testing": "^5.0.0",
"@inquirer/prompts": "^7.10.0",
"@octokit/core": "^7.0.6",
"@react-native-community/cli": "latest",
"@tsconfig/node-lts": "^22.0.4",
"@types/react": "~19.0.14",
"@types/react-native": "^0.73.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class UniversalFirebaseModule {
private final Context context;
private final String serviceName;

protected UniversalFirebaseModule(Context context, String serviceName) {
public UniversalFirebaseModule(Context context, String serviceName) {
this.context = context;
this.serviceName = serviceName;
this.executorService = new TaskExecutorService(getName());
Expand All @@ -43,7 +43,7 @@ public Context getApplicationContext() {
return getContext().getApplicationContext();
}

protected ExecutorService getExecutor() {
public ExecutorService getExecutor() {
return executorService.getExecutor();
}

Expand Down
6 changes: 6 additions & 0 deletions packages/app/ios/RNFBApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
2748D8202237018600FC8DC8 /* RNFBMeta.m in Sources */ = {isa = PBXBuildFile; fileRef = 2748D81F2237018600FC8DC8 /* RNFBMeta.m */; };
4D97BAD423042F2700077358 /* RNFBUtilsModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D97BAD323042F2700077358 /* RNFBUtilsModule.m */; };
DAA1F28522FCF6AD00F4DEC1 /* RNFBVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = DAA1F28422FCF6AD00F4DEC1 /* RNFBVersion.m */; };
RNFB00001A2025011100000001 /* RNFBNullSentinelInterceptor.m in Sources */ = {isa = PBXBuildFile; fileRef = RNFB00001A2025011100000002 /* RNFBNullSentinelInterceptor.m */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand Down Expand Up @@ -53,6 +54,8 @@
4D97BAD323042F2700077358 /* RNFBUtilsModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFBUtilsModule.m; sourceTree = "<group>"; };
DAA1F28422FCF6AD00F4DEC1 /* RNFBVersion.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFBVersion.m; sourceTree = "<group>"; };
DAA1F28622FCF6C200F4DEC1 /* RNFBVersion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFBVersion.h; sourceTree = "<group>"; };
RNFB00001A2025011100000002 /* RNFBNullSentinelInterceptor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFBNullSentinelInterceptor.m; sourceTree = "<group>"; };
RNFB00001A2025011100000003 /* RNFBNullSentinelInterceptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFBNullSentinelInterceptor.h; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -97,6 +100,8 @@
DAA1F28622FCF6C200F4DEC1 /* RNFBVersion.h */,
4D97BAD223042F0800077358 /* RNFBUtilsModule.h */,
4D97BAD323042F2700077358 /* RNFBUtilsModule.m */,
RNFB00001A2025011100000003 /* RNFBNullSentinelInterceptor.h */,
RNFB00001A2025011100000002 /* RNFBNullSentinelInterceptor.m */,
);
path = RNFBApp;
sourceTree = "<group>";
Expand Down Expand Up @@ -178,6 +183,7 @@
2744B99121F46140004F8E3F /* RNFBRCTEventEmitter.m in Sources */,
2744B9A421F48A4F004F8E3F /* RCTConvert+FIROptions.m in Sources */,
2748D8152236426300FC8DC8 /* RNFBJSON.m in Sources */,
RNFB00001A2025011100000001 /* RNFBNullSentinelInterceptor.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
47 changes: 47 additions & 0 deletions packages/app/ios/RNFBApp/RNFBNullSentinelInterceptor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Copyright (c) 2016-present Invertase Limited & Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this library except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#ifndef RNFBNullSentinelInterceptor_h
#define RNFBNullSentinelInterceptor_h

#import <Foundation/Foundation.h>

/**
* Intercepts TurboModule conversions to automatically decode null sentinels.
*
* iOS TurboModules strip null values from object properties during serialization.
* See: https://github.com/facebook/react-native/issues/52802
* The JavaScript side encodes nulls as sentinel objects,
* and this interceptor automatically converts them back to NSNull before they
* reach module implementation methods.
*
* This class uses method swizzling on RCTCxxConvert to intercept all TurboModule
* data conversion methods (JS_*Module_Spec*Data:), decoding sentinels before the
* data reaches the C++ bridging layer and ultimately your module methods.
*/
@interface RNFBNullSentinelInterceptor : NSObject

/**
* Initializes the null sentinel interceptor.
* This swizzles RCTCxxConvert (TurboModule converter) to automatically decode null sentinels.
* Called automatically when the class is loaded via +load.
*/
+ (void)initialize;

@end

#endif
74 changes: 74 additions & 0 deletions packages/app/ios/RNFBApp/RNFBNullSentinelInterceptor.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Copyright (c) 2016-present Invertase Limited & Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this library except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#import "RNFBNullSentinelInterceptor.h"
#import <objc/runtime.h>
#import "RNFBSharedUtils.h"

@implementation RNFBNullSentinelInterceptor

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzleRCTConvertMethods];
});
}

+ (void)swizzleRCTConvertMethods {
// For TurboModules: Swizzle RCTCxxConvert to intercept all conversion methods
Class cxxConvertClass = NSClassFromString(@"RCTCxxConvert");
if (cxxConvertClass) {
[self swizzleTurboModuleConversions:cxxConvertClass];
}
}

+ (void)swizzleTurboModuleConversions:(Class)cxxConvertClass {
// Get all methods from RCTCxxConvert
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(object_getClass(cxxConvertClass), &methodCount);

for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];
SEL selector = method_getName(method);
NSString *selectorName = NSStringFromSelector(selector);

// Intercept TurboModule data conversion methods (they follow pattern:
// JS_NativeRNFBTurbo*_Spec*)
if ([selectorName hasPrefix:@"JS_NativeRNFBTurbo"] && [selectorName containsString:@"_Spec"]) {
// Create a swizzled version using IMP
IMP originalIMP = method_getImplementation(method);
const char *typeEncoding = method_getTypeEncoding(method);

// Replace with our wrapper that decodes nulls
IMP newIMP = imp_implementationWithBlock(^id(id self, id json) {
// Decode null sentinels before passing to original conversion
id decoded = [RNFBSharedUtils decodeNullSentinels:json];

// Call original implementation with decoded data
typedef id (*OriginalFunc)(id, SEL, id);
OriginalFunc originalFunc = (OriginalFunc)originalIMP;
return originalFunc(self, selector, decoded);
});

method_setImplementation(method, newIMP);
}
}

free(methods);
}

@end
2 changes: 2 additions & 0 deletions packages/app/ios/RNFBApp/RNFBSharedUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ extern NSString *const DEFAULT_APP_NAME;

+ (BOOL)getConfigBooleanValue:(NSString *)tag key:(NSString *)key defaultValue:(BOOL)defaultValue;

+ (id)decodeNullSentinels:(id)value;

@end

#endif
127 changes: 127 additions & 0 deletions packages/app/ios/RNFBApp/RNFBSharedUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,131 @@ + (BOOL)getConfigBooleanValue:(NSString *)tag key:(NSString *)key defaultValue:(
return enabled;
}

/**
* Decodes null sentinel objects back to NSNull values.
* Uses iterative stack-based traversal to avoid stack overflow on deeply nested structures.
*
* This reverses the encoding done on the JavaScript side where null values in object
* properties are replaced with {__rnfbNull: true} sentinel objects to survive iOS
* TurboModule serialization.
*
* Process:
* 1. Detects sentinel objects: dictionaries with single key "__rnfbNull" set to true
* 2. Replaces sentinels with NSNull in object properties and arrays
* 3. Preserves regular NSNull values that were in arrays (never encoded as sentinels)
* 4. Deep processes all nested objects and arrays using a stack-based iteration
*
* @param value - The value to decode (dictionary, array, or primitive)
* @return The decoded value with sentinels replaced by NSNull
*/
+ (id)decodeNullSentinels:(id)value {
// Non-container values are returned as-is
if (![value isKindOfClass:[NSDictionary class]] && ![value isKindOfClass:[NSArray class]]) {
return value;
}

// Helper to detect the sentinel
BOOL (^isNullSentinel)(NSDictionary *) = ^BOOL(NSDictionary *dict) {
id flag = dict[@"__rnfbNull"];
return (dict.count == 1 && flag != nil && [flag boolValue]);
};

// Root-level sentinel case
if ([value isKindOfClass:[NSDictionary class]] && isNullSentinel((NSDictionary *)value)) {
return [NSNull null];
}

id rootOriginal = value;
id rootMutable = nil;

if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary *dict = (NSDictionary *)value;
rootMutable = [NSMutableDictionary dictionaryWithCapacity:dict.count];
} else {
NSArray *array = (NSArray *)value;
rootMutable = [NSMutableArray arrayWithCapacity:array.count];
}

// Stack-based iteration to process nested structures without recursion
// Stack frames: { @"original": container, @"mutable": mutableContainer }
NSMutableArray<NSDictionary *> *stack = [NSMutableArray array];
[stack addObject:@{@"original" : rootOriginal, @"mutable" : rootMutable}];

while (stack.count > 0) {
NSDictionary *frame = [stack lastObject];
[stack removeLastObject];

id original = frame[@"original"];
id mutable = frame[@"mutable"];

if ([original isKindOfClass:[NSDictionary class]]) {
NSDictionary *origDict = (NSDictionary *)original;
NSMutableDictionary *mutDict = (NSMutableDictionary *)mutable;

for (id key in origDict) {
id child = origDict[key];

if ([child isKindOfClass:[NSDictionary class]]) {
NSDictionary *childDict = (NSDictionary *)child;

if (isNullSentinel(childDict)) {
// Replace sentinel with NSNull
mutDict[key] = [NSNull null];
} else {
// Process nested dictionary
NSMutableDictionary *childMut =
[NSMutableDictionary dictionaryWithCapacity:childDict.count];
mutDict[key] = childMut;
[stack addObject:@{@"original" : childDict, @"mutable" : childMut}];
}
} else if ([child isKindOfClass:[NSArray class]]) {
// Process nested array
NSArray *childArray = (NSArray *)child;
NSMutableArray *childMut = [NSMutableArray arrayWithCapacity:childArray.count];
mutDict[key] = childMut;
[stack addObject:@{@"original" : childArray, @"mutable" : childMut}];
} else {
// Preserve primitive values
if (child) {
mutDict[key] = child;
} else {
// NSDictionary can't store nil, and original code wouldn't see nil values either.
}
}
}
} else if ([original isKindOfClass:[NSArray class]]) {
NSArray *origArray = (NSArray *)original;
NSMutableArray *mutArray = (NSMutableArray *)mutable;

for (id child in origArray) {
if ([child isKindOfClass:[NSDictionary class]]) {
NSDictionary *childDict = (NSDictionary *)child;

if (isNullSentinel(childDict)) {
// Replace sentinel with NSNull
[mutArray addObject:[NSNull null]];
} else {
// Process nested dictionary
NSMutableDictionary *childMut =
[NSMutableDictionary dictionaryWithCapacity:childDict.count];
[mutArray addObject:childMut];
[stack addObject:@{@"original" : childDict, @"mutable" : childMut}];
}
} else if ([child isKindOfClass:[NSArray class]]) {
// Process nested array
NSArray *childArray = (NSArray *)child;
NSMutableArray *childMut = [NSMutableArray arrayWithCapacity:childArray.count];
[mutArray addObject:childMut];
[stack addObject:@{@"original" : childArray, @"mutable" : childMut}];
} else {
// Preserve primitive values and NSNull (which never became sentinels in arrays)
[mutArray addObject:child ?: [NSNull null]];
}
}
}
}

return rootMutable;
}

@end
3 changes: 2 additions & 1 deletion packages/app/lib/internal/nativeModuleAndroidIos.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable no-console */
import { NativeModules } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

/**
* This is used by Android and iOS to get a native module.
Expand All @@ -8,7 +9,7 @@ import { NativeModules } from 'react-native';
* @param moduleName
*/
export function getReactNativeModule(moduleName) {
const nativeModule = NativeModules[moduleName];
const nativeModule = NativeModules[moduleName] || TurboModuleRegistry.get(moduleName);
if (!globalThis.RNFBDebug) {
return nativeModule;
}
Expand Down
Loading
Loading