Ta strona została przetłumaczona przez PageTurner AI (beta). Nie jest oficjalnie zatwierdzona przez projekt. Znalazłeś błąd? Zgłoś problem →
Moduły natywne
Twój kod aplikacji React Native może potrzebować interakcji z natywnymi API platformy, które nie są dostarczane przez React Native ani istniejącą bibliotekę. Możesz samodzielnie napisać kod integracyjny, używając modułu Turbo Native. Ten przewodnik pokaże ci, jak go stworzyć.
Podstawowe kroki to:
-
zdefiniuj typowaną specyfikację JavaScript przy użyciu jednego z popularnych języków adnotacji typów: Flow lub TypeScript;
-
skonfiguruj system zarządzania zależnościami do uruchomienia Codegen, który konwertuje specyfikację na interfejsy w językach natywnych;
-
napisz kod aplikacji korzystając ze swojej specyfikacji; oraz
-
napisz natywny kod platformy wykorzystując wygenerowane interfejsy, aby zaimplementować i podłączyć swój kod natywny do środowiska wykonawczego React Native.
Przejdźmy przez każdy z tych kroków, budując przykładowy moduł Turbo Native. Reszta przewodnika zakłada, że utworzyłeś aplikację uruchamiając polecenie:
npx @react-native-community/cli@latest init TurboModuleExample --version 0.76.0
Trwałe przechowywanie danych
Ten przewodnik pokaże ci, jak zaimplementować Web Storage API: localStorage. To API jest znane programistom Reacta, którzy mogą pisać kod aplikacji w twoim projekcie.
Aby to działało na urządzeniach mobilnych, musimy użyć API Androida i iOS:
-
Android: SharedPreferences, oraz
-
iOS: NSUserDefaults.
1. Deklaracja typowanej specyfikacji
React Native udostępnia narzędzie o nazwie Codegen, które przyjmuje specyfikację napisaną w TypeScript lub Flow i generuje platformowo specyficzny kod dla Androida i iOS. Specyfikacja deklaruje metody i typy danych, które będą przekazywane między twoim kodem natywnym a środowiskiem wykonawczym JavaScript React Native. Moduł Turbo Native to zarówno twoja specyfikacja, natywny kod, który piszesz, jak i interfejsy Codegen wygenerowane z twojej specyfikacji.
Aby utworzyć plik specyfikacji:
-
W głównym folderze aplikacji utwórz nowy folder o nazwie
specs. -
Utwórz nowy plik o nazwie
NativeLocalStorage.ts.
Wszystkie typy, które możesz użyć w swojej specyfikacji oraz typy natywne, które są generowane, znajdziesz w dokumentacji Dodatku.
Jeśli chcesz zmienić nazwę modułu i powiązanego pliku specyfikacji, upewnij się, że zawsze używasz prefiksu 'Native' (np. NativeStorage lub NativeUsersDefault).
Oto implementacja specyfikacji localStorage:
- TypeScript
- Flow
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',
);
import type {TurboModule} from 'react-native';
import {TurboModule, TurboModuleRegistry} from 'react-native';
export interface Spec extends TurboModule {
setItem(value: string, key: string): void;
getItem(key: string): ?string;
removeItem(key: string): void;
clear(): void;
}
2. Konfiguracja uruchomienia Codegen
Specyfikacja jest używana przez narzędzia Codegen React Native do generowania interfejsów specyficznych dla platform i szablonów kodu. Aby to zrobić, Codegen musi wiedzieć, gdzie znaleźć naszą specyfikację i co z nią zrobić. Zaktualizuj swój package.json, dodając:
"start": "react-native start",
"test": "jest"
},
"codegenConfig": {
"name": "NativeLocalStorageSpec",
"type": "modules",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.nativelocalstorage"
}
},
"dependencies": {
Po skonfigurowaniu Codegen, musimy przygotować nasz kod natywny do integracji z wygenerowanym kodem.
- Android
- iOS
Codegen is executed through the generateCodegenArtifactsFromSchema Gradle task:
cd android
./gradlew generateCodegenArtifactsFromSchema
BUILD SUCCESSFUL in 837ms
14 actionable tasks: 3 executed, 11 up-to-date
This is automatically run when you build your Android application.
Codegen is run as part of the script phases that's automatically added to the project generated by CocoaPods.
cd ios
bundle install
bundle exec pod install
The output will look like this:
...
Framework build type is static library
[Codegen] Adding script_phases to ReactCodegen.
[Codegen] Generating ./build/generated/ios/ReactCodegen.podspec.json
[Codegen] Analyzing /Users/me/src/TurboModuleExample/package.json
[Codegen] Searching for codegen-enabled libraries in the app.
[Codegen] Found TurboModuleExample
[Codegen] Searching for codegen-enabled libraries in the project dependencies.
[Codegen] Found react-native
...
3. Pisanie kodu aplikacji z użyciem modułu Turbo Native
Korzystając z NativeLocalStorage, oto zmodyfikowany App.tsx, który zawiera tekst do utrwalenia, pole wprowadzania i przyciski do aktualizacji tej wartości.
TurboModuleRegistry obsługuje 2 tryby pobierania modułu Turbo Native:
-
get<T>(name: string): T | nullzwrócinull, jeśli moduł Turbo Native jest niedostępny. -
getEnforcing<T>(name: string): Tzgłosi wyjątek, jeśli moduł Turbo Native jest niedostępny. Zakłada, że moduł jest zawsze dostępny.
import React from 'react';
import {
SafeAreaView,
StyleSheet,
Text,
TextInput,
Button,
} from 'react-native';
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 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({
text: {
margin: 10,
fontSize: 20,
},
textInput: {
margin: 10,
height: 40,
borderColor: 'black',
borderWidth: 1,
paddingLeft: 5,
paddingRight: 5,
borderRadius: 5,
},
});
export default App;
4. Pisanie natywnego kodu platformy
Po przygotowaniu wszystkiego zaczniemy pisać natywny kod platformy. Robimy to w 2 częściach:
Ten poradnik pokazuje, jak utworzyć moduł Turbo Native Module, który działa wyłącznie z Nową Architekturą. Jeśli potrzebujesz obsługiwać zarówno Nową Architekturę, jak i Starszą Architekturę, zapoznaj się z naszym przewodnikiem dotyczącym zgodności wstecznej.
- Android
- iOS
Ta strona została przetłumaczona przez PageTurner AI (beta). Nie jest oficjalnie zatwierdzona przez projekt. Znalazłeś błąd? Zgłoś problem →
Teraz czas napisać kod na platformie Android, aby zapewnić, że localStorage przetrwa po zamknięciu aplikacji.
Pierwszym krokiem jest zaimplementowanie wygenerowanego interfejsu NativeLocalStorageSpec:
- Java
- Kotlin
package com.nativelocalstorage;
import android.content.Context;
import android.content.SharedPreferences;
import com.nativelocalstorage.NativeLocalStorageSpec;
import com.facebook.react.bridge.ReactApplicationContext;
public class NativeLocalStorageModule extends NativeLocalStorageSpec {
public static final String NAME = "NativeLocalStorage";
public NativeLocalStorageModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return NAME;
}
@Override
public void setItem(String value, String key) {
SharedPreferences sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(key, value);
editor.apply();
}
@Override
public String getItem(String key) {
SharedPreferences sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE);
String username = sharedPref.getString(key, null);
return username;
}
@Override
public void removeItem(String key) {
SharedPreferences sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE);
sharedPref.edit().remove(key).apply();
}
@Override
public void clear() {
SharedPreferences sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE);
sharedPref.edit().clear().apply();
}
}
package com.nativelocalstorage
import android.content.Context
import android.content.SharedPreferences
import com.nativelocalstorage.NativeLocalStorageSpec
import com.facebook.react.bridge.ReactApplicationContext
class NativeLocalStorageModule(reactContext: ReactApplicationContext) : NativeLocalStorageSpec(reactContext) {
override fun getName() = NAME
override fun setItem(value: String, key: String) {
val sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
val editor = sharedPref.edit()
editor.putString(key, value)
editor.apply()
}
override fun getItem(key: String): String? {
val sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
val username = sharedPref.getString(key, null)
return username.toString()
}
override fun removeItem(key: String) {
val sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
val editor = sharedPref.edit()
editor.remove(key)
editor.apply()
}
override fun clear() {
val sharedPref = getReactApplicationContext().getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
val editor = sharedPref.edit()
editor.clear()
editor.apply()
}
companion object {
const val NAME = "NativeLocalStorage"
}
}
Następnie musimy utworzyć NativeLocalStoragePackage. Dostarcza on obiekt rejestrujący nasz moduł w środowisku wykonawczym React Native poprzez opakowanie go jako Base Native Package:
- Java
- Kotlin
package com.nativelocalstorage;
import com.facebook.react.BaseReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import java.util.HashMap;
import java.util.Map;
public class NativeLocalStoragePackage extends BaseReactPackage {
@Override
public NativeModule getModule(String name, ReactApplicationContext reactContext) {
if (name.equals(NativeLocalStorageModule.NAME)) {
return new NativeLocalStorageModule(reactContext);
} else {
return null;
}
}
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
return new ReactModuleInfoProvider() {
@Override
public Map<String, ReactModuleInfo> getReactModuleInfos() {
Map<String, ReactModuleInfo> map = new HashMap<>();
map.put(NativeLocalStorageModule.NAME, new ReactModuleInfo(
NativeLocalStorageModule.NAME, // name
NativeLocalStorageModule.NAME, // className
false, // canOverrideExistingModule
false, // needsEagerInit
false, // isCXXModule
true // isTurboModule
));
return map;
}
};
}
}
package com.nativelocalstorage
import com.facebook.react.BaseReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfo
import com.facebook.react.module.model.ReactModuleInfoProvider
class NativeLocalStoragePackage : BaseReactPackage() {
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
if (name == NativeLocalStorageModule.NAME) {
NativeLocalStorageModule(reactContext)
} else {
null
}
override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
mapOf(
NativeLocalStorageModule.NAME to ReactModuleInfo(
name = NativeLocalStorageModule.NAME,
className = NativeLocalStorageModule.NAME,
canOverrideExistingModule = false,
needsEagerInit = false,
isCxxModule = false,
isTurboModule = true
)
)
}
}
Na koniec musimy wskazać React Native w naszej głównej aplikacji, jak znaleźć ten Package. Proces ten nazywamy "rejestracją" pakietu w React Native.
W tym przypadku dodajesz go do wartości zwracanej przez metodę getPackages.
Później dowiesz się, jak dystrybuować swoje moduły natywne jako pakiety npm, które nasze narzędzia do budowania automatycznie podlinkują.
- Java
- Kotlin
package com.inappmodule;
import android.app.Application;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactHost;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactHost;
import com.facebook.react.defaults.DefaultReactNativeHost;
import com.facebook.soloader.SoLoader;
import com.nativelocalstorage.NativeLocalStoragePackage;
import java.util.ArrayList;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost reactNativeHost = new DefaultReactNativeHost(this) {
@Override
public List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new NativeLocalStoragePackage());
return packages;
}
@Override
public String getJSMainModuleName() {
return "index";
}
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
public boolean isNewArchEnabled() {
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}
@Override
public boolean isHermesEnabled() {
return BuildConfig.IS_HERMES_ENABLED;
}
};
@Override
public ReactHost getReactHost() {
return DefaultReactHost.getDefaultReactHost(getApplicationContext(), reactNativeHost);
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, false);
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
DefaultNewArchitectureEntryPoint.load();
}
}
}
package com.inappmodule
import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.soloader.SoLoader
import com.nativelocalstorage.NativeLocalStoragePackage
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
add(NativeLocalStoragePackage())
}
override fun getJSMainModuleName(): String = "index"
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
}
override val reactHost: ReactHost
get() = getDefaultReactHost(applicationContext, reactNativeHost)
override fun onCreate() {
super.onCreate()
SoLoader.init(this, false)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
load()
}
}
}
Możesz teraz zbudować i uruchomić swój kod na emulatorze:
- npm
- Yarn
npm run android
yarn run android
Ta strona została przetłumaczona przez PageTurner AI (beta). Nie jest oficjalnie zatwierdzona przez projekt. Znalazłeś błąd? Zgłoś problem →
Czas napisać kod platformy iOS, aby zapewnić, że localStorage przetrwa po zamknięciu aplikacji.
Przygotuj swój projekt w Xcode
Musimy przygotować Twój projekt iOS za pomocą Xcode. Po wykonaniu tych 6 kroków będziesz miał RCTNativeLocalStorage implementujący wygenerowany interfejs NativeLocalStorageSpec.
- Otwórz workspace Xcode wygenerowany przez CocoaPods:
cd ios
open TurboModuleExample.xcworkspace
- Kliknij prawym przyciskiem na aplikację i wybierz
New Group, nazwij nową grupęNativeLocalStorage.
- W grupie
NativeLocalStorageutwórzNew→File from Template.
- Użyj szablonu
Cocoa Touch Class.
- Nazwij klasę Cocoa Touch
RCTNativeLocalStoragew językuObjective-C.
- Zmień nazwę
RCTNativeLocalStorage.m→RCTNativeLocalStorage.mm, tworząc plik Objective-C++.
Implementacja localStorage z NSUserDefaults
Zacznij od aktualizacji RCTNativeLocalStorage.h:
// RCTNativeLocalStorage.h
// TurboModuleExample
#import <Foundation/Foundation.h>
#import <NativeLocalStorageSpec/NativeLocalStorageSpec.h>
NS_ASSUME_NONNULL_BEGIN
@interface RCTNativeLocalStorage : NSObject
@interface RCTNativeLocalStorage : NSObject <NativeLocalStorageSpec>
@end
Następnie zaktualizuj implementację, aby używała NSUserDefaults z niestandardową nazwą zestawu.
// RCTNativeLocalStorage.m
// TurboModuleExample
#import "RCTNativeLocalStorage.h"
static NSString *const RCTNativeLocalStorageKey = @"local-storage";
@interface RCTNativeLocalStorage()
@property (strong, nonatomic) NSUserDefaults *localStorage;
@end
@implementation RCTNativeLocalStorage
RCT_EXPORT_MODULE(NativeLocalStorage)
- (id) init {
if (self = [super init]) {
_localStorage = [[NSUserDefaults alloc] initWithSuiteName:RCTNativeLocalStorageKey];
}
return self;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
return std::make_shared<facebook::react::NativeLocalStorageSpecJSI>(params);
}
- (NSString * _Nullable)getItem:(NSString *)key {
return [self.localStorage stringForKey:key];
}
- (void)setItem:(NSString *)value
key:(NSString *)key {
[self.localStorage setObject:value forKey:key];
}
- (void)removeItem:(NSString *)key {
[self.localStorage removeObjectForKey:key];
}
- (void)clear {
NSDictionary *keys = [self.localStorage dictionaryRepresentation];
for (NSString *key in keys) {
[self removeItem:key];
}
}
@end
Ważne kwestie:
-
RCT_EXPORT_MODULEeksportuje i rejestruje moduł za pomocą identyfikatora, którego użyjemy do dostępu w środowisku JavaScript:NativeLocalStorage. Więcej szczegółów znajdziesz w tej dokumentacji. -
Możesz użyć Xcode do przejścia do protokołu
@protocol NativeLocalStorageSpec. Xcode może też wygenerować za Ciebie szablony metod.
Budowanie i uruchamianie kodu na symulatorze
- npm
- Yarn
npm run ios
yarn run ios