Compare commits

...
19 changed files with 360 additions and 66 deletions
@@ -22,6 +22,20 @@ const {StructCollector} = require('./StructCollector');
type FilesOutput = Map<string, string>;
const SwiftCompatibleDeclarationTemplate = ({
hasteModuleName,
protocolMethods,
}: $ReadOnly<{
hasteModuleName: string,
protocolMethods: string,
}>) => `
@protocol ${hasteModuleName}Spec <RCTBridgeModule, RCTSwiftTurboModule>
${protocolMethods}
@end
`
const ModuleDeclarationTemplate = ({
hasteModuleName,
structDeclarations,
@@ -58,6 +72,51 @@ namespace facebook::react {
};
} // namespace facebook::react`;
const SwiftCompatibleHeaderFileTemplate = ({
headerFileName,
moduleDeclarations,
assumeNonnull,
}: $ReadOnly<{
headerFileName: string,
moduleDeclarations: string,
assumeNonnull: boolean,
}>) => {
const headerFileNameWithNoExt = headerFileName
.replace(/\.h$/, '')
.replace(/-/, '');
return (
`/**
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
* ${'@'}generated by codegen project: GenerateModuleObjCpp
*
* We create an umbrella header (and corresponding implementation) here since
* Cxx compilation in BUCK has a limitation: source-code producing genrule()s
* must have a single output. More files => more genrule()s => slower builds.
*/
// Avoid multiple includes of ${headerFileNameWithNoExt} symbols
#ifndef ${headerFileNameWithNoExt}_H
#define ${headerFileNameWithNoExt}_H
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <ReactCommon/RCTSwiftTurboModule.h>
` +
(assumeNonnull ? '\nNS_ASSUME_NONNULL_BEGIN\n' : '') +
moduleDeclarations +
'\n' +
(assumeNonnull ? '\nNS_ASSUME_NONNULL_END\n' : '\n') +
`#endif // ${headerFileNameWithNoExt}_H` +
'\n'
);
};
const HeaderFileTemplate = ({
headerFileName,
moduleDeclarations,
@@ -150,6 +209,7 @@ module.exports = {
const nativeModules = getModules(schema);
const moduleDeclarations: Array<string> = [];
const swiftCompatibleModulesDeclarations: Array<string> = [];
const structInlineMethods: Array<string> = [];
const moduleImplementations: Array<string> = [];
@@ -209,6 +269,15 @@ module.exports = {
}),
);
swiftCompatibleModulesDeclarations.push(
SwiftCompatibleDeclarationTemplate({
hasteModuleName: hasteModuleName,
protocolMethods: methodSerializations
.map(({protocolMethod}) => protocolMethod)
.join('\n'),
}),
);
structInlineMethods.push(methodStrs.join('\n'));
moduleImplementations.push(
@@ -232,6 +301,13 @@ module.exports = {
assumeNonnull,
});
const swiftCompatibleHeaderFileName = `${libraryName}-Swift.h`;
const swiftHeaderFile = SwiftCompatibleHeaderFileTemplate({
headerFileName: swiftCompatibleHeaderFileName,
moduleDeclarations: swiftCompatibleModulesDeclarations.join('\n'),
assumeNonnull,
});
const sourceFileName = `${libraryName}-generated.mm`;
const sourceFile = SourceFileTemplate({
headerFileName,
@@ -240,6 +316,7 @@ module.exports = {
return new Map([
[headerFileName, headerFile],
[swiftCompatibleHeaderFileName, swiftHeaderFile],
[sourceFileName, sourceFile],
]);
},
@@ -0,0 +1,11 @@
//
// RCTModule.mm
// Pods
//
// Created by Riccardo Cipolleschi on 18/07/2025.
//
@protocol RCTModule
@end
@@ -0,0 +1,13 @@
//
// RCTSwiftTurboModule.h
// Pods
//
// Created by Riccardo Cipolleschi on 18/07/2025.
//
#import "RCTModule.h"
@protocol RCTSwiftTurboModule <RCTModule>
@end
@@ -19,8 +19,10 @@
#import <string>
#import <unordered_map>
#import "RCTModule.h"
#define RCT_IS_TURBO_MODULE_CLASS(klass) \
((RCTTurboModuleEnabled() && [(klass) conformsToProtocol:@protocol(RCTTurboModule)]))
((RCTTurboModuleEnabled() && [(klass) conformsToProtocol:@protocol(RCTModule)]))
#define RCT_IS_TURBO_MODULE_INSTANCE(module) RCT_IS_TURBO_MODULE_CLASS([(module) class])
namespace facebook::react {
@@ -186,13 +188,20 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
*/
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params;
@optional
/**
* Return an instance of an Apple Module
*/
- (Class<RCTModule>)getAppleModule;
@end
/**
* Protocol that objects can inherit to conform to be treated as turbomodules.
* It inherits from RCTTurboModuleProvider, meaning that a TurboModule can create itself
*/
@protocol RCTTurboModule <RCTModuleProvider>
@protocol RCTTurboModule <RCTModule, RCTModuleProvider>
@optional
- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper;
@@ -210,3 +219,4 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
- (std::shared_ptr<facebook::react::NativeMethodCallInvoker>)decorateNativeMethodCallInvoker:
(std::shared_ptr<facebook::react::NativeMethodCallInvoker>)nativeMethodCallInvoker;
@end
@@ -847,7 +847,7 @@ void ObjCTurboModule::setMethodArgConversionSelector(NSString *methodName, size_
void ObjCTurboModule::setEventEmitterCallback(EventEmitterCallback eventEmitterCallback)
{
if ([instance_ conformsToProtocol:@protocol(RCTTurboModule)] &&
if ([instance_ conformsToProtocol:@protocol(RCTModule)] &&
[instance_ respondsToSelector:@selector(setEventEmitterCallback:)]) {
EventEmitterCallbackWrapper *wrapper = [EventEmitterCallbackWrapper new];
wrapper->_eventEmitterCallback = std::move(eventEmitterCallback);
@@ -859,3 +859,4 @@ void ObjCTurboModule::setEventEmitterCallback(EventEmitterCallback eventEmitterC
@implementation EventEmitterCallbackWrapper
@end
@@ -159,7 +159,7 @@ class LegacyModuleNativeMethodCallInvoker : public ModuleNativeMethodCallInvoker
bool isTurboModuleClass(Class cls)
{
return [cls conformsToProtocol:@protocol(RCTTurboModule)];
return [cls conformsToProtocol:@protocol(RCTModule)];
}
bool isTurboModuleInstance(id module)
@@ -339,18 +339,24 @@ typedef struct {
/**
* Step 2: Look for platform-specific modules.
*/
id<RCTModuleProvider> module = [self _moduleProviderForName:moduleName];
id<RCTModuleProvider> moduleProvider = [self _moduleProviderForName:moduleName];
TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName);
// If we request that a TurboModule be created, its respective ObjC class must exist
// If the class doesn't exist, then _provideObjCModule returns nil
if (!module) {
if (!moduleProvider) {
return nullptr;
}
id<RCTModuleProvider> module = nullptr;
if ([moduleProvider respondsToSelector:@selector(getAppleModule)]) {
module = (id<RCTModuleProvider>)[self _provideObjCModule:moduleName moduleProvider:moduleProvider];
}
id<RCTModuleProvider> moduleOrProvider = module ? module : moduleProvider;
std::shared_ptr<NativeMethodCallInvoker> nativeMethodCallInvoker = nullptr;
dispatch_queue_t methodQueue = (dispatch_queue_t)objc_getAssociatedObject(module, &kAssociatedMethodQueueKey);
dispatch_queue_t methodQueue = (dispatch_queue_t)objc_getAssociatedObject(moduleOrProvider, &kAssociatedMethodQueueKey);
if (methodQueue) {
/**
* Step 2c: Create and native CallInvoker from the TurboModule's method queue.
@@ -370,11 +376,11 @@ typedef struct {
* Step 2d: If the moduleClass is a legacy CxxModule, return a TurboCxxModule instance that
* wraps CxxModule.
*/
Class moduleClass = [module class];
Class moduleClass = [moduleOrProvider class];
if ([moduleClass isSubclassOfClass:RCTCxxModule.class]) {
// Use TurboCxxModule compat class to wrap the CxxModule instance.
// This is only for migration convenience, despite less performant.
auto turboModule = std::make_shared<TurboCxxModule>([((RCTCxxModule *)module) createModule], _jsInvoker);
auto turboModule = std::make_shared<TurboCxxModule>([((RCTCxxModule *)moduleOrProvider) createModule], _jsInvoker);
_turboModuleCache.insert({moduleName, turboModule});
return turboModule;
}
@@ -385,27 +391,27 @@ typedef struct {
* Use respondsToSelector: below to infer conformance to @protocol(RCTTurboModule). Using conformsToProtocol: is
* expensive.
*/
if ([module respondsToSelector:@selector(getTurboModule:)]) {
if ([moduleProvider respondsToSelector:@selector(getTurboModule:)]) {
ObjCTurboModule::InitParams params = {
.moduleName = moduleName,
.instance = (id<RCTBridgeModule>)module,
.instance = (id<RCTBridgeModule>)moduleOrProvider,
.jsInvoker = _jsInvoker,
.nativeMethodCallInvoker = nativeMethodCallInvoker,
.isSyncModule = methodQueue == RCTJSThread,
.shouldVoidMethodsExecuteSync = (bool)RCTTurboModuleSyncVoidMethodsEnabled(),
};
auto turboModule = [(id<RCTTurboModule>)module getTurboModule:params];
auto turboModule = [(id<RCTTurboModule>)moduleProvider getTurboModule:params];
if (turboModule == nullptr) {
RCTLogError(@"TurboModule \"%@\"'s getTurboModule: method returned nil.", moduleClass);
}
_turboModuleCache.insert({moduleName, turboModule});
if ([module respondsToSelector:@selector(installJSIBindingsWithRuntime:callInvoker:)]) {
[(id<RCTTurboModuleWithJSIBindings>)module installJSIBindingsWithRuntime:*runtime callInvoker:_jsInvoker];
} else if ([module respondsToSelector:@selector(installJSIBindingsWithRuntime:)]) {
if ([moduleOrProvider respondsToSelector:@selector(installJSIBindingsWithRuntime:callInvoker:)]) {
[(id<RCTTurboModuleWithJSIBindings>)moduleOrProvider installJSIBindingsWithRuntime:*runtime callInvoker:_jsInvoker];
} else if ([moduleOrProvider respondsToSelector:@selector(installJSIBindingsWithRuntime:)]) {
// Old API without CallInvoker (deprecated)
[(id<RCTTurboModuleWithJSIBindings>)module installJSIBindingsWithRuntime:*runtime];
[(id<RCTTurboModuleWithJSIBindings>)moduleOrProvider installJSIBindingsWithRuntime:*runtime];
}
return turboModule;
}
@@ -496,16 +502,16 @@ typedef struct {
if ([_delegate respondsToSelector:@selector(getModuleProvider:)]) {
moduleProvider = [_delegate getModuleProvider:moduleName];
}
if (RCTTurboModuleInteropEnabled() && ![self _isTurboModule:moduleName] && !moduleProvider) {
return nil;
}
if (moduleProvider) {
if ([moduleProvider conformsToProtocol:@protocol(RCTTurboModule)]) {
// moduleProvider is also a TM, we need to initialize objectiveC properties, like the dispatch queue
if ([moduleProvider conformsToProtocol:@protocol(RCTModule)]) {
return (id<RCTModuleProvider>)[self _provideObjCModule:moduleName moduleProvider:moduleProvider];
}
// module is Cxx module
return moduleProvider;
}
@@ -585,7 +591,12 @@ typedef struct {
/**
* Step 2a: Resolve platform-specific class.
*/
Class moduleClass = moduleProvider ? [moduleProvider class] : [self _getModuleClassFromName:moduleName];
Class moduleClass = moduleProvider ?
([moduleProvider respondsToSelector:@selector(getAppleModule)] ?
[moduleProvider getAppleModule] :
[moduleProvider class]) :
[self _getModuleClassFromName:moduleName];
__block id<RCTBridgeModule> module = nil;
@@ -640,7 +651,7 @@ typedef struct {
return [moduleClass conformsToProtocol:@protocol(RCTBridgeModule)];
}
return [moduleClass conformsToProtocol:@protocol(RCTTurboModule)];
return [moduleClass conformsToProtocol:@protocol(RCTModule)];
}
/**
@@ -1104,3 +1115,4 @@ typedef struct {
}
@end
@@ -22,7 +22,7 @@ Pod::Spec.new do |s|
s.author = "Meta Platforms, Inc. and its affiliates"
s.platforms = min_supported_versions
s.source = source
s.source_files = "**/RCTAppDependencyProvider.{h,mm}"
s.source_files = ["**/RCTAppDependencyProvider.{h,mm}", "**/*-Swift.{h,mm}"]
# This guard prevent to install the dependencies when we run `pod install` in the old architecture.
s.pod_target_xcconfig = {
+57 -26
View File
@@ -1,42 +1,73 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import * as React from 'react';
import React from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
View,
useColorScheme,
TextInput,
Button,
} from 'react-native';
function App(): React.ReactNode {
const isDarkMode = useColorScheme() === 'dark';
import NativeLocalStorage from './specs/NativeLocalStorage';
const EMPTY = '<empty>';
function App(): React.JSX.Element {
const [value, setValue] = React.useState<string | null>(null);
const [editingValue, setEditingValue] = React.useState<
string | null
>(null);
React.useEffect(() => {
const storedValue = NativeLocalStorage?.getItem('myKey');
setValue(storedValue ?? '');
}, []);
function saveValue() {
NativeLocalStorage?.setItem(editingValue ?? EMPTY, 'myKey');
setValue(editingValue);
}
function clearAll() {
NativeLocalStorage?.clear();
setValue('');
}
function deleteValue() {
NativeLocalStorage?.removeItem('myKey');
setValue('');
}
return (
<SafeAreaView>
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
<ScrollView contentInsetAdjustmentBehavior="automatic">
<View>
<Text style={styles.title}>Hello, World!</Text>
</View>
</ScrollView>
<SafeAreaView style={{flex: 1}}>
<Text style={styles.text}>
Current stored value is: {value ?? 'No Value'}
</Text>
<TextInput
placeholder="Enter the text you want to store"
style={styles.textInput}
onChangeText={setEditingValue}
/>
<Button title="Save" onPress={saveValue} />
<Button title="Delete" onPress={deleteValue} />
<Button title="Clear" onPress={clearAll} />
</SafeAreaView>
);
}
const styles = StyleSheet.create({
title: {
fontSize: 24,
fontWeight: '600',
text: {
margin: 10,
fontSize: 20,
},
textInput: {
margin: 10,
height: 40,
borderColor: 'black',
borderWidth: 1,
paddingLeft: 5,
paddingRight: 5,
borderRadius: 5,
},
});
+1
View File
@@ -12,3 +12,4 @@ gem 'bigdecimal'
gem 'logger'
gem 'benchmark'
gem 'mutex_m'
gem 'ffi', '1.17.2'
+2 -1
View File
@@ -62,7 +62,7 @@ GEM
escape (0.0.4)
ethon (0.16.0)
ffi (>= 1.15.0)
ffi (1.17.0)
ffi (1.17.2)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
@@ -104,6 +104,7 @@ DEPENDENCIES
bigdecimal
cocoapods (~> 1.13, != 1.15.1, != 1.15.0)
concurrent-ruby (<= 1.3.4)
ffi (= 1.17.2)
logger
mutex_m
xcodeproj (< 1.26.0)
@@ -0,0 +1,8 @@
//
// HelloWorld-Bridging-Header.h
// HelloWorld
//
// Created by Riccardo Cipolleschi on 20/07/2025.
//
#import <React-RCTAppDelegate/RCTDefaultReactNativeFactoryDelegate.h>
@@ -8,12 +8,14 @@
/* Begin PBXBuildFile section */
00E356F31AD99517003FC87E /* HelloWorldTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* HelloWorldTests.m */; };
03B621F332C31CE9F6F5498B /* libPods-HelloWorld-HelloWorldTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 87728D6F9B41932E3190949E /* libPods-HelloWorld-HelloWorldTests.a */; };
0B80405BD1BDA4AEC91E6FD6 /* libPods-HelloWorld.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 46A81F6F6DFF1CFBA9274965 /* libPods-HelloWorld.a */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
6EA01F72FAC10D00AECACF94 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0EC7AB76F90EED035707BA4E /* PrivacyInfo.xcprivacy */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
CD087E612E2D0DE600049510 /* RCTNativeLocalStorageModuleProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = CD087E602E2D0DE600049510 /* RCTNativeLocalStorageModuleProvider.mm */; };
CD087E642E2D0E9F00049510 /* NativeLocalStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD087E632E2D0E9F00049510 /* NativeLocalStorage.swift */; };
CDA0ED1A2D0B2D810079F561 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDA0ED192D0B2D810079F561 /* AppDelegate.swift */; };
D462E9F4436EDF91C8A1FA0A /* Pods_HelloWorld.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3F2101C8317E5C933C1BD4C /* Pods_HelloWorld.framework */; };
D693EA25CB545D6C1C7F8538 /* Pods_HelloWorld_HelloWorldTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A97924660E462ECF2425C3A /* Pods_HelloWorld_HelloWorldTests.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -36,12 +38,16 @@
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = HelloWorld/Info.plist; sourceTree = "<group>"; };
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = HelloWorld/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
3B4392A12AC88292D35C810B /* Pods-HelloWorld.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.debug.xcconfig"; path = "Target Support Files/Pods-HelloWorld/Pods-HelloWorld.debug.xcconfig"; sourceTree = "<group>"; };
46A81F6F6DFF1CFBA9274965 /* libPods-HelloWorld.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HelloWorld.a"; sourceTree = BUILT_PRODUCTS_DIR; };
5709B34CF0A7D63546082F79 /* Pods-HelloWorld.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.release.xcconfig"; path = "Target Support Files/Pods-HelloWorld/Pods-HelloWorld.release.xcconfig"; sourceTree = "<group>"; };
5B7EB9410499542E8C5724F5 /* Pods-HelloWorld-HelloWorldTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld-HelloWorldTests.debug.xcconfig"; path = "Target Support Files/Pods-HelloWorld-HelloWorldTests/Pods-HelloWorld-HelloWorldTests.debug.xcconfig"; sourceTree = "<group>"; };
7A97924660E462ECF2425C3A /* Pods_HelloWorld_HelloWorldTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HelloWorld_HelloWorldTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = HelloWorld/LaunchScreen.storyboard; sourceTree = "<group>"; };
87728D6F9B41932E3190949E /* libPods-HelloWorld-HelloWorldTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HelloWorld-HelloWorldTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
89C6BE57DB24E9ADA2F236DE /* Pods-HelloWorld-HelloWorldTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld-HelloWorldTests.release.xcconfig"; path = "Target Support Files/Pods-HelloWorld-HelloWorldTests/Pods-HelloWorld-HelloWorldTests.release.xcconfig"; sourceTree = "<group>"; };
B3F2101C8317E5C933C1BD4C /* Pods_HelloWorld.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HelloWorld.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CD087E5F2E2D0DE600049510 /* RCTNativeLocalStorageModuleProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTNativeLocalStorageModuleProvider.h; sourceTree = "<group>"; };
CD087E602E2D0DE600049510 /* RCTNativeLocalStorageModuleProvider.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNativeLocalStorageModuleProvider.mm; sourceTree = "<group>"; };
CD087E632E2D0E9F00049510 /* NativeLocalStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeLocalStorage.swift; sourceTree = "<group>"; };
CD087E652E2D0F9700049510 /* HelloWorld-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "HelloWorld-Bridging-Header.h"; sourceTree = "<group>"; };
CDA0ED192D0B2D810079F561 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = HelloWorld/AppDelegate.swift; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
@@ -51,7 +57,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D693EA25CB545D6C1C7F8538 /* Pods_HelloWorld_HelloWorldTests.framework in Frameworks */,
03B621F332C31CE9F6F5498B /* libPods-HelloWorld-HelloWorldTests.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -59,7 +65,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D462E9F4436EDF91C8A1FA0A /* Pods_HelloWorld.framework in Frameworks */,
0B80405BD1BDA4AEC91E6FD6 /* libPods-HelloWorld.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -92,6 +98,7 @@
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
0EC7AB76F90EED035707BA4E /* PrivacyInfo.xcprivacy */,
CDA0ED192D0B2D810079F561 /* AppDelegate.swift */,
CD087E652E2D0F9700049510 /* HelloWorld-Bridging-Header.h */,
);
name = HelloWorld;
sourceTree = "<group>";
@@ -100,8 +107,8 @@
isa = PBXGroup;
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
B3F2101C8317E5C933C1BD4C /* Pods_HelloWorld.framework */,
7A97924660E462ECF2425C3A /* Pods_HelloWorld_HelloWorldTests.framework */,
46A81F6F6DFF1CFBA9274965 /* libPods-HelloWorld.a */,
87728D6F9B41932E3190949E /* libPods-HelloWorld-HelloWorldTests.a */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -116,6 +123,7 @@
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
CD087E5E2E2D0DD100049510 /* NativeLocalStorage */,
13B07FAE1A68108700A75B9A /* HelloWorld */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* HelloWorldTests */,
@@ -148,6 +156,16 @@
path = Pods;
sourceTree = "<group>";
};
CD087E5E2E2D0DD100049510 /* NativeLocalStorage */ = {
isa = PBXGroup;
children = (
CD087E5F2E2D0DE600049510 /* RCTNativeLocalStorageModuleProvider.h */,
CD087E602E2D0DE600049510 /* RCTNativeLocalStorageModuleProvider.mm */,
CD087E632E2D0E9F00049510 /* NativeLocalStorage.swift */,
);
path = NativeLocalStorage;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -206,7 +224,7 @@
TestTargetID = 13B07F861A680F5B00A75B9A;
};
13B07F861A680F5B00A75B9A = {
LastSwiftMigration = 1610;
LastSwiftMigration = 1640;
};
};
};
@@ -394,7 +412,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CD087E642E2D0E9F00049510 /* NativeLocalStorage.swift in Sources */,
CDA0ED1A2D0B2D810079F561 /* AppDelegate.swift in Sources */,
CD087E612E2D0DE600049510 /* RCTNativeLocalStorageModuleProvider.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -484,6 +504,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = HelloWorld;
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../packages/react-native";
SWIFT_OBJC_BRIDGING_HEADER = "HelloWorld-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -511,6 +532,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = HelloWorld;
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../packages/react-native";
SWIFT_OBJC_BRIDGING_HEADER = "HelloWorld-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
@@ -596,8 +618,11 @@
"-DFOLLY_CFG_NO_COROUTINES=1",
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
);
OTHER_LDFLAGS = "$(inherited) ";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../react-native";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../packages/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
USE_HERMES = true;
@@ -676,8 +701,11 @@
"-DFOLLY_CFG_NO_COROUTINES=1",
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
);
OTHER_LDFLAGS = "$(inherited) ";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../react-native";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../packages/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
VALIDATE_PRODUCT = YES;
+2 -1
View File
@@ -26,7 +26,6 @@
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsLocalNetworking</key>
@@ -34,6 +33,8 @@
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>RCTNewArchEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
@@ -0,0 +1,34 @@
//
// NativeLocalStorage.swift
// HelloWorld
//
// Created by Riccardo Cipolleschi on 20/07/2025.
//
import Foundation
import ReactAppDependencyProvider
class NativeLocalStorage: NSObject, NativeLocalStorageSpec {
private let storage: UserDefaults = UserDefaults(suiteName: "local-storage") ?? .standard
func setItem(_ value: String, key: String) {
storage.setValue(value, forKey: key)
}
func getItem(_ key: String) -> String? {
storage.string(forKey: key)
}
func removeItem(_ key: String) {
storage.removeObject(forKey: key)
}
func clear() {
storage.dictionaryRepresentation().keys.forEach { storage.removeObject(forKey: $0) }
}
static func moduleName() -> String! {
return "NativeLocalStorage"
}
}
@@ -0,0 +1,17 @@
//
// RCTNativeLocalStorageModuleProvider.h
// HelloWorld
//
// Created by Riccardo Cipolleschi on 20/07/2025.
//
#import <Foundation/Foundation.h>
#import <ReactCommon/RCTTurboModule.h>
NS_ASSUME_NONNULL_BEGIN
@interface RCTNativeLocalStorageModuleProvider : NSObject <RCTModuleProvider>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,23 @@
//
// RCTNativeLocalStorageModuleProvider.m
// HelloWorld
//
// Created by Riccardo Cipolleschi on 20/07/2025.
//
#import "RCTNativeLocalStorageModuleProvider.h"
#import <NativeLocalStorageSpec/NativeLocalStorageSpec.h>
#import "HelloWorld-Swift.h"
@implementation RCTNativeLocalStorageModuleProvider
- (Class<RCTModule>)getAppleModule
{
return [NativeLocalStorage class];
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
return std::make_shared<facebook::react::NativeLocalStorageSpecJSI>(params);
}
@end
+14 -1
View File
@@ -34,5 +34,18 @@
},
"engines": {
"node": ">= 20.19.4"
}
},
"codegenConfig": {
"name": "NativeLocalStorageSpec",
"type": "modules",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.nativelocalstorage"
},
"ios": {
"modulesProvider": {
"NativeLocalStorage": "RCTNativeLocalStorageModuleProvider"
}
}
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ program
android: {},
},
root: path.join(__dirname, '../'),
reactNativePath: path.join(__dirname, '../../react-native'),
reactNativePath: path.join(__dirname, '../../../packages/react-native'),
},
{
experimentalDebugger: false,
@@ -0,0 +1,13 @@
import type {TurboModule} from 'react-native';
import {TurboModuleRegistry} from 'react-native';
export interface Spec extends TurboModule {
setItem(value: string, key: string): void;
getItem(key: string): string | null;
removeItem(key: string): void;
clear(): void;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'NativeLocalStorage',
);