PageSourceSearch

https://reactnative.dev/assets/js/b4ecf95f.202c9d36.js

js reactnative.dev collected 2026-09-24 07:16:09 UTC 70,746 bytes, 2 lines download raw bytes

1"use strict";(self.webpackChunkreact_native_website=self.webpackChunkreact_native_website||[]).push([["11928"],{27411(e,a,n){n.r(a),n.d(a,{metadata:()=>t,default:()=>g,frontMatter:()=>d,contentTitle:()=>u,toc:()=>v,assets:()=>h});var t=JSON.parse('{"id":"legacy/native-modules-android","title":"Android Native Modules","description":"Welcome to Native Modules for Android. Please start by reading the Native Modules Intro for an intro to what native modules are.","source":"@site/versioned_docs/version-0.78/legacy/native-modules-android.md","sourceDirName":"legacy","slug":"/legacy/native-modules-android","permalink":"/docs/0.78/legacy/native-modules-android","draft":false,"unlisted":false,"editUrl":"[{\\"label\\":\\"Edit page for next release\\",\\"href\\":\\"https://github.com/facebook/react-native-website/edit/main/docs/legacy/native-modules-android.md\\"},{\\"label\\":\\"Edit page for 0.78 release\\",\\"href\\":\\"https://github.com/facebook/react-native-website/edit/main/website/versioned_docs/version-0.78/legacy/native-modules-android.md\\"}]","tags":[],"version":"0.78","lastUpdatedAt":1763547393000,"frontMatter":{"id":"native-modules-android","title":"Android Native Modules"},"sidebar":"docs","previous":{"title":"Native Modules Intro","permalink":"/docs/0.78/legacy/native-modules-intro"},"next":{"title":"iOS Native Modules","permalink":"/docs/0.78/legacy/native-modules-ios"}}'),i=n(62540),o=n(43023),r=n(33264),l=n(77779),s=n(98263),c=n(38469);let d={id:"native-modules-android",title:"Android Native Modules"},u,h={},v=[...r.RM,{value:"Create a Calendar Native Module",id:"create-a-calendar-native-module",level:2},{value:"Setup",id:"setup",level:3},{value:"Create A Custom Native Module File",id:"create-a-custom-native-module-file",level:3},{value:"Module Name",id:"module-name",level:3},{value:"Export a Native Method to JavaScript",id:"export-a-native-method-to-javascript",level:3},{value:"Synchronous Methods",id:"synchronous-methods",level:3},{value:"Register the Module (Android Specific)",id:"register-the-module-android-specific",level:3},{value:"Test What You Have Built",id:"test-what-you-have-built",level:3},{value:"Building as You Iterate",id:"building-as-you-iterate",level:3},{value:"Recap\u2728",id:"recap",level:3},{value:"Beyond a Calendar Native Module",id:"beyond-a-calendar-native-module",level:2},{value:"Better Native Module Export",id:"better-native-module-export",level:3},{value:"Argument Types",id:"argument-types",level:3},{value:"Exporting Constants",id:"exporting-constants",level:3},{value:"Callbacks",id:"callbacks",level:3},{value:"Promises",id:"promises",level:3},{value:"Sending Events to JavaScript",id:"sending-events-to-javascript",level:3},{value:"Getting Activity Result from startActivityForResult",id:"getting-activity-result-from-startactivityforresult",level:3},{value:"Listening to Lifecycle Events",id:"listening-to-lifecycle-events",level:3},{value:"Threading",id:"threading",level:3}];function m(e){let a={a:"a",blockquote:"blockquote",code:"code",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(r.Ay,{}),"\n",(0,i.jsxs)(a.p,{children:["Welcome to Native Modules for Android. Please start by reading the ",(0,i.jsx)(a.a,{href:"native-modules-intro",children:"Native Modules Intro"})," for an intro to what native modules are."]}),"\n",(0,i.jsx)(a.h2,{id:"create-a-calendar-native-module",children:"Create a Calendar Native Module"}),"\n",(0,i.jsxs)(a.p,{children:["In the following guide you will create a native module, ",(0,i.jsx)(a.code,{children:"CalendarModule"}),", that will allow you to access Android\u2019s calendar APIs from JavaScript. By the end, you will be able to call ",(0,i.jsx)(a.code,{children:"CalendarModule.createCalendarEvent('Dinner Party', 'My House');"})," from JavaScript, invoking a Java/Kotlin method that creates a calendar event."]}),"\n",(0,i.jsx)(a.h3,{id:"setup",children:"Setup"}),"\n",(0,i.jsx)(a.p,{children:"To get started, open up the Android project within your React Native application in Android Studio. You can find your Android project here within a React Native app:"}),"\n",(0,i.jsxs)("figure",{children:[(0,i.jsx)("img",{src:"/docs/assets/native-modules-android-open-project.png",width:"500",alt:"Image of opening up an Android project within a React Native app inside of Android Studio."}),(0,i.jsx)("figcaption",{children:"Image of where you can find your Android project"})]}),"\n",(0,i.jsx)(a.p,{children:"We recommend using Android Studio to write your native code. Android Studio is an IDE built for Android development and using it will help you resolve minor issues like code syntax errors quickly."}),"\n",(0,i.jsxs)(a.p,{children:["We also recommend enabling ",(0,i.jsx)(a.a,{href:"https://docs.gradle.org/2.9/userguide/gradle_daemon.html",children:"Gradle Daemon"})," to speed up builds as you iterate on Java/Kotlin code."]}),"\n",(0,i.jsx)(a.h3,{id:"create-a-custom-native-module-file",children:"Create A Custom Native Module File"}),"\n",(0,i.jsxs)(a.p,{children:["The first step is to create the (",(0,i.jsx)(a.code,{children:"CalendarModule.java"})," or ",(0,i.jsx)(a.code,{children:"CalendarModule.kt"}),") Java/Kotlin file inside ",(0,i.jsx)(a.code,{children:"android/app/src/main/java/com/your-app-name/"})," folder (the folder is the same for both Kotlin and Java). This Java/Kotlin file will contain your native module Java/Kotlin class."]}),"\n",(0,i.jsxs)("figure",{children:[(0,i.jsx)("img",{src:"/docs/assets/native-modules-android-add-class.png",width:"700",alt:"Image of adding a class called CalendarModule.java within the Android Studio."}),(0,i.jsx)("figcaption",{children:"Image of how to add the CalendarModuleClass"})]}
1),"\n",(0,i.jsx)(a.p,{children:"Then add the following content:"}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"package com.your-apps-package-name; // replace your-apps-package-name with your app\u2019s package name\nimport com.facebook.react.bridge.NativeModule;\nimport com.facebook.react.bridge.ReactApplicationContext;\nimport com.facebook.react.bridge.ReactContext;\nimport com.facebook.react.bridge.ReactContextBaseJavaModule;\nimport com.facebook.react.bridge.ReactMethod;\nimport java.util.Map;\nimport java.util.HashMap;\n\npublic class CalendarModule extends ReactContextBaseJavaModule {\n   CalendarModule(ReactApplicationContext context) {\n       super(context);\n   }\n}\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"package com.your-apps-package-name; // replace your-apps-package-name with your app\u2019s package name\nimport com.facebook.react.bridge.N
1ativeModule\nimport com.facebook.react.bridge.ReactApplicationContext\nimport com.facebook.react.bridge.ReactContext\nimport com.facebook.react.bridge.ReactContextBaseJavaModule\nimport com.facebook.react.bridge.ReactMethod\n\nclass CalendarModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {...}\n"})})})]}),"\n",(0,i.jsxs)(a.p,{children:["As you can see, your ",(0,i.jsx)(a.code,{children:"CalendarModule"})," class extends the ",(0,i.jsx)(a.code,{children:"ReactContextBaseJavaModule"})," class. For Android, Java/Kotlin native modules are written as classes that extend ",(0,i.jsx)(a.code,{children:"ReactContextBaseJavaModule"})," and implement the functionality required by JavaScript."]}),"\n",(0,i.jsxs)(a.blockquote,{children:["\n",(0,i.jsxs)(a.p,{children:["It is worth noting that technically Java/Kotlin classes only need to extend the ",(0,i.jsx)(a.code,{children:"BaseJavaModule"})," class or implement the ",(0,i.jsx)(a.code,{children:"NativeModule"})," interface to be considered a Native Module by React Native."]}),"\n"]}),"\n",(0,i.jsxs)(a.blockquote,{children:["\n",(0,i.jsxs)(a.p,{children:["However we recommend that you use ",(0,i.jsx)(a.code,{children:"ReactContextBaseJavaModule"}),", as shown above. ",(0,i.jsx)(a.code,{children:"ReactContextBaseJavaModule"})," gives access to the ",(0,i.jsx)(a.code,{children:"ReactApplicationContext"})," (RAC), which is useful for Native Modules that need to hook into activity lifecycle methods. Using ",(0,i.jsx)(a.code,{children:"ReactContextBaseJavaModule"})," will also make it easier to make your native module type-safe in the future. For native module type-safety, which is coming in future releases, React Native looks at each native module's JavaScript spec and generates an abstract base class that extends ",(0,i.jsx)(a.code,{children:"ReactContextBaseJavaModule"}),"."]}),"\n"]}),"\n",(0,i.jsx)(a.h3,{id:"module-name",children:"Module Name"}),"\n",(0,i.jsxs)(a.p,{children:["All Java/Kotlin native modules in Android need to implement the ",(0,i.jsx)(a.code,{children:"getName()"})," method. This method returns a string, which represents the name of the native module. The native module can then be accessed in JavaScript using its name. For example, in the below code snippet, ",(0,i.jsx)(a.code,{children:"getName()"})," returns ",(0,i.jsx)(a.code,{children:'"CalendarModule"'}),"."]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:'// add to CalendarModule.java\n@Override\npublic String getName() {\n   return "CalendarModule";\n}\n'})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:'// add to CalendarModule.kt\noverride fun getName() = "CalendarModule"\n'})})})]}),"\n",(0,i.jsx)(a.p,{children:"The native module can then be accessed in JS like this:"}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const {CalendarModule} = ReactNative.NativeModules;\n"})}),"\n",(0,i.jsx)(a.h3,{id:"export-a-native-method-to-javascript",children:"Export a Native Method to JavaScript"}),"\n",(0,i.jsxs)(a.p,{children:["Next you will need to add a method to your native module that will create calendar events and can be invoked in JavaScript. All native module methods meant to be invoked from JavaScript must be annotated with ",(0,i.jsx)(a.code,{children:"@ReactMethod"}),"."]}),"\n",(0,i.jsxs)(a.p,{children:["Set up a method ",(0,i.jsx)(a.code,{children:"createCalendarEvent()"})," for ",(0,i.jsx)(a.code,{children:"CalendarModule"})," that can be invoked in JS through ",(0,i.jsx)(a.code,{children:"CalendarModule.createCalendarEvent()"}),". For now, the method will take in a name and location as strings. Argument type options will be covered shortly."]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"@ReactMethod\npublic void createCalendarEvent(String name, String location) {\n}\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"@ReactMethod fun createCalendarEvent(name: String, location: String) {}\n"})})})]}),"\n",(0,i.jsxs)(a.p,{children:["Add a debug log in the method to confirm it has been invoked when you call it from your application. Below is an example of how you can import and use the ",(0,i.jsx)(a.a,{href:"https://developer.android.com/reference/android/util/Log",children:"Log"})," class from the Android util package:"]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:'import android.util.Log;\n\n@ReactMethod\npublic void createCalendarEvent(String name, String location) {\n   Log.d("CalendarModule", "Create event called with name: " + name\n   + " and location: " + location);\n}\n'})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:'import android.util.Log\n\n@ReactMethod\nfun createCalendarEvent(name: String, location: String) {\n    Log.d("CalendarModule", "Create event called with name: $name and location: $location")\n}\n'})})})]}),"\n",(0,i.jsxs)(a.p,{children:["Once you finish implementing the native module and hook it up in JavaScript, you can follow ",(0,i.jsx)(a.a,{href:"https://developer.android.com/studio/debug/am-logcat.html",children:"these steps"})," to view the logs from your app."]}),"\n",(0,i.jsx)(a.h3,{id:"synchronous-methods",children:"Synchronous Methods"}),"\n",(0,i.jsxs)(a.p,{children:["You can pass ",(0,i.jsx)(a.code,{children:"isBlockingSynchronousMethod = true"})," to a native method to mark it as a synchronous method."]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"@ReactMethod(isBlockingSynchronousMethod = true)\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"@ReactMethod(isBlockingSynchronousMethod = true)\n"})})})]}),"\n",(0,i.jsxs)(a.p,{children:["At the moment, we do not recommend this, since calling methods synchronously can have strong performance penalties and introduce threading-related bugs to your native modules. Additionally, please note that if you choose to enable ",(0,i.jsx)(a.code,{children:"isBlockingSynchronousMethod"}),", your app can no longer use the Google Chrome debugger. This is because synchronous methods require the JS VM to 
1share memory with the app. For the Google Chrome debugger, React Native runs inside the JS VM in Google Chrome, and communicates asynchronously with the mobile devices via WebSockets."]}),"\n",(0,i.jsx)(a.h3,{id:"register-the-module-android-specific",children:"Register the Module (Android Specific)"}),"\n",(0,i.jsxs)(a.p,{children:["Once a native module is written, it needs to be registered with React Native. In order to do so, you need to add your native module to a ",(0,i.jsx)(a.code,{children:"ReactPackage"})," and register the ",(0,i.jsx)(a.code,{children:"ReactPackage"})," with React Native. During initialization, React Native will loop over all packages, and for each ",(0,i.jsx)(a.code,{children:"ReactPackage"}),", register each native module within."]}),"\n",(0,i.jsxs)(a.p,{children:["React Native invokes the method ",(0,i.jsx)(a.code,{children:"createNativeModules()"})," on a ",(0,i.jsx)(a.code,{children:"ReactPackage"})," in order to get the list of native modules to register. For Android, if a module is not instantiated and returned in createNativeModules it will not be available from JavaScript."]}),"\n",(0,i.jsxs)(a.p,{children:["To add your Native Module to ",(0,i.jsx)(a.code,{children:"ReactPackage"}),", first create a new Java/Kotlin Class named (",(0,i.jsx)(a.code,{children:"MyAppPackage.java"})," or ",(0,i.jsx)(a.code,{children:"MyAppPackage.kt"}),") that implements ",(0,i.jsx)(a.code,{children:"ReactPackage"})," inside the ",(0,i.jsx)(a.code,{children:"android/app/src/main/java/com/your-app-name/"})," folder:"]}),"\n",(0,i.jsx)(a.p,{children:"Then add the following content:"}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"package com.your-app-name; // replace your-app-name with your app\u2019s name\nimport com.facebook.react.ReactPackage;
1\nimport com.facebook.react.bridge.NativeModule;\nimport com.facebook.react.bridge.ReactApplicationContext;\nimport com.facebook.react.uimanager.ViewManager;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class MyAppPackage implements ReactPackage {\n\n   @Override\n   public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {\n       return Collections.emptyList();\n   }\n\n   @Override\n   public List<NativeModule> createNativeModules(\n           ReactApplicationContext reactContext) {\n       List<NativeModule> modules = new ArrayList<>();\n\n       modules.add(new CalendarModule(reactContext));\n\n       return modules;\n   }\n\n}\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"package com.your-app-name // replace your-app-name with your app\u2019s name\n\nimport android.view.View\nimport com.facebook.react.ReactPackage\nimport com.facebook.react.bridge.N
1ativeModule\nimport com.facebook.react.bridge.ReactApplicationContext\nimport com.facebook.react.uimanager.ReactShadowNode\nimport com.facebook.react.uimanager.ViewManager\n\nclass MyAppPackage : ReactPackage {\n\n    override fun createViewManagers(\n        reactContext: ReactApplicationContext\n    ): MutableList<ViewManager<View, ReactShadowNode<*>>> = mutableListOf()\n\n    override fun createNativeModules(\n        reactContext: ReactApplicationContext\n    ): MutableList<NativeModule> = listOf(CalendarModule(reactContext)).toMutableList()\n}\n"})})})]}),"\n",(0,i.jsxs)(a.p,{children:["This file imports the native module you created, ",(0,i.jsx)(a.code,{children:"CalendarModule"}),". It then instantiates ",(0,i.jsx)(a.code,{children:"CalendarModule"})," within the ",(0,i.jsx)(a.code,{children:"createNativeModules()"})," function and returns it as a list of ",(0,i.jsx)(a.code,{children:"NativeModules"})," to register. If you add more native modules down the line, you can also instantiate them and add them to the list returned here."]}),"\n",(0,i.jsxs)(a.blockquote,{children:["\n",(0,i.jsxs)(a.p,{children:["It is worth noting that this way of registering native modules eagerly initializes all native modules when the application starts, which adds to the startup time of an application. You can use ",(0,i.jsx)(a.a,{href:"https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/TurboReactPackage.java",children:"TurboReactPackage"})," as an alternative. Instead of ",(0,i.jsx)(a.code,{children:"createNativeModules"}),", which return a list of instantiated native module objects, TurboReactPackage implements a ",(0,i.jsx)(a.code,{children:"getModule(String name, ReactApplicationContext rac)"})," method that creates the native module object, when required. TurboReactPackage is a bit more complicated to implement at the moment. In addition to implementing a ",(0,i.jsx)(a.code,{children:"getModule()"})," method, you have to implement a ",(0,i.jsx)(a.code,{children:"getReactModuleInfoProvider()"})," method, which returns a list of all the native modules the package can instantiate along with a function that instantiates them, example ",(0,i.jsx)(a.a,{href:"https://github.com/facebook/react-native/blob/8ac467c51b94c82d81930b4802b2978c85539925/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java#L86-L165",children:"here"}),". Again, using TurboReactPackage will allow your application to have a faster startup time, but it is currently a bit cumbersome to write. So proceed with caution if you choose to use TurboReactPackages."]}),"\n"]}),"\n",(0,i.jsxs)(a.p,{children:["To register the ",(0,i.jsx)(a.code,{children:"CalendarModule"})," package, you must add ",(0,i.jsx)(a.code,{children:"MyAppPackage"})," to the list of packages returned in ReactNativeHost's ",(0,i.jsx)(a.code,{children:"getPackages()"})," method. Open up your ",(0,i.jsx)(a.code,{children:"MainApplication.java"})," or ",(0,i.jsx)(a.code,{children:"MainApplication.kt"})," file, which can be found in the following path: ",(0,i.jsx)(a.code,{children:"android/app/src/main/java/com/your-app-name/"}),"."]}),"\n",(0,i.jsxs)(a.p,{children:["Locate ReactNativeHost\u2019s ",(0,i.jsx)(a.code,{children:"getPackages()"})," method and add your package to the packages list ",(0,i.jsx)(a.code,{children:"getPackages()"})," returns:"]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"@Override\nprotected List<ReactPackage> getPackages() {\n    List<ReactPackage> packages = new PackageList(this).getPackages();\n    // Packages that cannot be autolinked yet can be added manually here, for example:\n    // packages.add(new MyReactNativePackage());\n    packages.add(new MyAppPackage());\n    return packages;\n}\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"override fun getPackages(): List<ReactPackage> =\n    PackageList(this).packages.apply {\n        // Packages that cannot be autolinked yet can be added manually here, for example:\n        // add(MyReactNativePackage())\n        add(MyAppPackage())\n    }\n"})})})]}),"\n",(0,i.jsx)(a.p,{children:"You have now successfully registered your native module for Android!"}),"\n",(0,i.jsx)(a.h3,{id:"test-what-you-have-built",children:"Test What You Have Built"}),"\n",(0,i.jsx)(a.p,{children:"At this point, you have set up the basic scaffolding for your native module in Android. Test that out by accessing the native module and invoking its exported method in JavaScript."}),"\n",(0,i.jsxs)(a.p,{children:["Find a place in your application where you would like to add a call to the native module\u2019s ",(0,i.jsx)(a.code,{children:"createCalendarEvent()"})," method. Below is an example of a component, ",(0,i.jsx)(a.code,{children:"NewM
1oduleButton"})," you can add in your app. You can invoke the native module inside ",(0,i.jsx)(a.code,{children:"NewModuleButton"}),"'s ",(0,i.jsx)(a.code,{children:"onPress()"})," function."]}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"import React from 'react';\nimport {NativeModules, Button} from 'react-native';\n\nconst NewModuleButton = () => {\n  const onPress = () => {\n    console.log('We will invoke the native module here!');\n  };\n\n  return (\n    <Button\n      title=\"Click to invoke your native module!\"\n      color=\"#841584\"\n      onPress={onPress}\n    />\n  );\n};\n\nexport default NewModuleButton;\n"})}),"\n",(0,i.jsxs)(a.p,{children:["In order to access your native module from JavaScript you need to first import ",(0,i.jsx)(a.code,{children:"NativeModules"})," from React Native:"]}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"import {NativeModules} from 'react-native';\n"})}),"\n",(0,i.jsxs)(a.p,{children:["You can then access the ",(0,i.jsx)(a.code,{children:"CalendarModule"})," native module off of ",(0,i.jsx)(a.code,{children:"NativeModules"}),"."]}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const {CalendarModule} = NativeModules;\n"})}),"\n",(0,i.jsxs)(a.p,{children:["Now that you have the CalendarModule native module available, you can invoke your native method ",(0,i.jsx)(a.code,{children:"createCalendarEvent()"}),". Below it is added to the ",(0,i.jsx)(a.code,{children:"onPress()"})," method in ",(0,i.jsx)(a.code,{children:"NewModuleButton"}),":"]}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const onPress = () => {\n  CalendarModule.createCalendarEvent('testName', 'testLocation');\n};\n"})}),"\n",(0,i.jsx)(a.p,{children:"The final step is to rebuild the React Native app so that you can have the latest native code (with your new native module!) available. In your command line, where the react native application is located, run the following:"}),"\n",(0,i.jsxs)(l.A,{groupId:"package-manager",queryString:!0,defaultValue:c.A.defaultPackageManager,values:c.A.packageManagers,children:[(0,i.jsx)(s.A,{value:"npm",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-shell",children:"npm run android\n"})})}),(0,i.jsx)(s.A,{value:"yarn",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-shell",children:"yarn android\n"})})})]}),"\n",(0,i.jsx)(a.h3,{id:"building-as-you-iterate",children:"Building as You Iterate"}),"\n",(0,i.jsx)(a.p,{children:"As you work through these guides and iterate on your native module, you will need to do a native rebuild of your application to access your most recent changes from JavaScript. This is because the code that you are writing sits within the native part of your application. While React Native\u2019s metro bundler can watch for changes in JavaScript and rebuild on the fly for you, it will not do so for native code. So if you want to test your latest native changes you need to rebuild by using the above command."}),"\n",(0,i.jsx)(a.h3,{id:"recap",children:"Recap\u2728"}),"\n",(0,i.jsxs)(a.p,{children:["You should now be able to invoke your ",(0,i.jsx)(a.code,{children:"createCalendarEvent()"})," method on your native module in the app. In our example this occurs by pressing the ",(0,i.jsx)(a.code,{children:"NewModuleButton"}),". You can confirm this by viewing the log you set up in your ",(0,i.jsx)(a.code,{children:"createCalendarEvent()"})," method. You can follow ",(0,i.jsx)(a.a,{href:"https://developer.android.com/studio/debug/am-logcat.html",children:"these steps"})," to view ADB logs in your app. You should then be able to search for your ",(0,i.jsx)(a.code,{children:"Log.d"})," message (in our example \u201CCreate event called with name: testName and location: testLocation\u201D) and see your message logged each time you invoke your native module method."]}),"\n",(0,i.jsxs)("figure",{children:[(0,i.jsx)("img",{src:"/docs/assets/native-modules-android-logs.png",width:"1000",alt:"Image of logs."}),(0,i.jsx)("figcaption",{children:"Image of ADB logs in Android Studio"})]}),"\n",(0,i.jsx)(a.p,{children:"At this point you have created an Android native module and invoked its native method from JavaScript in your React Native application. You can read on to learn more about things like argument types available to a native module method and how to setup callbacks and promises."}),"\n",(0,i.jsx)(a.h2,{id:"beyond-a-calendar-native-module",children:"Beyond a Calendar Native Module"}),"\n",(0,i.jsx)(a.h3,{id:"better-native-module-export",children:"Better Native Module Export"}),"\n",(0,i.jsxs)(a.p,{children:["Importing your native module by pulling it off of ",(0,i.jsx)(a.code,{children:"NativeModules"})," like above is a bit clunky."]}),"\n",(0,i.jsxs)(a.p,{children:["To save consumers of your native module from needing to do that each time they want to access your native module, you can create a JavaScript wrapper for the module. Create a new JavaScript file named ",(0,i.jsx)(a.code,{children:"CalendarModule.js"})," with the following content:"]}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"/**\n* This exposes the native CalendarModule module as a JS module. This has a\n* function 'createCalendarEvent' which takes the following parameters:\n\n* 1. String name: A string representing the name of the event\n* 2. String location: A string representing the location of the event\n*/\nimport {NativeModules} from 'react-native';\nconst {CalendarModule} = NativeModules;\nexport default CalendarModule;\n"})}),"\n",(0,i.jsx)(a.p,{children:"This JavaScript file also becomes a good location for you to add any JavaScript side functionality. For example, if you use a type system like TypeScript you can add type annotations for your native module here. While React Native does not yet support Native to JS type safety, all your JS code will be type safe. Doing so will also make it easier for you to switch to type-safe native modules down the line. Below is an example of adding type safety to the CalendarModule:"}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"/**\n * This exposes the native CalendarModule module as a JS module. This has a\n * function 'createCalendarEvent' which takes the following parameters:\n *\n * 1. String name: A string representing the name of the event\n * 2. Str
1ing location: A string representing the location of the event\n */\nimport {NativeModules} from 'react-native';\nconst {CalendarModule} = NativeModules;\ninterface CalendarInterface {\n  createCalendarEvent(name: string, location: string): void;\n}\nexport default CalendarModule as CalendarInterface;\n"})}),"\n",(0,i.jsx)(a.p,{children:"In your other JavaScript files you can access the native module and invoke its method like this:"}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"import CalendarModule from './CalendarModule';\nCalendarModule.createCalendarEvent('foo', 'bar');\n"})}),"\n",(0,i.jsxs)(a.blockquote,{children:["\n",(0,i.jsxs)(a.p,{children:["This assumes that the place you are importing ",(0,i.jsx)(a.code,{children:"CalendarModule"})," is in the same hierarchy as ",(0,i.jsx)(a.code,{children:"CalendarModule.js"}),". Please update the relative import as necessary."]}),"\n"]}),"\n",(0,i.jsx)(a.h3,{id:"argument-types",children:"Argument Types"}),"\n",(0,i.jsx)(a.p,{children:"When a native module method is invoked in JavaScript, React Native converts the arguments from JS objects to their Java/Kotlin object analogues. So for example, if your Java Native Module method accepts a double, in JS you need to call the method with a number. React Native will handle the conversion for you. Below is a list of the argument types supported for native module methods and the JavaScript equivalents they map to."}),"\n",(0,i.jsxs)(a.table,{children:[(0,i.jsx)(a.thead,{children:(0,i.jsxs)(a.tr,{children:[(0,i.jsx)(a.th,{children:"Java"}),(0,i.jsx)(a.th,{children:"Kotlin"}),(0,i.jsx)(a.th,{children:"JavaScript"})]})}),(0,i.jsxs)(a.tbody,{children:[(0,i.jsxs)(a.tr,{children:[(0,i.jsx)(a.td,{children:"Boolean"}),(0,i.jsx)(a.td,{children:"Boolean"}),(0,i.jsx)(a.td,{children:"?boolean"})]}),(0,i.jsxs)(a.tr,{children:[(0,i.jsx)(a.td,{children:"boolean"}),(0,i.jsx)(a.td,{}),(0,i.jsx)(a.td,{children:"boolean"})]}),(0,i.jsxs)(a.tr,{children:[(0,i.jsx)(a.td,{children:"Double"}),(0,i.jsx)(a.td,{children:"Double"}),(0,i.jsx)(a.td,{children:"?number"})]}),(0,i.jsxs)(a.tr,{children:[(0,i.jsx)(a.td,{children:"double"}),(0,i.jsx)(a.td,{}),(0,i.jsx)(a.td,{children:"number"})]}),(0,i.jsxs)(a.tr,{children:[(0,i.jsx)(a.td,{children:"String"}),(0,i.jsx)(a.td,{children:"String"}),(0,i.jsx)(a.td,{children:"string"})]}),(0,i.jsxs)(a.tr,{children:[(0,i.jsx)(a.td,{children:"Callback"}),(0,i.jsx)(a.td,{children:"Callback"}),(0,i.jsx)(a.td,{children:"Function"})]}),(0,i.jsxs)(a.tr,{children:[(0,i.jsx)(a.td,{children:"Promise"}),(0,i.jsx)(a.td,{children:"Promise"}),(0,i.jsx)(a.td,{children:"Promise"})]}),(0,i.jsxs)(a.tr,{children:[(0,i.jsx)(a.td,{children:"ReadableMap"}),(0,i.jsx)(a.td,{children:"ReadableMap"}),(0,i.jsx)(a.td,{children:"Object"})]}),(0,i.jsxs)(a.tr,{children:[(0,i.jsx)(a.td,{children:"ReadableArray"}),(0,i.jsx)(a.td,{children:"ReadableArray"}),(0,i.jsx)(a.td,{children:"Array"})]})]})]}),"\n",(0,i.jsxs)(a.blockquote,{children:["\n",(0,i.jsx)(a.p,{children:"The following types are currently supported but will not be supported in TurboModules. Please avoid using them:"}),"\n",(0,i.jsxs)(a.ul,{children:["\n",(0,i.jsx)(a.li,{children:"Integer Java/Kotlin -> ?number"}),"\n",(0,i.jsx)(a.li,{children:"Float Java/Kotlin -> ?number"}),"\n",(0,i.jsx)(a.li,{children:"int Java -> number"}),"\n",(0,i.jsx)(a.li,{children:"float Java -> number"}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(a.p,{children:["For argument types not listed above, you will need to handle the conversion yourself. For example, in Android, ",(0,i.jsx)(a.code,{children:"Date"})," conversion is not supported out of the box. You can handle the conversion to the ",(0,i.jsx)(a.code,{children:"Date"})," type within the native method yourself like so:"]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:'    String dateFormat = "yyyy-MM-dd";\n    SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);\n    Calendar eStartDate = Calendar.getInstance();\n    try {\n        eStartDate.setTime(sdf.parse(startDate));\n    }\n\n'})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:'    val dateFormat = "yyyy-MM-dd"\n    val sdf = SimpleDateFormat(dateFormat, Locale.US)\n    val eStartDate = Calendar.getInstance()\n    try {\n        sdf.parse(startDate)?.let {\n            eStartDate.time = it\n        }\n    }\n'})})})]}),"\n",(0,i.jsx)(a.h3,{id:"exporting-constants",children:"Exporting Constants"}),"\n",(0,i.jsxs)(a.p,{children:["A native module can export constants by implementing the native method ",(0,i.jsx)(a.code,{children:"getConstants()"}),", which is available in JS. Below you will implement ",(0,i.jsx)(a.code,{children:"getConstants()"})," and return a Map that contains a ",(0,i.jsx)(a.code,{children:"DEFAULT_EVENT_NAME"})," constant you can access in JavaScript:"]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:'@Override\npublic Map<String, Object> getConstants() {\n   final Map<String, Object> constants = new HashMap<>();\n   constants.put("DEFAULT_EVENT_NAME", "New Event");\n   return constants;\n}\n'})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:'override fun getConstants(): MutableMap<String, Any> =\n    hashMapOf("DEFAULT_EVENT_NAME" to "New Event")\n'})})})]}),"\n",(0,i.jsxs)(a.p,{children:["The constant can then be accessed by invoking ",(0,i.jsx)(a.code,{children:"getConstants"})," on the native module in JS:"]}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const {DEFAULT_EVENT_NAME} = CalendarModule.getConstants();\nconsole.log(DEFAULT_EVENT_NAME);\n"})}),"\n",(0,i.jsxs)(a.p,{children:["Technically it is possible to access constants exported in ",(0,i.jsx)(a.code,{children:"getConstants()"})," directly off the native module object. This will no longer be supported with TurboModules, so we encourage the community to switch to the above approach to avoid necessary migration down the line."]}),"\n",(0,i.jsxs)(a.blockquote,{children:["\n",(0,i.jsxs)(a.p,{children:["That currently constants are exported only at initialization time, so if you change getConstants values at runtime it won't affect the JavaScript environment. This will change with Turbomodules. With Turbomodules, ",(0,i.jsx)(a.code,{children:"getConstants()"})," will become a regular native module method, and each invocation will hit the native side."]}),"\n"]}),"\n",(0,i.jsx)(a.h3,{id:"callbacks",children:"Callbacks"}),"\n",(0,i.jsx)(a.p,{children:"Native modules also support a unique kind of argument: a callback. Callbacks are used to pass data from Java/Kotlin to JavaScript for asynchronous methods. They can also be used to asynchronously execute JavaScript from the native side."}),"\n",(0,i.jsxs)(a.p,{children:["In order to create a native module method with a callback, first import the ",(0,i.jsx)(a.code,{children:"Callback"})," interface, and then add a new parameter to your native module method of type ",(0,i.jsx)(a.code,{children:"Callback"}),". There are a couple of nuances with callback arguments that will soon be lifted with TurboModules. First off, you can only have tw
1o callbacks in your function arguments- a successCallback and a failureCallback. In addition, the last argument to a native module method call, if it's a function, is treated as the successCallback, and the second to last argument to a native module method call, if it's a function, is treated as the failure callback."]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"import com.facebook.react.bridge.Callback;\n\n@ReactMethod\npublic void createCalendarEvent(String name, String location, Callback callBack) {\n}\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"import com.facebook.react.bridge.Callback\n\n@ReactMethod fun createCalendarEvent(name: String, location: String, callback: Callback) {}\n"})})})]}),"\n",(0,i.jsxs)(a.p,{children:["You can invoke the callback in your Java/Kotlin method, providing whatever data you want to pass to JavaScript. Please note that you can only pass serializable data from native code to JavaScript. If you need to pass back a native object you can use ",(0,i.jsx)(a.code,{children:"WriteableMaps"}),", if you need to use a collection use ",(0,i.jsx)(a.code,{children:"WritableArrays"}),". It is also important to highlight that the callback is not invoked immediately after the native function completes. Below the ID of an event created in an earlier call is passed to the callback."]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"  @ReactMethod\n   public void createCalendarEvent(String name, String location, Callback callBack) {\n       Integer eventId = ...\n       callBack.invoke(eventId);\n   }\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"  @ReactMethod\n  fun createCalendarEvent(name: String, location: String, callback: Callback) {\n      val eventId = ...\n      callback.invoke(eventId)\n  }\n"})})})]}),"\n",(0,i.jsx)(a.p,{children:"This method could then be accessed in JavaScript using:"}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const onPress = () => {\n  CalendarModule.createCalendarEvent(\n    'Party',\n    'My House',\n    eventId => {\n      console.log(`Created a new event with id ${eventId}`);\n    },\n  );\n};\n"})}),"\n",(0,i.jsx)(a.p,{children:"Another important detail to note is that a native module method can only invoke one callback, one time. This means that you can either call a success callback or a failure callback, but not both, and each callback can only be invoked at most one time. A native module can, however, store the callback and invoke it later."}),"\n",(0,i.jsx)(a.p,{children:"There are two approaches to error handling with callbacks. The first is to follow Node\u2019s convention and treat the first argument passed to the callback as an error object."}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"  @ReactMethod\n   public void createCalendarEvent(String name, String location, Callback callBack) {\n       Integer eventId = ...\n       callBack.invoke(null, eventId);\n   }\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"  @ReactMethod\n  fun createCalendarEvent(name: String, location: String, callback: Callback) {\n      val eventId = ...\n      callback.invoke(null, eventId)\n  }\n"})})})]}),"\n",(0,i.jsx)(a.p,{children:"In JavaScript, you can then check the first argument to see if an error was passed through:"}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const onPress = () => {\n  CalendarModule.createCalendarEvent(\n    'testName',\n    'testLocation',\n    (error, eventId) => {\n      if (error) {\n        console.error(`Error found! ${error}`);\n      }\n      console.log(`event id ${eventId} returned`);\n    },\n  );\n};\n"})}),"\n",(0,i.jsx)(a.p,{children:"Another option is to use an onSuccess and onFailure callback:"}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"@ReactMethod\npublic void createCalendarEvent(String name, String location, Callback myFailureCallback, Callback mySuccessCallback) {\n}\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"@ReactMethod\n  fun createCalendarEvent(\n      name: String,\n      location: String,\n      myFailureCallback: Callback,\n      mySuccessCallback: Callback\n  ) {}\n"})})})]}
1),"\n",(0,i.jsx)(a.p,{children:"Then in JavaScript you can add a separate callback for error and success responses:"}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const onPress = () => {\n  CalendarModule.createCalendarEvent(\n    'testName',\n    'testLocation',\n    error => {\n      console.error(`Error found! ${error}`);\n    },\n    eventId => {\n      console.log(`event id ${eventId} returned`);\n    },\n  );\n};\n"})}),"\n",(0,i.jsx)(a.h3,{id:"promises",children:"Promises"}),"\n",(0,i.jsxs)(a.p,{children:["Native modules can also fulfill a ",(0,i.jsx)(a.a,{href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise",children:"Promise"}),", which can simplify your JavaScript, especially when using ES2016's ",(0,i.jsx)(a.a,{href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function",children:"async/await"})," syntax. When the last parameter of a native module Java/Kotlin method is a Promise, its corresponding JS method will return a JS Promise object."]}),"\n",(0,i.jsx)(a.p,{children:"Refactoring the above code to use a promise instead of callbacks looks like this:"}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:'import com.facebook.react.bridge.Promise;\n\n@ReactMethod\npublic void createCalendarEvent(String name, String location, Promise promise) {\n    try {\n        Integer eventId = ...\n        promise.resolve(eventId);\n    } catch(Exception e) {\n        promise.reject("Create Event Error", e);\n    }\n}\n'})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:'import com.facebook.react.bridge.Promise\n\n@ReactMethod\nfun createCalendarEvent(name: String, location: String, promise: Promise) {\n    try {\n        val eventId = ...\n        promise.resolve(eventId)\n    } catch (e: Throwable) {\n        promise.reject("Create Event Error", e)\n    }\n}\n'})})})]}),"\n",(0,i.jsxs)(a.blockquote,{children:["\n",(0,i.jsx)(a.p,{children:"Similar to callbacks, a native module method can either reject or resolve a promise (but not both) and can do so at most once. This means that you can either call a success callback or a failure callback, but not both, and each callback can only be invoked at most one time. A native module can, however, store the callback and invoke it later."}),"\n"]}),"\n",(0,i.jsxs)(a.p,{children:["The JavaScript counterpart of this method returns a Promise. This means you can use the ",(0,i.jsx)(a.code,{children:"await"})," keyword within an async function to call it and wait for its result:"]}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const onSubmit = async () => {\n  try {\n    const eventId = await CalendarModule.createCalendarEvent(\n      'Party',\n      'My House',\n    );\n    console.log(`Created a new event with id ${eventId}`);\n  } catch (e) {\n    console.error(e);\n  }\n};\n"})}),"\n",(0,i.jsx)(a.p,{children:"The reject method takes different combinations of the following arguments:"}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"String code, String message, WritableMap userInfo, Throwable throwable\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"code: String, message: String, userInfo: WritableMap, throwable: Throwable\n"})})})]}),"\n",(0,i.jsxs)(a.p,{children:["For more detail, you can find the ",(0,i.jsx)(a.code,{children:"Promise.java"})," interface ",(0,i.jsx)(a.a,{href:"https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/Promise.java",children:"here"}),". If ",(0,i.jsx)(a.code,{children:"userInfo"})," is not provided, ReactNative will set it to null. For the rest of the parameters React Native will use a default value. The ",(0,i.jsx)(a.code,{children:"message"})," argument provides the error ",(0,i.jsx)(a.code,{children:"message"})," shown at the top of an error call stack. Below is an example of the error message shown in JavaScript from the following reject call in Java/Kotlin."]}),"\n",(0,i.jsx)(a.p,{children:"Java/Kotlin reject call:"}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:'promise.reject("Create Event error", "Error parsing date", e);\n'})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:'promise.reject("Create Event error", "Error parsing date", e)\n'})})})]}),"\n",(0,i.jsx)(a.p,{children:"Error message in React Native App when promise is rejected:"}),"\n",(0,i.jsxs)("figure",{children:[(0,i.jsx)("img",{src:"/docs/assets/native-modules-android-errorscreen.png",width:"200",alt:"Image of error message in React Native app."}),(0,i.jsx)("figcaption",{children:"Image of error message"})]}),"\n",(0,i.jsx)(a.h3,{id:"sending-events-to-javascript",children:"Sending Events to JavaScript"}),"\n",(0,i.jsxs)(a.p,{children:["Native modules can signal events to JavaScript without being invoked directly. For example, you might want to signal to JavaScript a reminder that a calendar event from the native Android calendar app will occur soon. The easiest way to do this is to use the ",(0,i.jsx)(a.code,{children:"RCTDeviceEventEmitter"})," which can be obtained from the ",(0,i.jsx)(a.code,{children:"ReactContext"})," as in the code snippet below."]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:'...\nimport com.facebook.react.modules.core.DeviceEventManagerModule;\nimport com.facebook.react.bridge.WritableMap;\nimport com.facebook.react.bridge.Arguments;\n...\nprivate void sendEvent(ReactContext reactContext,\n                      String eventName,\n                      @Nullable WritableMap params) {\n reactContext\n     .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)\n     .emit(eventName, params);\n}\n\nprivate int listenerCount = 0;\n\n@ReactMethod\npublic void addListener(String eventName) {\n  if (listenerCount == 0) {\n    // Set up any upstream listeners or background tasks as 
1necessary\n  }\n\n  listenerCount += 1;\n}\n\n@ReactMethod\npublic void removeListeners(Integer count) {\n  listenerCount -= count;\n  if (listenerCount == 0) {\n    // Remove upstream listeners, stop unnecessary background tasks\n  }\n}\n...\nWritableMap params = Arguments.createMap();\nparams.putString("eventProperty", "someValue");\n...\nsendEvent(reactContext, "EventReminder", params);\n'})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:'...\nimport com.facebook.react.bridge.WritableMap\nimport com.facebook.react.bridge.Arguments\nimport com.facebook.react.modules.core.DeviceEventManagerModule\n...\n\nprivate fun sendEvent(reactContext: ReactContext, eventName: String, params: WritableMap?) {\n    reactContext\n      .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)\n      .emit(eventName, params)\n}\n\nprivate var listenerCount = 0\n\n@ReactMethod\nfun addListener(eventName: String) {\n  if (listenerCount == 0) {\n    // Set up any upstream listeners or background tasks as necessary\n  }\n\n  listenerCount += 1\n}\n\n@ReactMethod\nfun removeListeners(count: Int) {\n  listenerCount -= count\n  if (listenerCount == 0) {\n    // Remove upstream listeners, stop unnecessary background tasks\n  }\n}\n...\nval params = Arguments.createMap().apply {\n    putString("eventProperty", "someValue")\n}\n...\nsendEvent(reactContext, "EventReminder", params)\n'})})})]}),"\n",(0,i.jsxs)(a.p,{children:["JavaScript modules can then register to receive events by ",(0,i.jsx)(a.code,{children:"addListener"})," on the ",(0,i.jsx)(a.a,{href:"https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/EventEmitter/NativeEventEmitter.js",children:"NativeEventEmitter"})," class."]}),"\n",(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-tsx",metastring:'title="React TSX"',children:"import {NativeEventEmitter, NativeModules} from 'react-native';\n...\nuseEffect(() => {\n    const eventEmitter = new NativeEventEmitter(NativeModules.ToastExample);\n    let eventListener = eventEmitter.addListener('EventReminder', event => {\n      console.log(event.eventProperty) // \"someValue\"\n    });\n\n    // Removes the listener once unmounted\n    return () => {\n      eventListener.remove();\n    };\n  }, []);\n"})}),"\n",(0,i.jsx)(a.h3,{id:"getting-activity-result-from-startactivityforresult",children:"Getting Activity Result from startActivityForResult"}),"\n",(0,i.jsxs)(a.p,{children:["You'll need to listen to ",(0,i.jsx)(a.code,{children:"onActivityResult"})," if you want to get results from an activity you started with ",(0,i.jsx)(a.code,{children:"startActivityForResult"}),". To do this, you must extend ",(0,i.jsx)(a.code,{children:"BaseActivityEventListener"})," or implement ",(0,i.jsx)(a.code,{children:"ActivityEventListener"}),". The former is preferred as it is more resilient to API changes. Then, you need to register the listener in the module's constructor like so:"]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"reactContext.addActivityEventListener(mActivityResultListener);\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"reactContext.addActivityEventListener(mActivityResultListener);\n"})})})]}),"\n",(0,i.jsxs)(a.p,{children:["Now you can listen to ",(0,i.jsx)(a.code,{children:"onActivityResult"})," by implementing the following method:"]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"@Override\npublic void onActivityResult(\n final Activity activity,\n final int requestCode,\n final int resultCode,\n final Intent intent) {\n // Your logic here\n}\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"override fun onActivityResult(\n    activity: Activity?,\n    requestCode: Int,\n    resultCode: Int,\n    intent: Intent?\n) {\n    // Your logic here\n}\n"})})})]}),"\n",(0,i.jsxs)(a.p,{children:["Let's implement a basic image picker to demonstrate this. The image picker will expose the method ",(0,i.jsx)(a.code,{children:"pickImage"})," to JavaScript, which will return the path of the image when called."]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:'public class ImagePickerModule extends ReactContextBaseJavaModule {\n\n  private static final int IMAGE_PICKER_REQUEST = 1;
1\n  private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";\n  private static final String E_PICKER_CANCELLED = "E_PICKER_CANCELLED";\n  private static final String E_FAILED_TO_SHOW_PICKER = "E_FAILED_TO_SHOW_PICKER";\n  private static final String E_NO_IMAGE_DATA_FOUND = "E_NO_IMAGE_DATA_FOUND";\n\n  private Promise mPickerPromise;\n\n  private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {\n\n    @Override\n    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {\n      if (requestCode == IMAGE_PICKER_REQUEST) {\n        if (mPickerPromise != null) {\n          if (resultCode == Activity.RESULT_CANCELED) {\n            mPickerPromise.reject(E_PICKER_CANCELLED, "Image picker was cancelled");\n          } else if (resultCode == Activity.RESULT_OK) {\n            Uri uri = intent.getData();\n\n            if (uri == null) {\n              mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, "No image data found");\n            } else {\n              mPickerPromise.resolve(uri.toString());\n            }\n          }\n\n          mPickerPromise = null;\n        }\n      }\n    }\n  };\n\n  ImagePickerModule(ReactApplicationContext reactContext) {\n    super(reactContext);\n\n    // Add the listener for `onActivityResult`\n    reactContext.addActivityEventListener(mActivityEventListener);\n  }\n\n  @Override\n  public String getName() {\n    return "ImagePickerModule";\n  }\n\n  @ReactMethod\n  public void pickImage(final Promise promise) {\n    Activity currentActivity = getCurrentActivity();\n\n    if (currentActivity == null) {\n      promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn\'t exist");\n      return;\n    }\n\n    // Store the promise to resolve/reject when picker returns data\n    mPickerPromise = promise;\n\n    try {\n      final Intent galleryIntent = new Intent(Intent.ACTION_PICK);\n\n      galleryIntent.setType("image/*");\n\n      final Intent chooserIntent = Intent.createChooser(galleryIntent, "Pick an image");\n\n      currentActivity.startActivityForResult(chooserIntent, IMAGE_PICKER_REQUEST);\n    } catch (Exception e) {\n      mPickerPromise.reject(E_FAILED_TO_SHOW_PICKER, e);\n      mPickerPromise = null;\n    }\n  }\n}\n'})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:'class ImagePickerModule(reactContext: ReactApplicationContext) :\n    ReactContextBaseJavaModule(reactContext) {\n\n    private var pickerPromise: Promise? = null\n\n    private val activityEventListener =\n        object : BaseActivityEventListener() {\n            override fun onActivityResult(\n                activity: Activity?,\n                requestCode: Int,\n                resultCode: Int,\n                intent: Intent?\n            ) {\n                if (requestCode == IMAGE_PICKER_REQUEST) {\n                    pickerPromise?.let { promise ->\n                        when (resultCode) {\n                            Activity.RESULT_CANCELED ->\n                                promise.reject(E_PICKER_CANCELLED, "Image picker was cancelled")\n                            Activity.RESULT_OK -> {\n                                val uri = intent?.data\n\n                                uri?.let { promise.resolve(uri.toString())}\n                                    ?: promise.reject(E_NO_IMAGE_DATA_FOUND, "No image data found")\n                            }\n                        }\n\n                        pickerPromise = null\n                    }\n                }\n            }\n        }\n\n    init {\n        reactContext.addActivityEventListener(activityEventListener)\n    }\n\n    override fun getName() = "ImagePickerModule"\n\n    @ReactMethod\n    fun pickImage(promise: Promise) {\n        val activity = currentActivity\n\n        if (activity == null) {\n            promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn\'t exist")\n            return\n        }\n\n        pickerPromise = promise\n\n        try {\n            val galleryIntent = Intent(Intent.ACTION_PICK).apply { type = "image\\/*" }\n\n            val chooserIntent = Intent.createChooser(galleryIntent, "Pick an image")\n\n            activity.startActivityForResult(chooserIntent, IMAGE_PICKER_REQUEST)\n        } catch (t: Throwable) {\n            pickerPromise?.reject(E_FAILED_TO_SHOW_PICKER, t)\n            pickerPromise = null\n        }\n    }\n\n    companion object {\n        const val IMAGE_PICKER_REQUEST = 1\n        const val E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST"\n        const val E_PICKER_CANCELLED = "E_PICKER_CANCELLED"\n        const val E_FAILED_TO_SHOW_PICKER = "E_FAILED_TO_SHOW_PICKER"\n        const val E_NO_IMAGE_DATA_FOUND = "E_NO_IMAGE_DATA_FOUND"\n    }\n}\n'})})})]}),"\n",(0,i.jsx)(a.h3,{id:"listening-to-lifecycle-events",children:"Listening to Lifecycle Events"}),"\n",(0,i.jsxs)(a.p,{children:["Listening to the activity's LifeCycle events such as ",(0,i.jsx)(a.code,{children:"onResume"}),", ",(0,i.jsx)(a.code,{children:"onPause"})," etc. is very similar to how ",(0,i.jsx)(a.code,{children:"ActivityEventListener"})," was implemented. The module must implement ",(0,i.jsx)(a.code,{children:"LifecycleEventListener"}),". Then, you need to register a listener in the module's constructor like so:"]}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"reactContext.addLifecycleEventListener(this);\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"reactContext.addLifecycleEventListener(this)\n"})})})]}),"\n",(0,i.jsx)(a.p,{children:"Now you can listen to the activity's LifeCycle events by implementing the following methods:"}),"\n",(0,i.jsxs)(l.A,{groupId:"android-language",queryString:!0,defaultValue:c.A.defaultAndroidLanguage,values:c.A.androidLanguages,children:[(0,i.jsx)(s.A,{value:"java",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-java",metastring:'title="Java"',children:"@Override\npublic void onHostResume() {\n   // Activity `onResume`\n}\n@Override\npublic void onHostPause() {\n   // Activity `onPause`\n}\n@Override\npublic void onHostDestroy() {\n   // Activity `onDestroy`\n}\n"})})}),(0,i.jsx)(s.A,{value:"kotlin",children:(0,i.jsx)(a.pre,{children:(0,i.jsx)(a.code,{className:"language-kotlin",metastring:'title="Kotlin"',children:"override fun onHostResume() {\n    // Activity `onResume`\n}\n\noverride fun onHostPause() {\n    // Activity `onPause`\n}\n\noverride fun onHostDestroy() {\n    // Activity `onDestroy`\n}\n"})})})]}),"\n",(0,i.jsx)(a.h3,{id:"threading",children:"Threading"}),"\n",(0,i.jsx)(a.p,{children:"To date, on Android, all native module async methods execute on one thread. Native modules should not have any assumptions about what thread they are being called on, as the current assignment is subject to change in the future. If a blocking call is required, the heavy w
1ork should be dispatched to an internally managed worker thread, and any callbacks distributed from there."})]})}function g(e={}){let{wrapper:a}={...(0,o.R)(),...e.components};return a?(0,i.jsx)(a,{...e,children:(0,i.jsx)(m,{...e})}):m(e)}},33264(e,a,n){n.d(a,{Ay:()=>l,RM:()=>o});var t=n(62540),i=n(43023);let o=[];function r(e){let a={a:"a",admonition:"admonition",p:"p",...(0,i.R)(),...e.components};return(0,t.jsx)(a.admonition,{type:"info",children:(0,t.jsxs)(a.p,{children:["Native Module and Native Components are our stable technologies used by the legacy architecture.\nThey will be deprecated in the future when the New Architecture will be stable. The New Architecture uses ",(0,t.jsx)(a.a,{href:"https://github.com/reactwg/react-native-new-architecture/blob/main/docs/turbo-modules.md",children:"Turbo Native Module"})," and ",(0,t.jsx)(a.a,{href:"https://github.com/reactwg/react-native-new-architecture/blob/main/docs/fabric-native-components.md",children:"Fabric Native Components"})," to achieve similar results."]})})}function l(e={}){let{wrapper:a}={...(0,i.R)(),...e.components};return a?(0,t.jsx)(a,{...e,children:(0,t.jsx)(r,{...e})}):r(e)}},98263(e,a,n){n.d(a,{A:()=>l});var t=n(62540);n(63696);var i=n(11750),o=n(23865);function r({children:e,className:a,hidden:n}){return(0,t.jsx)("div",{role:"tabpanel",className:(0,i.A)("tabItem_wHwb",a),hidden:n,children:e})}function l({children:e,className:a,value:n}){let{selectedValue:i,lazy:s}=(0,o.uc)(),c=n===i;return!c&&s?null:(0,t.jsx)(r,{className:a,hidden:!c,children:e})}},77779(e,a,n){n.d(a,{A:()=>h});var t=n(62540);n(63696);var i=n(11750),o=n(46065),r=n(23865),l=n(45194),s=n(83029);function c({className:e}){let{selectedValue:a,selectValue:n,tabValues:o,block:s}=(0,r.uc)(),d=[],{blockElementScrollPositionUntilNextRender:u}=(0,l.a_)(),h=e=>{let t=e.currentTarget,i=o[d.indexOf(t)].value;i!==a&&(u(t),n(i))},v=e=>{let a=null;switch(e.key){case"Enter":h(e);break;case"ArrowRight":{let n=d.indexOf(e.currentTarget)+1;a=d[n]??d[0];break}case"ArrowLeft":{let n=d.indexOf(e.currentTarget)-1;a=d[n]??d[d.length-1]}}a?.focus()};return(0,t.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,i.A)("tabs",{"tabs--block":s},e),children:o.map(({value:e,label:n,attributes:o})=>(0,t.jsx)("li",{role:"tab",tabIndex:a===e?0:-1,"aria-selected":a===e,ref:e=>{d.push(e)},onKeyDown:v,onClick:h,...o,className:(0,i.A)("tabs__item","tabItem_l0OV",o?.className,{"tabs__item--active":a===e}),children:n??e},e))})}function d({children:e}){return(0,t.jsx)("div",{className:"margin-top--md",children:e})}function u({className:e,children:a}){return(0,t.jsxs)("div",{className:(0,i.A)(o.G.tabs.container,"tabs-container","tabList_J5MA"),children:[(0,t.jsx)(c,{className:e}),(0,t.jsx)(d,{children:a})]})}function h(e){let a=(0,s.A)(),n=(0,r.OC)(e);return(0,t.jsx)(r.O_,{value:n,children:(0,t.jsx)(u,{className:e.className,children:(0,r.vT)(e.children)})},String(a))}},23865(e,a,n){n.d(a,{OC:()=>h,O_:()=>g,uc:()=>m,vT:()=>d});var t=n(62540),i=n(63696),o=n(49519),r=n(6743),l=n(88511),s=n(15396),c=n(89030);function d(e){return i.Children.toArray(e).filter(e=>"\n"!==e)}function u({value:e,tabValues:a}){return a.some(a=>a.value===e)}function h(e){let a,{defaultValue:n,queryString:t=!1,groupId:d}=e,h=function(e){let{values:a,children:n}=e;return(0,i.useMemo)(()=>{let e=a??i.Children.toArray(n).flatMap(e=>{if(!e)return[];if((0,i.isValidElement)(e)&&function(e){let{props:a}=e;return!!a&&"object"==typeof a&&"value"in a}(e))return[e];let a="string"==typeof e.type?e.type:e.type.name;throw Error(`Docusaurus error: Bad <Tabs> child <${a}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.
2If you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)}).map(({props:{value:e,label:a,attributes:n,default:t}})=>({value:e,label:a,attributes:n,default:t})),t=(0,s.XI)(e,(e,a)=>e.value===a.value);if(t.length>0)throw Error(`Docusaurus error: Duplicate values "${t.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`);return e},[a,n])}(e),[v,m]=(0,i.useState)(()=>(function({defaultValue:e,tabValues:a}){if(0===a.length)throw Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!u({value:e,tabValues:a}))throw Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${a.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}let n=a.find(e=>e.default)??a[0];if(!n)throw Error("Unexpected error: 0 tabValues");return n.value})({defaultValue:n,tabValues:h})),[g,p]=function({queryString:e=!1,groupId:a}){let n=(0,o.W6)(),t=function({queryString:e=!1,groupId:a}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!a)throw Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return a??null}({queryString:e,groupId:a});return[(0,l.aZ)(t),(0,i.useCallback)(e=>{if(!t)return;let a=new URLSearchParams(n.location.search);a.set(t,e),n.replace({...n.location,search:a.toString()})},[t,n])]}({queryString:t,groupId:d}),[x,j]=function({groupId:e}){let a=e?`docusaurus.tab.${e}`:null,[n,t]=(0,c.Dv)(a);return[n,(0,i.useCallback)(e=>{a&&t.set(e)},[a,t])]}({groupId:d}),f=u({value:a=g??x,tabValues:h})?a:null;return(0,r.A)(()=>{f&&m(f)},[f]),{selectedValue:v,selectValue:(0,i.useCallback)(e=>{if(!u({value:e,tabValues:h}))throw Error(`Can't select invalid tab value=${e}`);m(e),p(e),j(e)},[p,j,h]),tabValues:h,lazy:e.lazy??!1,block:e.block??!1}}let v=(0,i.createContext)(null);function m(){let e=i.useContext(v);if(!e)throw Error("useTabsContext() must be used within a Tabs component");return e}function g(e){return(0,t.jsx)(v.Provider,{value:e.value,children:e.children})}},38469(e,a,n){n.d(a,{A:()=>r});var t=n(93227);let i=!!t.A.canUseDOM&&navigator.platform.startsWith("Mac"),o=!!t.A.canUseDOM&&navigator.platform.startsWith("Win"),r={defaultGuide:"quickstart",defaultJsDebugger:"flipper",defaultOs:i?"macos":o?"windows":"linux",defaultPackageManager:"npm",defaultPlatform:i?"ios":"android",defaultSyntax:"functional",defaultAndroidLanguage:"java",defaultAppleLanguage:"objc",defaultJavaScriptSpecLanguages:"typescript",getDevNotesTabs:(e=["android","ios","web","windows"])=>[e.includes("android")?{label:"Android",value:"android"}:void 0,e.includes("ios")?{label:"iOS",value:"ios"}:void 0,e.includes("web")?{label:"Web",value:"web"}:void 0,e.includes("windows")?{label:"Windows",value:"windows"}
2:void 0].filter(Boolean),guides:[{label:"Expo Go Quickstart",value:"quickstart"},{label:"React Native CLI Quickstart",value:"native"}],oses:[{label:"macOS",value:"macos"},{label:"Windows",value:"windows"},{label:"Linux",value:"linux"}],packageManagers:[{label:"npm",value:"npm"},{label:"Yarn",value:"yarn"}],platforms:[{label:"Android",value:"android"},{label:"iOS",value:"ios"}],syntax:[{label:"Function Component",value:"functional"},{label:"Class Component",value:"classical"}],androidLanguages:[{label:"Java",value:"java"},{label:"Kotlin",value:"kotlin"}],appleLanguages:[{label:"ObjectiveC",value:"objc"},{label:"Swift",value:"swift"}],javaScriptSpecLanguages:[{label:"TypeScript",value:"typescript"},{label:"Flow",value:"flow"}],jsDebuggers:[{label:"Hermes Debugger / Expo",value:"hermes"},{label:"Flipper",value:"flipper"},{label:"New Debugger (Experimental)",value:"new-debugger"}],snackLanguages:[{label:"TypeScript",value:"typescript"},{label:"JavaScript",value:"javascript"}],defaultSnackLanguage:"typescript"}},43023(e,a,n){n.d(a,{R:()=>r,x:()=>l});var t=n(63696);let i={},o=t.createContext(i);function r(e){let a=t.useContext(o);return t.useMemo(function(){return"function"==typeof e?e(a):{...a,...e}},[a,e])}function l(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),t.createElement(o.Provider,{value:a},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.