I take a reference from official document, it's working. https://reactnative.dev/docs/turbo-native-modules-introduction?platforms=ios
But when I try to set multiple specs for iOS, I get the error
10 duplicate symbols
Linker command failed with exit code 1 (use -v to see invocation)
Here is what I try:
step 1 npx @react-native-community/cli@latest init FirstApp
"react-native": "0.77.0",
step 2 add codegen setting in package.json
"codegenConfig": {
"libraries": [
{
"name": "NativeLocalStorageSpec",
"type": "modules",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.nativelocalstorage"
}
},
{
"name": "NativeBluetoothSpec",
"type": "modules",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.nativebluetooth"
}
}
]
},
step 3 add file under specs folder (RN root project)
specs/NativeBluetooth.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
enableBluetooth(): void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('NativeBluetooth');
specs/NativeLocalStorage.ts
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');
step 4 type terminal command
cd ios
bundle install
bundle exec pod install
and then open XCode build the project get the error:
10 duplicate symbols
Linker command failed with exit code 1 (use -v to see invocation)
What is the problem ?
The implementation has changed I believe. Here is the documentation
Here is how I have done it:
"codegenConfig": {
"name": "MyAppSpec",
"type": "all",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.myApp"
}
},
Then in the specs folder:
// NativeIconchooser.ts
import { TurboModule, TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
changeIcon: (iconName: string) => Promise<string>;
getAvailableIcons: () => Promise<string[]>;
getIcon: () => string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('NativeIconChooser');
// NativeSentiveInfo.ts
import { TurboModule, TurboModuleRegistry } from 'react-native';
export interface RNSensitiveInfoOptions {
keychainService?: string;
sharedPreferencesName?: string;
}
export interface Spec extends TurboModule {
setItem: (key: string, value: string, options: RNSensitiveInfoOptions) => Promise<null>;
getItem: (key: string, options: RNSensitiveInfoOptions) => Promise<string>;
deleteItem: (key: string, options: RNSensitiveInfoOptions) => Promise<null>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('NativeSensitiveInfo');
In objective-c:
// RCTNativeIconChooser.h
#import <UIKit/UIKit.h>
#import "MyAppSpec/MyAppSpec.h"
@interface RCTNativeIconChooser : NSObject <NativeIconChooserSpec>
@end
// RCTNativeIconChooser.mm
#import "RCTNativeIconChooser.h"
@implementation RCTNativeIconChooser
RCT_EXPORT_MODULE(NativeIconChooser)
+ (BOOL)requiresMainQueueSetup {
return NO;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
return std::make_shared<facebook::react::NativeIconChooserSpecJSI>(params);
}
- (NSString *)getIcon {
NSString *currentIcon = [[UIApplication sharedApplication] alternateIconName];
if (currentIcon) {
return currentIcon;
}
return @"default";
}
// ...
@end
// RCTNativeSensitiveInfo.h
#import <Foundation/Foundation.h>
#import <Security/Security.h>
#import "MyAppSpec/MyAppSpec.h"
NS_ASSUME_NONNULL_BEGIN
@interface RCTNativeSensitiveInfo : NSObject <NativeSensitiveInfoSpec>
@end
NS_ASSUME_NONNULL_END