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 — Native Module Swift
@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 — Native Module Android
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 از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید