Native Module w React Native — co to jest, jak tworzyć i integrować

Autor: IT Sectr Opublikowano: 2026-06-03 Czas czytania: 9 min

Native Module to klasa Java lub Objective-C, która udostępnia natywne API platformy z poziomu JavaScript w React Native. Każdy moduł rejestruje się w Bridge i eksportuje metody, które można wywoływać z kodu JS jak zwykłe funkcje. Według danych Meta, 2024, Native Module pozostaje głównym sposobem integracji kodu platformowego w aplikacjach React Native.

Najważniejsze

  • Native Module — most między JavaScript a natywnym kodem iOS lub Android.
  • Rejestracja — moduł deklaruje się przez adnotacje iOS (RCT_EXPORT_MODULE) lub Android (@ReactMethod).
  • Typy danych — obsługiwane są prymitywy, ciągi znaków, tablice, słowniki i Promise.
  • Użycie — z JS moduł jest dostępny przez NativeModules.NazwaModułu.
  • Nowa architektura — w React Native 0.76+ Native Module może działać jako Turbo Module przez JSI.

Co to jest Native Module?

Native Module to element architektoniczny React Native, który pozwala wykonywać kod w języku platformy (Objective-C/Swift dla iOS, Java/Kotlin dla Android) i zwracać wynik do JavaScript. Bez Native Module niemożliwy jest dostęp do natywnych możliwości urządzenia — aparatu, GPS, akcelerometru, systemu plików czy Bluetooth.

React Native jest dostarczany z zestawem wbudowanych Native Module: CameraRoll, AsyncStorage, Geolocation, NetInfo i inne. Jednak do specyficznych zadań — integracja zewnętrznego SDK, praca z czujnikami sprzętowymi czy procesy w tle — programista tworzy własne moduły. Według ankiety State of React Native 2024, 67% programistów używa co najmniej jednego niestandardowego Native Module w swoich projektach.

Architektura Native Module zależy od wersji React Native. W klasycznej architekturze (React Native 0.72 i starsze) moduł podłącza się przez Bridge i komunikuje z JS asynchronicznie przez serializację JSON. W nowej architekturze (React Native 0.76+) moduł może działać jako Turbo Module, używając JSI do synchronicznego dostępu bez serializacji.

Tworzenie Native Module dla iOS

Tworzenie Native Module dla iOS zaczyna się od zadeklarowania klasy Objective-C, która implementuje protokół RCTBridgeModule. Makro RCT_EXPORT_MODULE rejestruje moduł w Bridge, a RCT_EXPORT_METHOD eksportuje metodę dostępną z JavaScript.

objective-c
// ImageCompressor.m — Native Module dla 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

Metoda compressImage przyjmuje ścieżkę do obrazu i jakość kompresji (0.0–1.0), przetwarza dane po stronie natywnej i zwraca ścieżkę do skompresowanego pliku. Kluczowa zaleta — kompresja wykonuje się natywnym kodem, co jest znacznie szybsze i bardziej wydajne pamięciowo niż analogiczna operacja w JavaScript.

Dla modułów Swift używa się adnotacji @objc przed klasą i metodami, aby były dostępne dla środowiska uruchomieniowego Objective-C, z którym pracuje Bridge. Klasa musi dziedziczyć po NSObject i implementować RCTBridgeModule.

swift
// 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)
    }
}

Tworzenie Native Module dla Android

Na Android Native Module tworzy się jako klasę Java dziedziczącą po ReactContextBaseJavaModule. Adnotacja @ReactMethod eksportuje metodę do Bridge. Do zwracania wyniku używa się interfejsu Promise z com.facebook.react.bridge.

java
// 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());
        }
    }
}

Metoda getName() zwraca nazwę modułu, pod którą będzie dostępny z JavaScript. W podanym przykładzie moduł rejestruje się jako ImageCompressor. Adnotacja @ReactMethod wskazuje Bridge, że metoda powinna być wyeksportowana. Ważne: metody muszą być void i przyjmować tylko typy obsługiwane przez Bridge: String, Boolean, Integer, Double, ReadableArray, ReadableMap, Promise.

Po utworzeniu klasy moduł należy zarejestrować w pakiecie aplikacji. W tym celu tworzy się klasę implementującą ReactPackage i dodaje do listy modułów w metodzie createNativeModules.

java
// ImageCompressorPackage.java — rejestracja modułu
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();
    }
}

Rejestracja i używanie Native Module

Po utworzeniu modułów dla obu platform należy je zarejestrować w React Native. Dla Android pakiet dodaje się w MainApplication.java w metodzie getPackages(). Dla iOS moduł rejestruje się automatycznie przez makro RCT_EXPORT_MODULE, ale można też użyć ręcznej rejestracji w pliku AppDelegate.mm.

java
// MainApplication.java — dodawanie pakietu do 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;
        }
    };
}

Po rejestracji moduł staje się dostępny w JavaScript przez NativeModules. React Native automatycznie podstawia nazwę modułu podaną w getName() dla Android lub w RCT_EXPORT_MODULE dla iOS.

js
// Używanie Native Module z 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('Skompresowano:', result);
    return result;
  } catch (error) {
    console.error('Kompresja nieudana:', error);
    throw error;
  }
}

Native Module vs Turbo Module: porównanie

Porównanie klasycznego Native Module i Turbo Module pomaga zrozumieć, które podejście wybrać dla nowego projektu. Oba mechanizmy zapewniają dostęp do kodu natywnego, ale zasadniczo różnią się architekturą i wydajnością.

CechaNative Module (Bridge)Turbo Module (JSI)
KomunikacjaAsynchroniczna przez JSONSynchroniczna przez JSI
SerializacjaJSON przy każdym wywołaniuBez kopiowania danych
TypowanieRęczne, bez generacjiAutomatyczne przez Codegen
ŁadowaniePrzy inicjalizacji aplikacjiLeniwę (lazy load)
KompatybilnośćWszystkie wersje React NativeReact Native 0.73+

Dla istniejących projektów na React Native 0.72 i starszych klasyczne Native Module pozostają głównym wyborem. Dla nowych projektów zaleca się używanie Turbo Module, szczególnie jeśli wymagana jest wysoka wydajność przy częstych wywołaniach metod natywnych. Wraz ze stopniową aktualizacją React Native społeczność zmierza w kierunku pełnego przejścia na nową architekturę.

Często zadawane pytania

Czy można przekazywać callback zamiast Promise w Native Module?

Tak, Bridge obsługuje callback. Zamiast Promise można używać funkcji zwrotnych RCTResponseSenderBlock w iOS i Callback w Android. Jednak Promise uważa się za nowoczesny standard i jest zalecany dla nowych modułów.

Jak debugować Native Module w Xcode lub Android Studio?

Native Module debuguje się jak zwykły kod natywny — ustaw punkty przerwania w Xcode lub Android Studio. Dla iOS użyj schematu budowania z React Native, dla Android — konfiguracji Debug. Punkt wejścia — metody wywołane z JS.

Jakich typów danych NIE obsługuje Native Module?

Native Module przez Bridge nie obsługuje danych binarnych (NSData/byte[]), obiektów niestandardowych i funkcji. Do przesyłania obrazów użyj ścieżki do pliku lub ciągu base64. Turbo Module przez JSI usuwa część tych ograniczeń.

Czy trzeba przepisywać Native Module przy aktualizacji React Native?

Zazwyczaj nie — Native Module API jest stabilny i wstecznie zgodny. Przy przejściu na nową architekturę (Turbo Module) moduł adaptuje się przez dekoratory, ale istniejący kod nadal działa.

Jak wysłać zdarzenie z Native Module do JavaScript?

Użyj RCTEventEmitter w iOS lub DeviceEventEmitter w Android. Moduł wysyła zdarzenie, a strona JS subskrybuje przez NativeEventEmitter z react-native. Jest to przydatne do danych strumieniowych i zdarzeń czujników.

Podsumowanie

  • Native Module — klasa Java/Objective-C, która udostępnia natywne API z poziomu JavaScript w React Native.
  • Rejestracja — iOS używa RCT_EXPORT_MODULE, Android — ReactContextBaseJavaModule z @ReactMethod.
  • Wywołanie — z JS moduł jest dostępny przez NativeModules.Modul i obsługuje Promise i callback.
  • Typy danych — Bridge przesyła tylko typy zgodne z JSON: ciągi, liczby, tablice, słowniki.
  • Wydajność — klasyczny Native Module ustępuje Turbo Module z powodu asynchroniczności i serializacji.
  • Migracja — przy przejściu na React Native 0.76+ moduł może działać jako Turbo Module bez przepisywania logiki.
  • Zastosowanie — Native Module jest niezastąpiony dla aparatu, Bluetooth, systemu plików i integracji SDK.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również