Native Module adalah kelas Java atau Objective-C yang membuat API native platform tersedia dari JavaScript di React Native. Setiap modul didaftarkan di Bridge dan mengekspor metode yang dapat dipanggil dari kode JS seperti fungsi biasa. Menurut Meta, 2024, Native Module tetap menjadi cara utama integrasi kode platform dalam aplikasi React Native.
Poin Utama
Native Module adalah elemen arsitektur React Native yang memungkinkan eksekusi kode dalam bahasa platform (Objective-C/Swift untuk iOS, Java/Kotlin untuk Android) dan mengembalikan hasil ke JavaScript. Tanpa Native Module, akses ke kemampuan native perangkat — kamera, GPS, akselerometer, sistem file, atau Bluetooth — tidak mungkin dilakukan.
React Native hadir dengan seperangkat Native Module bawaan: CameraRoll, AsyncStorage, Geolocation, NetInfo dan lainnya. Namun untuk tugas spesifik — integrasi SDK pihak ketiga, bekerja dengan sensor perangkat keras, atau proses latar belakang — pengembang membuat modul sendiri. Menurut survei State of React Native 2024, 67% pengembang menggunakan setidaknya satu Native Module kustom dalam proyek mereka.
Arsitektur Native Module tergantung pada versi React Native. Dalam arsitektur klasik (React Native 0.72 dan lebih lama) modul terhubung melalui Bridge dan berkomunikasi dengan JS secara asinkron melalui serialisasi JSON. Dalam arsitektur baru (React Native 0.76+) modul dapat bekerja sebagai Turbo Module, menggunakan JSI untuk akses sinkron tanpa serialisasi.
Pembuatan Native Module untuk iOS dimulai dengan mendeklarasikan kelas Objective-C yang mengimplementasikan protokol RCTBridgeModule. Makro RCT_EXPORT_MODULE mendaftarkan modul di Bridge, dan RCT_EXPORT_METHOD mengekspor metode yang dapat diakses dari JavaScript.
// ImageCompressor.m — Native Module untuk 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
Metode compressImage menerima jalur gambar dan kualitas kompresi (0.0–1.0), memproses data di sisi native dan mengembalikan jalur ke file yang dikompresi. Keuntungan utama — kompresi dilakukan dengan kode native, yang secara signifikan lebih cepat dan lebih efisien memori daripada operasi serupa di JavaScript.
Untuk modul Swift, anotasi @objc digunakan sebelum kelas dan metode agar dapat diakses oleh runtime Objective-C yang digunakan Bridge. Kelas harus mewarisi NSObject dan mengimplementasikan 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)
}
}
Di Android Native Module dibuat sebagai kelas Java yang mewarisi ReactContextBaseJavaModule. Anotasi @ReactMethod mengekspor metode ke Bridge. Untuk mengembalikan hasil, digunakan antarmuka Promise dari 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());
}
}
}
Metode getName() mengembalikan nama modul yang akan digunakan untuk mengaksesnya dari JavaScript. Dalam contoh yang diberikan, modul didaftarkan sebagai ImageCompressor. Anotasi @ReactMethod memberi tahu Bridge bahwa metode harus diekspor. Penting: metode harus void dan hanya menerima tipe yang didukung Bridge: String, Boolean, Integer, Double, ReadableArray, ReadableMap, Promise.
Setelah pembuatan kelas, modul harus didaftarkan dalam paket aplikasi. Untuk ini, dibuat kelas yang mengimplementasikan ReactPackage dan ditambahkan ke daftar modul dalam metode createNativeModules.
// ImageCompressorPackage.java — pendaftaran modul
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();
}
}
Setelah pembuatan modul untuk kedua platform, modul-modul tersebut harus didaftarkan di React Native. Untuk Android, paket ditambahkan di MainApplication.java dalam metode getPackages(). Untuk iOS, modul secara otomatis didaftarkan melalui makro RCT_EXPORT_MODULE, tetapi pendaftaran manual dalam file AppDelegate.mm juga dapat digunakan.
// MainApplication.java — menambahkan paket ke 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;
}
};
}
Setelah pendaftaran, modul tersedia di JavaScript melalui NativeModules. React Native secara otomatis mengganti nama modul yang ditentukan dalam getName() untuk Android atau dalam RCT_EXPORT_MODULE untuk iOS.
// Menggunakan Native Module dari 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('Dikompresi:', result);
return result;
} catch (error) {
console.error('Kompresi gagal:', error);
throw error;
}
}
Perbandingan Native Module klasik dan Turbo Module membantu memahami pendekatan mana yang harus dipilih untuk proyek baru. Kedua mekanisme menyediakan akses ke kode native, tetapi berbeda secara fundamental dalam arsitektur dan kinerja.
| Karakteristik | Native Module (Bridge) | Turbo Module (JSI) |
|---|---|---|
| Komunikasi | Asinkron melalui JSON | Sinkron melalui JSI |
| Serialisasi | JSON setiap panggilan | Tanpa penyalinan data |
| Tipifikasi | Manual, tanpa generasi | Otomatis melalui Codegen |
| Pemuatan | Saat inisialisasi aplikasi | Lazy load |
| Kompatibilitas | Semua versi React Native | React Native 0.73+ |
Untuk proyek yang ada di React Native 0.72 dan lebih lama, Native Module klasik tetap menjadi pilihan utama. Untuk proyek baru, disarankan menggunakan Turbo Module, terutama jika diperlukan kinerja tinggi pada panggilan metode native yang sering. Dengan pembaruan bertahap React Native, komunitas bergerak menuju transisi penuh ke arsitektur baru.
Pertanyaan yang Sering Diajukan
Ya, Bridge mendukung callback. Alih-alih Promise, dapat menggunakan fungsi panggilan balik RCTResponseSenderBlock di iOS dan Callback di Android. Namun Promise dianggap sebagai standar modern dan direkomendasikan untuk modul baru.
Native Module di-debug seperti kode native biasa — atur breakpoint di Xcode atau Android Studio. Untuk iOS gunakan skema build dengan React Native, untuk Android — konfigurasi Debug. Titik masuk — metode yang dipanggil dari JS.
Native Module melalui Bridge tidak mendukung data biner (NSData/byte[]), objek kustom, dan fungsi. Untuk mengirim gambar, gunakan jalur file atau string base64. Turbo Module melalui JSI menghilangkan sebagian dari batasan ini.
Biasanya tidak — Native Module API stabil dan kompatibel mundur. Saat beralih ke arsitektur baru (Turbo Module), modul diadaptasi melalui dekorator, tetapi kode yang ada tetap berfungsi.
Gunakan RCTEventEmitter di iOS atau DeviceEventEmitter di Android. Modul mengirim event, dan sisi JS berlangganan melalui NativeEventEmitter dari react-native. Ini berguna untuk data streaming dan event sensor.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga