Native Module је Java или Objective-C класа која чини нативне API-је платформе доступним из JavaScript-а у React Native-у. Сваки модул се региструје у Bridge-у и извози методе које се могу позивати из JS кода као обичне функције. Према подацима Meta, 2024, Native Module остаје основни начин интеграције платформског кода у React Native апликације.
Главно
Native Module је архитектонски елемент React Native-а који омогућава извршавање кода на језику платформе (Objective-C/Swift за iOS, Java/Kotlin за Android) и враћање резултата у JavaScript. Без Native Module-а немогуће је приступити нативним могућностима уређаја — камери, GPS-у, акцелерометру, систему датотека или Bluetooth-у.
React Native долази са сетом уграђених Native Module-а: CameraRoll, AsyncStorage, Geolocation, NetInfo и други. Међутим, за специфичне задатке — интеграцију спољног SDK-ја, рад са хардверским сензорима или позадинске процесе — програмер креира сопствене модуле. Према анкети State of React Native 2024, 67% програмера користи бар један прилагођени Native Module у својим пројектима.
Архитектура Native Module-а зависи од верзије React Native-а. У класичној архитектури (React Native 0.72 и старије) модул се повезује преко Bridge-а и комуницира са JS-ом асинхроно преко JSON серијализације. У новој архитектури (React Native 0.76+) модул може радити као Turbo Module, користећи JSI за синхрони приступ без серијализације.
Креирање Native Module-а за iOS почиње декларацијом Objective-C класе која имплементира протокол RCTBridgeModule. Мацро RCT_EXPORT_MODULE региструје модул у Bridge-у, а RCT_EXPORT_METHOD извози метод доступан из JavaScript-а.
// ImageCompressor.m — Native Module за iOS
@interface ImageCompressor () RCT_EXPORT_MODULE()
@end
@implementation ImageCompressor
RCT_EXPORT_METHOD(compressImage:(NSString *)imagePath
quality:(NSNumber *)quality
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
NSData *compressedData = [UIImageJPEGRepresentation(image, quality.floatValue)];
NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"compressed.jpg"];
[compressedData writeToFile:outputPath atomically:YES];
resolve(outputPath);
}
@end
Метод compressImage прима путању до слике и квалитет компресије (0.0–1.0), обрађује податке на нативној страни и враћа путању до компресованог фајла. Кључна предност — компресија се извршава нативним кодом, што је значајно брже и ефикасније по меморији од аналогне операције у JavaScript-у.
За Swift модуле користи се анотација @objc испред класе и метода како би били доступни Objective-C рутајму са којим Bridge ради. Класа мора да наслеђује NSObject и имплементира RCTBridgeModule.
// ImageCompressor.swift — Swift Native Module
@objc(ImageCompressor)
class ImageCompressor: NSObject {
@objc
func compressImage(
_ imagePath: String,
quality: Float,
resolver: @escaping RCTPromiseResolveBlock,
rejecter: @escaping RCTPromiseRejectBlock
) {
guard let image = UIImage(contentsOfFile: imagePath) else {
rejecter("FILE_ERROR", "Cannot load image", nil)
return
}
guard let data = image.jpegData(compressionQuality: CGFloat(quality)) else {
rejecter("COMPRESS_ERROR", "Compression failed", nil)
return
}
let outputPath = NSTemporaryDirectory() + "compressed.jpg"
try? data.write(to: URL(fileURLWithPath: outputPath))
resolver(outputPath)
}
}
На Android-у Native Module се креира као Java класа која наслеђује ReactContextBaseJavaModule. Анотација @ReactMethod извози метод у Bridge. За враћање резултата користи се интерфејс Promise из com.facebook.react.bridge.
// ImageCompressorModule.java — Android Native Module
public class ImageCompressorModule
extends ReactContextBaseJavaModule {
@Override
public String getName() {
return "ImageCompressor";
}
@ReactMethod
public void compressImage(
String imagePath,
Float quality,
Promise promise) {
try {
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
File outputFile = new File(
ReactNative.getApplicationContext()
.getCacheDir(), "compressed.jpg");
FileOutputStream fos = new FileOutputStream(outputFile);
bitmap.compress(
Bitmap.CompressFormat.JPEG,
(int)(quality * 100), fos);
fos.close();
promise.resolve(outputFile.getAbsolutePath());
} catch (Exception e) {
promise.reject("COMPRESS_ERROR", e.getMessage());
}
}
}
Метод getName() враћа име модула под којим ће бити доступан из JavaScript-а. У наведеном примеру модул се региструје као ImageCompressor. Анотација @ReactMethod указује Bridge-у да метод треба да буде извезен. Важно: методи морају бити void и прихватати само типове које Bridge подржава: String, Boolean, Integer, Double, ReadableArray, ReadableMap, Promise.
Након креирања класе, модул је потребно регистровати у пакету апликације. За то се креира класа која имплементира ReactPackage и додаје у листу модула у методи createNativeModules.
// ImageCompressorPackage.java — регистрација модула
public class ImageCompressorPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
return Arrays.asList(
new ImageCompressorModule(reactContext)
);
}
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
Након креирања модула за обе платформе, потребно их је регистровати у React Native-у. За Android пакет се додаје у MainApplication.java у методу getPackages(). За iOS модул се аутоматски региструје преко макроа RCT_EXPORT_MODULE, али се може користити и ручна регистрација у фајлу AppDelegate.mm.
// MainApplication.java — додавање пакета у React Native
import com.yourapp.nativemodules.ImageCompressorPackage;
public class MainApplication extends Application
implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages =
new PackageList(this).getPackages();
packages.add(new ImageCompressorPackage());
return packages;
}
};
}
Након регистрације, модул постаје доступан у JavaScript-у преко NativeModules-а. React Native аутоматски подмеће име модула наведено у getName() за Android или у RCT_EXPORT_MODULE за iOS.
// Коришћење Native Module-а из JavaScript-а
import { NativeModules } from 'react-native';
import { Platform } from 'react-native';
const ImageCompressor = NativeModules.ImageCompressor;
async function compressPhoto(uri: string) {
try {
const result = await ImageCompressor.compressImage(
uri.replace('file://', ''), 0.8
);
console.log('Компресовано:', result);
return result;
} catch (error) {
console.error('Компресија неуспешна:', error);
throw error;
}
}
Поређење класичног Native Module-а и Turbo Module-а помаже да се разуме који приступ одабрати за нови пројекат. Оба механизма пружају приступ нативном коду, али се суштински разликују по архитектури и перформансама.
| Карактеристика | Native Module (Bridge) | Turbo Module (JSI) |
|---|---|---|
| Комуникација | Асинхрона преко JSON-а | Синхрона преко JSI-ја |
| Серијализација | JSON при сваком позиву | Без копирања података |
| Типизација | Ручна, без генерисања | Аутоматска преко Codegen-а |
| Учитавање | При иницијализацији апликације | Лења (lazy load) |
| Компатибилност | Све верзије React Native-а | React Native 0.73+ |
За постојеће пројекте на React Native 0.72 и старијим, класични Native Module остаје основни избор. За нове пројекте препоручује се коришћење Turbo Module-а, посебно ако је потребна висока перформанса при честим позивима нативних метода. Са постепеним ажурирањем React Native-а, заједница се креће ка потпуном преласку на нову архитектуру.
Често постављана питања
Да, Bridge подржава callback. Уместо Promise-а могу се користити функције повратног позива RCTResponseSenderBlock у iOS-у и Callback у Android-у. Међутим, Promise се сматра савременим стандардом и препоручује се за нове модуле.
Native Module се отклања као обичан нативни код — поставите брејкпоинте у Xcode-у или Android Studio-у. За iOS користите шему изградње са React Native-ом, за Android — Debug конфигурацију. Тачка улаза — методи позвани из JS-а.
Native Module преко Bridge-а не подржава бинарне податке (NSData/byte[]), прилагођене објекте и функције. За пренос слика користите путању до фајла или base64 стринг. Turbo Module преко JSI-ја уклања део ових ограничења.
Обично не — Native Module API је стабилан и уназад компатибилан. При преласку на нову архитектуру (Turbo Module) модул се прилагођава преко декоратора, али постојећи код наставља да ради.
Користите RCTEventEmitter у iOS-у или DeviceEventEmitter у Android-у. Модул шаље догађај, а JS страна се претплаћује преко NativeEventEmitter-а из react-native-а. Ово је корисно за токове података и догађаје сензора.
Завршни преглед
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође