← Back to list

Let’s Practice Tubor Module Of React Native

Run npx create-react-native-library@latest

John Luong · 2025-10-18 07:16 · 0 claps · 5.0 min read
#react-native #turbo-module #native-modules #react-native-modules
Open on Medium ↗
Wiki topics: 🌐 · Web Development 📱 · Mobile Development 📚 · Books & Reading

Let’s Practice Tubor Module Of React Native

  1. Run npx create-react-native-library@latest
npx create-react-native-library@latest react-native-fast-restart
cd react-native-fast-restart && yarn install
cd example && yarn install
cd ios && pod install && cd ..
  1. Goto src/NativeFastRestart.ts define your function. Here i will make restartApp function to restart app and return string
import { TurboModuleRegistry, type TurboModule } from 'react-native';

export interface Spec extends TurboModule {
  restartApp(): string;
}

export default TurboModuleRegistry.getEnforcing<Spec>('FastRestart');
  1. Goto src/index.ts
import FastRestart from './NativeFastRestart';

export function restartApp() {
  return FastRestart.restartApp();
}
  1. Goto example/src/App.tsx add
import { Button, StyleSheet, View } from 'react-native';
import { restartApp } from 'react-native-fast-restart';

export default function App() {
  return (
    <View style={styles.container}>
      <Button
        title="Restart"
        onPress={() => {
          console.log('restartApp()', restartApp());
          restartApp();
        }}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Continue we will make Native Module for IOS

  1. Goto FastRestart.podspec add swift to source_files to code swift

s.source_files = “ios/*/.{h,m,mm,cpp,swift}”

  1. Run pod install to gen codegen (
npx react-native codegen && cd example/ios && pod install
  1. Open xcode and then open example/ios

  2. In xcode select Pods/Development Pods/FastRestart and click right choose New File From Template

  1. Choose Swift -> Next

  1. Put name FastRestartImpl.swift in your ios in root folder (not in example/ios)-> create

  1. If the message “you want to create FastRestartImpl Header” show-> choose Don’t create bridge header

  2. In FastRestartImpl.swift, you can code in header. Remember add @objc public to expose file, NSObject react native need NSObject to expose

import Foundation
import UIKit
import React

@objc public class FastRestartImpl: NSObject {
  @objc public func restartApp() -> String {
    DispatchQueue.main.async {
      print("[FastRestart] Triggering reload command...")
      RCTTriggerReloadCommandListeners("FastRestartImpl.restartApp() called")
    }
    return "restartApp"
  }
}
  1. In FastRestart.mm

Full Code

#import "FastRestart.h"
#import "FastRestart-Swift.h"
@implementation FastRestart {
  FastRestartImpl *moduleImpl;
}

-(instancetype) init {
  self = [super init];
  if(self) {
    moduleImpl = [FastRestartImpl new];
  }
  return self;
}

- (NSString *)restartApp {
  return [moduleImpl restartApp];
}

- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
    (const facebook::react::ObjCTurboModule::InitParams &)params
{
    return std::make_shared<facebook::react::NativeFastRestartSpecJSI>(params);
}

+ (NSString *)moduleName
{
  return @"FastRestart";
}

@end

In Full Code Remember To make your code swift file working to need to have:

#import "FastRestart-Swift.h"
@implementation FastRestart {
  FastRestartImpl *moduleImpl;
}

-(instancetype) init {
  self = [super init];
  if(self) {
    moduleImpl = [FastRestartImpl new];
  }
  return self;
}
  1. in FastRestart.h add #import <React/RCTReloadCommand.h> to swift file use RCTTriggerReloadCommandListeners to restart App
#import <FastRestartSpec/FastRestartSpec.h>
#import <React/RCTReloadCommand.h>
@interface FastRestart : NSObject <NativeFastRestartSpec>

@end

Done For IOS. You can go to example to build and test module

cd example/ios && pod install && cd .. && yarn build 

Continue we will make Native Module for Android

Go to root project/android/src/main/java/com/fastrestart (not android in example) add

At FastRestartModule.kt

package com.fastrestart

import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.annotations.ReactModule
import android.content.Intent
import android.os.Process

@ReactModule(name = FastRestartModule.NAME)
class FastRestartModule(reactContext: ReactApplicationContext) :
  NativeFastRestartSpec(reactContext) {

  override fun getName(): String {
    return NAME
  }

  /**
   * Restarts the entire application by fetching the main launch Intent,
   * setting necessary flags to clear the task stack, and launching the activity.
   */
  override fun restartApp(): String {
    val context = reactApplicationContext.applicationContext

    // 1. Get the Intent that was used to launch this application
    val packageManager = context.packageManager
    val intent = packageManager.getLaunchIntentForPackage(context.packageName)

    if (intent != null) {
      // 2. Set flags to clear the entire task stack before launching the new activity
      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)

      // 3. Start the activity (restarting the app)
      context.startActivity(intent)

      // 4. Optionally, kill the old process to ensure a clean restart
      // This is often needed for a true, fresh restart.
      Process.killProcess(Process.myPid())
    }
    return "Restart App"
  }

  companion object {
    const val NAME = "FastRestart"
  }
}

At FastRestartPackage.kt you dont need do anything

package com.fastrestart

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

class FastRestartPackage : BaseReactPackage() {
  override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
    return if (name == FastRestartModule.NAME) {
      FastRestartModule(reactContext)
    } else {
      null
    }
  }

  override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
    return ReactModuleInfoProvider {
      val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
      moduleInfos[FastRestartModule.NAME] = ReactModuleInfo(
        FastRestartModule.NAME,
        FastRestartModule.NAME,
        false,  // canOverrideExistingModule
        false,  // needsEagerInit
        false,  // isCxxModule
        true // isTurboModule
      )
      moduleInfos
    }
  }
}

Done For Android and go cd example && yarn android to build

Advanced Data type

in root project -> src/NativeFastRestart.ts

import { TurboModuleRegistry, type TurboModule } from 'react-native';

export interface Spec extends TurboModule {
  getStatus(): string;
  requestPermission(): Promise<string>
  openPdf(onSuccess: (uri: string) => void, onError: (error: string) => void): void
  openPdf(uri: string): Promise<string>;
}

export default TurboModuleRegistry.getEnforcing<Spec>('FastRestart');

in root project -> ios/FastRestartImpl.swift

import Foundation

@objc public class FastRestartImpl: NSObject {
  @objc public func getStatus() -> String {
    return "Permission Status"
  }

  @objc public func requestPermission(onChanged: @escaping (String) -> Void){
      onChanged("Permission Status Changed")
  }

  @objc public func openPdf(onSuccess: @escaping (String) -> Void, onError: @escaping (String) -> Void){
    onSuccess("PDF Opened")
  }

  @objc public func openPdf(uri: String, resolver: @escaping (String) -> Void, rejecter: @escaping (String) -> Void) {
     DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
        resolver("Opened PDF at page from uri: \(uri)")
     }
  }
}

in root project -> ios/FastRestart.mm

#import "FastRestart.h"
#import "FastRestart-Swift.h"
@implementation FastRestart {
  FastRestartImpl *moduleImpl;
}

-(instancetype) init {
  self = [super init];
  if(self) {
    moduleImpl = [FastRestartImpl new];
  }
  return self;
}

- (NSString *)getStatus {
  return [moduleImpl getStatus];
}

- (void)requestPermission:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
  return [moduleImpl requestPermission:^(NSString * _Nonnull status){
    resolve(status)
  }];
}

- (void)openPdf:(RCTResponseSenderBlock)onSuccess onError:(RCTResponseSenderBlock)onError {
  return [moduleImpl openPdf:^(NSString * _Nonnull result){
    onSuccess(@[result]);
  } onError:^(NSString * _Nonnull error){
    onError(@[error]);
  }];
}

- (void)openPdf:(nonnull NSString *)uri
         resolve:(nonnull RCTPromiseResolveBlock)resolve
          reject:(nonnull RCTPromiseRejectBlock)reject
{
  [moduleImpl openPdfWithUri:uri
                   resolver:^(NSString * _Nonnull result) {
                     resolve(result);
                   }
                   rejecter:^(NSString * _Nonnull error) {
                     reject(@"E_OPEN_PDF", error, nil);
                   }];
}

- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
    (const facebook::react::ObjCTurboModule::InitParams &)params
{
    return std::make_shared<facebook::react::NativeFastRestartSpecJSI>(params);
}

+ (NSString *)moduleName
{
  return @"FastRestart";
}

@end

in root project -> android/src/main/java/com/fastrestart/FastRestartModule.kt

package com.fastrestart

import android.content.Intent
import android.net.Uri
import android.util.Log
import androidx.core.content.FileProvider
import com.facebook.react.bridge.*
import com.facebook.react.module.annotations.ReactModule
import java.io.File

@ReactModule(name = FastRestartModule.NAME)
class FastRestartModule(reactContext: ReactApplicationContext) :
  NativeFastRestartSpec(reactContext) {

  private val context = reactContext

  override fun getName() = NAME

  override fun getStatus(): String {
    return "Permission Status"
  }

  override fun requestPermission(promise: Promise) {
    promise.resolve("Permission Status Changed")
  }

  override fun openPdf(onSuccess: Callback, onError: Callback) {
    onSuccess.invoke("PDF Opened")
  }

  // ✅ New method: openPdfFile({ uri, options })
  override fun openPdfFile(params: ReadableMap, promise: Promise) {
    try {
      val uriString = params.getString("uri")
      if (uriString == null) {
        promise.reject("E_INVALID_URI", "Missing or invalid uri")
        return
      }

      val uri = Uri.parse(uriString)
      Log.d("FastRestart", "openPdfFile called with: $uri")

      // Nếu là file nội bộ, cần convert sang FileProvider URI
      val file = File(uri.path ?: "")
      val pdfUri = if (file.exists()) {
        FileProvider.getUriForFile(context, "${context.packageName}.provider", file)
      } else {
        uri // fallback nếu là remote http://
      }

      // Mở PDF viewer
      val intent = Intent(Intent.ACTION_VIEW).apply {
        setDataAndType(pdfUri, "application/pdf")
        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
      }

      context.startActivity(intent)

      promise.resolve("Opened PDF successfully")
    } catch (e: Exception) {
      e.printStackTrace()
      promise.reject("E_PDF_OPEN_FAILED", e.message, e)
    }
  }

  companion object {
    const val NAME = "FastRestart"
  }
}

메타데이터
post_id
94eb44ecdaec
slug
lets-practice-tubor-module-of-react-native-94eb44ecdaec
url
https://medium.com/@thongluong.edu/lets-practice-tubor-module-of-react-native-94eb44ecdaec
canonical_url
https://medium.com/@thongluong.edu/lets-practice-tubor-module-of-react-native-94eb44ecdaec
author_url
https://medium.com/@thongluong.edu
status
ok
fetched_at
2026-08-03 03:40:02