PageSourceSearch

https://reactnative.dev/assets/js/4570e5b5.c8adfb7b.js

js reactnative.dev collected 2026-09-24 07:17:59 UTC 53,402 bytes, 2 lines download raw bytes

1"use strict";(self.webpackChunkreact_native_website=self.webpackChunkreact_native_website||[]).push([["15911"],{16523(e,n,t){t.r(n),t.d(n,{metadata:()=>a,default:()=>v,frontMatter:()=>d,contentTitle:()=>u,toc:()=>m,assets:()=>h});var a=JSON.parse('{"id":"legacy/native-modules-ios","title":"iOS Native Modules","description":"Welcome to Native Modules for iOS. Please start by reading the Native Modules Intro for an intro to what native modules are.","source":"@site/versioned_docs/version-0.83/legacy/native-modules-ios.md","sourceDirName":"legacy","slug":"/legacy/native-modules-ios","permalink":"/docs/0.83/legacy/native-modules-ios","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-ios.md\\"},{\\"label\\":\\"Edit page for 0.83 release\\",\\"href\\":\\"https://github.com/facebook/react-native-website/edit/main/website/versioned_docs/version-0.83/legacy/native-modules-ios.md\\"}]","tags":[],"version":"0.83","lastUpdatedAt":1780494561000,"frontMatter":{"id":"native-modules-ios","title":"iOS Native Modules"},"sidebar":"docs","previous":{"title":"Android Native Modules","permalink":"/docs/0.83/legacy/native-modules-android"},"next":{"title":"Native Modules NPM Package Setup","permalink":"/docs/0.83/legacy/native-modules-setup"}}'),i=t(62540),o=t(43023),r=t(79010),l=t(77779),s=t(98263),c=t(38469);let d={id:"native-modules-ios",title:"iOS Native Modules"},u,h={},m=[...r.RM,{value:"Create a Calendar Native Module",id:"create-a-calendar-native-module",level:2},{value:"Setup",id:"setup",level:3},{value:"Create Custom Native Module Files",id:"create-custom-native-module-files",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:"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:"Threading",id:"threading",level:3},{value:"Dependency Injection",id:"dependency-injection",level:3},{value:"Exporting Swift",id:"exporting-swift",level:3},{value:"Reserved Method Names",id:"reserved-method-names",level:3},{value:"invalidate()",id:"invalidate",level:4}];function p(e){let n={a:"a",admonition:"admonition",code:"code",h2:"h2",h3:"h3",h4:"h4",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)(n.p,{children:["Welcome to Native Modules for iOS. Please start by reading the ",(0,i.jsx)(n.a,{href:"native-modules-intro",children:"Native Modules Intro"})," for an intro to what native modules are."]}),"\n",(0,i.jsx)(n.h2,{id:"create-a-calendar-native-module",children:"Create a Calendar Native Module"}),"\n",(0,i.jsxs)(n.p,{children:["In the following guide you will create a native module, ",(0,i.jsx)(n.code,{children:"CalendarModule"}),", that will allow you to access Apple's calendar APIs from JavaScript. By the end you will be able to call ",(0,i.jsx)(n.code,{children:"CalendarModule.createCalendarEvent('Dinner Party', 'My House');"})," from JavaScript, invoking a native method that creates a calendar event."]}),"\n",(0,i.jsx)(n.h3,{id:"setup",children:"Setup"}),"\n",(0,i.jsx)(n.p,{children:"To get started, open up the iOS project within your React Native application in Xcode. You can find your iOS project here within a React Native app:"}),"\n",(0,i.jsxs)("figure",{children:[(0,i.jsx)("img",{src:"/docs/assets/native-modules-ios-open-project.png",width:"500",alt:"Image of opening up an iOS project within a React Native app inside of Xcode."}),(0,i.jsx)("figcaption",{children:"Image of where you can find your iOS project"})]}),"\n",(0,i.jsx)(n.p,{children:"We recommend using Xcode to write your native code. Xcode is built for iOS development, and using it will help you to quickly resolve smaller errors like code syntax."}),"\n",(0,i.jsx)(n.h3,{id:"create-custom-native-module-files",children:"Create Custom Native Module Files"}),"\n",(0,i.jsxs)(n.p,{children:["The first step is to create our main custom native module header and implementation files. Create a new file called ",(0,i.jsx)(n.code,{children:"RCTCalendarModule.h"})]}),"\n",(0,i.jsxs)("figure",{children:[(0,i.jsx)("img",{src:"/docs/assets/native-modules-ios-add-class.png",width:"500",alt:"Image of creating a class called  RCTCalendarModule.h."}),(0,i.jsx)("figcaption",{children:"Image of creating a custom native module file within the same folder as AppDelegate"})]}),"\n",(0,i.jsx)(n.p,{children:"and add the following to it:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"//  RCTCalendarModule.h\n#import <React/RCTBridgeModule.h>\n@interface RCTCalendarModule : NSObject <RCTBridgeModule>\n@end\n\n"})}),"\n",(0,i.jsxs)(n.p,{children:["You can use any name that fits the native module you are building. Name the class ",(0,i.jsx)(n.code,{children:"RCTCalendarModule"})," since you are creating a calendar native module. Since ObjC does not have language-level support for namespaces like Java or C++, convention is to prepe
1nd the class name with a substring. This could be an abbreviation of your application name or your infra name. RCT, in this example, refers to React."]}),"\n",(0,i.jsxs)(n.p,{children:["As you can see below, the CalendarModule class implements the ",(0,i.jsx)(n.code,{children:"RCTBridgeModule"})," protocol. A native module is an Objective-C class that implements the ",(0,i.jsx)(n.code,{children:"RCTBridgeModule"})," protocol."]}),"\n",(0,i.jsxs)(n.p,{children:["Next up, let\u2019s start implementing the native module. Create the corresponding implementation file using cocoa touch class in xcode, ",(0,i.jsx)(n.code,{children:"RCTCalendarModule.m"}),", in the same folder and include the following content:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:'// RCTCalendarModule.m\n#import "RCTCalendarModule.h"\n\n@implementation RCTCalendarModule\n\n// To export a module named RCTCalendarModule\nRCT_EXPORT_MODULE();\n\n@end\n\n'})}),"\n",(0,i.jsx)(n.h3,{id:"module-name",children:"Module Name"}),"\n",(0,i.jsxs)(n.p,{children:["For now, your ",(0,i.jsx)(n.code,{children:"RCTCalendarModule.m"})," native module only includes a ",(0,i.jsx)(n.code,{children:"RCT_EXPORT_MODULE"})," macro, which exports and registers the native module class with React Native. The ",(0,i.jsx)(n.code,{children:"RCT_EXPORT_MODULE"})," macro also takes an optional argument that specifies the name that the module will be accessible as in your JavaScript code."]}),"\n",(0,i.jsxs)(n.p,{children:["This argument is not a string literal. In the example below ",(0,i.jsx)(n.code,{children:"RCT_EXPORT_MODULE(CalendarModuleFoo)"})," is passed, not ",(0,i.jsx)(n.code,{children:'RCT_EXPORT_MODULE("CalendarModuleFoo")'}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"// To export a module named CalendarModuleFoo\nRCT_EXPORT_MODULE(CalendarModuleFoo);\n"})}),"\n",(0,i.jsx)(n.p,{children:"The native module can then be accessed in JS like this:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const {CalendarModuleFoo} = ReactNative.NativeModules;\n"})}),"\n",(0,i.jsx)(n.p,{children:'If you do not specify a name, the JavaScript module name will match the Objective-C class name, with any "RCT" or "RK" prefixes removed.'}),"\n",(0,i.jsxs)(n.p,{children:["Let's follow the example below and call ",(0,i.jsx)(n.code,{children:"RCT_EXPORT_MODULE"})," without any arguments. As a result, the module will be exposed to React Native using the name ",(0,i.jsx)(n.code,{children:"CalendarModule"}),", since that is the Objective-C class name, with RCT removed."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"// Without passing in a name this will export the native module name as the Objective-C class name with \u201CRCT\u201D removed\nRCT_EXPORT_MODULE();\n"})}),"\n",(0,i.jsx)(n.p,{children:"The native module can then be accessed in JS like this:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const {CalendarModule} = ReactNative.NativeModules;\n"})}),"\n",(0,i.jsx)(n.h3,{id:"export-a-native-method-to-javascript",children:"Export a Native Method to JavaScript"}),"\n",(0,i.jsxs)(n.p,{children:["React Native will not expose any methods in a native module to JavaScript unless explicitly told to. This can be done using the ",(0,i.jsx)(n.code,{children:"RCT_EXPORT_METHOD"})," macro. Methods written in the ",(0,i.jsx)(n.code,{children:"RCT_EXPORT_METHOD"})," macro are asynchronous and the return type is therefore always void. In order to pass a result from a ",(0,i.jsx)(n.code,{children:"RCT_EXPORT_METHOD"})," method to JavaScript you can use callbacks or emit events (covered below). Let\u2019s go ahead and set up a native method for our ",(0,i.jsx)(n.code,{children:"CalendarModule"})," native module using the ",(0,i.jsx)(n.code,{children:"RCT_EXPORT_METHOD"})," macro. Call it ",(0,i.jsx)(n.code,{children:"createCalendarEvent()"})," and for now have it take in name and location arguments as strings. Argument type options will be covered shortly."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)\n{\n}\n"})}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["Please note that the ",(0,i.jsx)(n.code,{children:"RCT_EXPORT_METHOD"})," macro will not be necessary with TurboModules unless your method relies on RCT argument conversion (see argument types below). Ultimately, React Native will remove ",(0,i.jsx)(n.code,{children:"RCT_EXPORT_MACRO,"})," so we discourage people from using ",(0,i.jsx)(n.code,{children:"RCTConvert"}),". Instead, you can do the argument conversion within the method body."]})}),"\n",(0,i.jsxs)(n.p,{children:["Before you build out the ",(0,i.jsx)(n.code,{children:"createCalendarEvent()"})," method\u2019s functionality, add a console log in the method so you can confirm it has been invoked from JavaScript in your React Native application. Use the ",(0,i.jsx)(n.code,{children:"RCT
1Log"})," APIs from React. Let\u2019s import that header at the top of your file and then add the log call."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:'#import <React/RCTLog.h>\nRCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)\n{\n RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);\n}\n'})}),"\n",(0,i.jsx)(n.h3,{id:"synchronous-methods",children:"Synchronous Methods"}),"\n",(0,i.jsxs)(n.p,{children:["You can use the ",(0,i.jsx)(n.code,{children:"RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD"})," to create a synchronous native method."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getName)\n{\nreturn [[UIDevice currentDevice] name];\n}\n"})}),"\n",(0,i.jsx)(n.p,{children:"The return type of this method must be of object type (id) and should be serializable to JSON. This means that the hook can only return nil or JSON values (e.g. NSNumber, NSString, NSArray, NSDictionary)."}),"\n",(0,i.jsxs)(n.p,{children:["At the moment, we do not recommend using synchronous methods, 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 use ",(0,i.jsx)(n.code,{children:"RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD"}),", your app can no longer use the Google Chrome debugger. This is because synchronous methods require the JS VM to share 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)(n.h3,{id:"test-what-you-have-built",children:"Test What You Have Built"}),"\n",(0,i.jsx)(n.p,{children:"At this point you have set up the basic scaffolding for your native module in iOS. Test that out by accessing the native module and invoking it\u2019s exported method in JavaScript."}),"\n",(0,i.jsxs)(n.p,{children:["Find a place in your application where you would like to add a call to the native module\u2019s ",(0,i.jsx)(n.code,{children:"createCalendarEvent()"})," method. Below is an example of a component, ",(0,i.jsx)(n.code,{children:"NewModuleButton"})," you can add in your app. You can invoke the native module inside ",(0,i.jsx)(n.code,{children:"NewModuleButton"}),"'s ",(0,i.jsx)(n.code,{children:"onPress()"})," function."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-tsx",metastring:'title="React TSX"',children:"import {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)(n.p,{children:["In order to access your native module from JavaScript you need to first import ",(0,i.jsx)(n.code,{children:"NativeModules"})," from React Native:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-tsx",metastring:'title="React TSX"',children:"import {NativeModules} from 'react-native';\n"})}),"\n",(0,i.jsxs)(n.p,{children:["You can then access the ",(0,i.jsx)(n.code,{children:"CalendarModule"})," native module off of ",(0,i.jsx)(n.code,{children:"NativeModules"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const {CalendarModule} = NativeModules;\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Now that you have the CalendarModule native module available, you can invoke your native method ",(0,i.jsx)(n.code,{children:"createCalendarEvent()"}),". Below it is added to the ",(0,i.jsx)(n.code,{children:"onPress()"})," method in ",(0,i.jsx)(n.code,{children:"NewModuleButton"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const onPress = () => {\n  CalendarModule.createCalendarEvent('testName', 'testLocation');\n};\n"})}),"\n",(0,i.jsx)(n.p,{children:"The final step is to rebuild the React Native app so that you can have the latest native code (with your new n
1ative 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)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:"npm run ios\n"})})}),(0,i.jsx)(s.A,{value:"yarn",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:"yarn ios\n"})})})]}),"\n",(0,i.jsx)(n.h3,{id:"building-as-you-iterate",children:"Building as You Iterate"}),"\n",(0,i.jsx)(n.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 JS bundle 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)(n.h3,{id:"recap",children:"Recap\u2728"}),"\n",(0,i.jsxs)(n.p,{children:["You should now be able to invoke your ",(0,i.jsx)(n.code,{children:"createCalendarEvent()"})," method on your native module in JavaScript. Since you are using ",(0,i.jsx)(n.code,{children:"RCTLog"})," in the function, you can confirm your native method is being invoked by ",(0,i.jsx)(n.a,{href:"https://reactnative.dev/docs/debugging#chrome-developer-tools",children:"enabling debug mode in your app"})," and looking at the JS console in Chrome or the mobile app debugger Flipper. You should see your ",(0,i.jsx)(n.code,{children:'RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);'})," message each time you invoke the native module method."]}),"\n",(0,i.jsxs)("figure",{children:[(0,i.jsx)("img",{src:"/docs/assets/native-modules-ios-logs.png",width:"1000",alt:"Image of logs."}),(0,i.jsx)("figcaption",{children:"Image of iOS logs in Flipper"})]}),"\n",(0,i.jsx)(n.p,{children:"At this point you have created an iOS native module and invoked a method on it from JavaScript in your React Native application. You can read on to learn more about things like what argument types your native module method takes and how to setup callbacks and promises within your native module."}),"\n",(0,i.jsx)(n.h2,{id:"beyond-a-calendar-native-module",children:"Beyond a Calendar Native Module"}),"\n",(0,i.jsx)(n.h3,{id:"better-native-module-export",children:"Better Native Module Export"}),"\n",(0,i.jsxs)(n.p,{children:["Importing your native module by pulling it off of ",(0,i.jsx)(n.code,{children:"NativeModules"})," like above is a bit clunky."]}),"\n",(0,i.jsx)(n.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 NativeCalendarModule.js with the following content:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.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)(n.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, with these type annotations, all your JS code will be type safe. These annotations 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 Calendar Module:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.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)(n.p,{children:"In your other JavaScript files you can access the native module and invoke its method like this:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-tsx",metastring:'title="React TSX"',children:"import NativeCalendarModule from './NativeCalendarModule';\nNativeCalendarModule.createCalendarEvent('foo', 'bar');\n"})}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["This assumes that the place you are importing ",(0,i.jsx)(n.code,{children:"CalendarModule"})," is in the same hierarchy as ",(0,i.jsx)(n.code,{children:"NativeCalendarModule.js"}),". Please update the relative import as necessary."]})}),"\n",(0,i.jsx)(n.h3,{id:"argument-types",children:"Argument Types"}),"\n",(0,i.jsx)(n.p,{children:"When a native module method is invoked in JavaScript, React Native converts the arguments from JS objects to their Objective-C/Swift object analogues. So for example, if your Objective-C Native Module method accepts a NSNumber, 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)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{children:"Objective-C"}),(0,i.jsx)(n.th,{children:"JavaScript"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"NSString"}),(0,i.jsx)(n.td,{children:"string, ?string"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"BOOL"}),(0,i.jsx)(n.td,{children:"boolean"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"double"}),(0,i.jsx)(n.td,{children:"number"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"NSNumber"}),(0,i.jsx)(n.td,{children:"?number"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"NSArray"}),(0,i.jsx)(n.td,{children:"Array, ?Array"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"NSDictionary"}),(0,i.jsx)(n.td,{children:"Object, ?Object"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"RCTResponseSenderBlock"}),(0,i.jsx)(n.td,{children:"Function (success)"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"RCTResponseSenderBlock, RCTResponseErrorBlock"}),(0,i.jsx)(n.td,{children:"Function (failure)"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"RCTPromiseResolveBlock, RCTPromiseRejectBlock"}),(0,i.jsx)(n.td,{children:"Promise"})]})]})]}),"\n",(0,i.jsxs)(n.admonition,{type:"info",children:[(0,i.jsx)(n.p,{children:"The following types are currently supported but will not be supported in TurboModules. Please avoid using them."}),(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Function (failure) -> RCTResponseErrorBlock"}),"\n",(0,i.jsx)(n.li,{children:"Number -> NSInteger"}),"\n",(0,i.jsx)(n.li,{children:"Number -> CGFloat"}),"\n",(0,i.jsx)(n.li,{children:"Number -> float"}),"\n"]})]}),"\n",(0,i.jsxs)(n.p,{children:["For iOS, you can also write native module methods with any argument type that is supported by the ",(0,i.jsx)(n.code,{children:"RCTConvert"})," class (see ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/react-native/blob/main/packages/react-native/React/Base/RCTConvert.h",children:"RCTConvert"})," for details about what is supported). The RCTConvert helper functions all accept a JSON value as input and map it to a native Objective-C type or class."]}),"\n",(0,i.jsx)(n.h3,{id:"exporting-constants",children:"Exporting Constants"}),"\n",(0,i.jsxs)(n.p,{children:["A native module can export constants by overriding the native method ",(0,i.jsx)(n.code,{children:"constantsToExport()"}),". Below ",(0,i.jsx)(n.code,{children:"constantsToExport()"})," is overridden, and returns a Dictionary that contains a default event name property you can access in JavaScript like so:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:'- (NSDictionary *)constantsToExport\n{\n return @{ @"DEFAULT_EVENT_NAME": @"New Event" };\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:["The constant can then be accessed by invoking ",(0,i.jsx)(n.code,{children:"getConstants()"})," on the native module in JS like so:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.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)(n.p,{children:["Technically, it is possible to access constants exported in ",(0,i.jsx)(n.code,{children:"constantsToExport()"})," directly off the ",(0,i.jsx)(n.code,{children:"NativeModule"})," 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.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["The constants are exported only at initialization time, so if you change ",(0,i.jsx)(n.code,{children:"constantsToExport()"})," values at runtime it won't affect the JavaScript environment."]})}),"\n",(0,i.jsxs)(n.p,{children:["For iOS, if you override ",(0,i.jsx)(n.code,{children:"constantsToExport()"})," then you should also implement ",(0,i.jsx)(n.code,{children:"+ requiresMainQueueSetup"})," to let React Native know if your module needs to be initialized on the main thread, before any JavaScript code executes. Otherwise you will see a warning that in the future your module may be initialized on a background thread unless you explicitly opt out with ",(0,i.jsx)(n.code,{children:"+ requiresMainQueueSetup:"}),". If your module does not require access to UIKit, then you should respond to ",(0,i.jsx)(n.code,{children:"+ requiresMainQueueSetup"})," with NO."]}),"\n",(0,i.jsx)(n.h3,{id:"callbacks",children:"Callbacks"}),"\n",(0,i.jsx)(n.p,{children:"Native modules also support a unique kind of argument - a callback. Callbacks are used to pass data from Objective-C to JavaScript for asynchronous methods. They can also be used to asynchronously execute JS from the native side."}),"\n",(0,i.jsxs)(n.p,{children:["For iOS, callbacks are implemented using the type ",(0,i.jsx)(n.code,{children:"RCTResponseSenderBlock"}),". Below the callback parameter ",(0,i.jsx)(n.code,{children:"myCallBack"})," is added to the ",(0,i.jsx)(n.code,{children:"createCalendarEventMethod()"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title\n                location:(NSString *)location\n                myCallback:(RCTResponseSenderBlock)callback)\n\n"})}),"\n",(0,i.jsxs)(n.p,{children:["You can then invoke the callback in your native function, providing whatever result you want to pass to JavaScript in an array. Note that ",(0,i.jsx)(n.code,{children:"RCTResponseSenderBlock"})," accepts only one argument - an array of parameters to pass to the JavaScript callback. Below you will pass back the ID of an event created in an earlier call."]}),"\n",(0,i.jsx)(n.admonition,{type:"info",children:(0,i.jsx)(n.p,{children:"It is important to highlight that the callback is not invoked immediately after the native function completes\u2014remember the communication is asynchronous."})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:'RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title location:(NSString *)location callback: (RCTResponseSenderBlock)callback)\n{\n NSInteger eventId = ...\n callback(@[@(eventId)]);\n\n RCTLogInfo(@"Pretending to create an event %@ at %@", title, location);\n}\n\n'})}),"\n",(0,i.jsx)(n.p,{children:"This method could then be accessed in JavaScript using the following:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const onSubmit = () => {\n  CalendarModule.createCalendarEvent(\n    'Party',\n    '04-12-2020',\n    eventId =>
1 {\n      console.log(`Created a new event with id ${eventId}`);\n    },\n  );\n};\n"})}),"\n",(0,i.jsxs)(n.p,{children:["A native module is supposed to invoke its callback only once. It can, however, store the callback and invoke it later. This pattern is often used to wrap iOS APIs that require delegates\u2014 see ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/react-native/blob/main/packages/react-native/React/CoreModules/RCTAlertManager.mm",children:(0,i.jsx)(n.code,{children:"RCTAlertManager"})})," for an example. If the callback is never invoked, some memory is leaked."]}),"\n",(0,i.jsx)(n.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 array as an error object."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"RCT_EXPORT_METHOD(createCalendarEventCallback:(NSString *)title location:(NSString *)location callback: (RCTResponseSenderBlock)callback)\n{\n  NSNumber *eventId = [NSNumber numberWithInt:123];\n  callback(@[[NSNull null], eventId]);\n}\n"})}),"\n",(0,i.jsx)(n.p,{children:"In JavaScript, you can then check the first argument to see if an error was passed through:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const onPress = () => {\n  CalendarModule.createCalendarEventCallback(\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)(n.p,{children:"Another option is to use two separate callbacks: onFailure and onSuccess."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"RCT_EXPORT_METHOD(createCalendarEventCallback:(NSString *)title\n                  location:(NSString *)location\n                  errorCallback: (RCTResponseSenderBlock)errorCallback\n                  successCallback: (RCTResponseSenderBlock)successCallback)\n{\n  @try {\n    NSNumber *eventId = [NSNumber numberWithInt:123];\n    successCallback(@[eventId]);\n  }\n\n  @catch ( NSException *e ) {\n    errorCallback(@[e]);\n  }\n}\n"})}),"\n",(0,i.jsx)(n.p,{children:"Then in JavaScript you can add a separate callback for error and success responses:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-tsx",metastring:'title="React TSX"',children:"const onPress = () => {\n  CalendarModule.createCalendarEventCallback(\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.jsxs)(n.p,{children:["If you want to pass error-like objects to JavaScript, use ",(0,i.jsx)(n.code,{children:"RCTMakeError"})," from ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/react-native/blob/main/packages/react-native/React/Base/RCTUtils.h",children:(0,i.jsx)(n.code,{children:"RCTUtils.h."})})," Right now this only passes an Error-shaped dictionary to JavaScript, but React Native aims to automatically generate real JavaScript Error objects in the future. You can also provide a ",(0,i.jsx)(n.code,{children:"RCTResponseErrorBlock"})," argument, which is used for error callbacks and accepts an ",(0,i.jsx)(n.code,{children:"NSError \\* object"}),". Please note that this argument type will not be supported with TurboModules."]}),"\n",(0,i.jsx)(n.h3,{id:"promises",children:"Promises"}),"\n",(0,i.jsxs)(n.p,{children:["Native modules can also fulfill a promise, which can simplify your JavaScript, especially when using ES2016's ",(0,i.jsx)(n.code,{children:"async/await"})," syntax. When the last parameter of a native module method is a ",(0,i.jsx)(n.code,{children:"RCTPromiseResolveBlock"})," and ",(0,i.jsx)(n.code,{children:"RCTPromiseRejectBlock"}),", its corresponding JS method will return a JS Promise object."]}),"\n",(0,i.jsx)(n.p,{children:"Refactoring the above code to use a promise instead of callbacks looks like this:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:'RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title\n                 location:(NSString *)location\n                 resolver:(RCTPromiseResolveBlock)resolve\n                 rejecter:(RCTPromiseRejectBlock)reject)\n{\n NSInteger eventId = createCalendarEvent();\n if (eventId) {\n    resolve(@(eventId));\n  } else {\n    reject(@"event_failure", @"no event id returned", nil);\n  }\n}\n\n'})}),"\n",(0,i.jsxs)(n.p,{children:["The JavaScript counterpart of this method returns a Promise. This means you can use the ",(0,i.jsx)(n.code,{children:"await"})," keyword within an async function to call it and wait for its result:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.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)(n.h3,{id:"sending-events-to-javascript",children:"Sending Events to JavaScript"}),"\n",(0,i.jsxs)(n.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 iOS calendar app will occur soon. The preferred way to do this is to subclass ",(0,i.jsx)(n.code,{children:"RCTEventEmitter"}),", implement ",(0,i.jsx)(n.code,{children:"supportedEvents"})," and call self ",(0,i.jsx)(n.code,{children:"sendEventWithName"}),":"]}),"\n",(0,i.jsxs)(n.p,{children:["Update your header class to import ",(0,i.jsx)(n.code,{children:"RCTEventEmitter"})," and subclass ",(0,i.jsx)(n.code,{children:"RCTEventEmitter"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"//  CalendarModule.h\n\n#import <React/RCTBridgeModule.h>\n#import <React/RCTEventEmitter.h>
1\n\n@interface CalendarModule : RCTEventEmitter <RCTBridgeModule>\n@end\n\n"})}),"\n",(0,i.jsxs)(n.p,{children:["JavaScript code can subscribe to these events by creating a new ",(0,i.jsx)(n.code,{children:"NativeEventEmitter"})," instance around your module."]}),"\n",(0,i.jsxs)(n.p,{children:["You will receive a warning if you expend resources unnecessarily by emitting an event while there are no listeners. To avoid this, and to optimize your module's workload (e.g. by unsubscribing from upstream notifications or pausing background tasks), you can override ",(0,i.jsx)(n.code,{children:"startObserving"})," and ",(0,i.jsx)(n.code,{children:"stopObserving"})," in your ",(0,i.jsx)(n.code,{children:"RCTEventEmitter"})," subclass."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:'@implementation CalendarModule\n{\n  bool hasListeners;\n}\n\n// Will be called when this module\'s first listener is added.\n-(void)startObserving {\n    hasListeners = YES;\n    // Set up any upstream listeners or background tasks as necessary\n}\n\n// Will be called when this module\'s last listener is removed, or on dealloc.\n-(void)stopObserving {\n    hasListeners = NO;\n    // Remove upstream listeners, stop unnecessary background tasks\n}\n\n- (void)calendarEventReminderReceived:(NSNotification *)notification\n{\n  NSString *eventName = notification.userInfo[@"name"];\n  if (hasListeners) {// Only send events if anyone is listening\n    [self sendEventWithName:@"EventReminder" body:@{@"name": eventName}];\n  }\n}\n\n'})}),"\n",(0,i.jsx)(n.h3,{id:"threading",children:"Threading"}),"\n",(0,i.jsxs)(n.p,{children:["Unless the native module provides its own method queue, it shouldn't make any assumptions about what thread it's being called on. Currently, if a native module doesn't provide a method queue, React Native will create a separate GCD queue for it and invoke its methods there. Please note that this is an implementation detail and might change. If you want to explicitly provide a method queue for a native module, override the ",(0,i.jsx)(n.code,{children:"(dispatch_queue_t) methodQueue"})," method in the native module. For example, if it needs to use a main-thread-only iOS API, it should specify this via:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"- (dispatch_queue_t)methodQueue\n{\n  return dis
1patch_get_main_queue();\n}\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Similarly, if an operation may take a long time to complete, the native module can specify its own queue to run operations on. Again, currently React Native will provide a separate method queue for your native module, but this is an implementation detail you should not rely on. If you don't provide your own method queue, in the future, your native module's long running operations may end up blocking async calls being executed on other unrelated native modules. The ",(0,i.jsx)(n.code,{children:"RCTAsyncLocalStorage"})," module here, for example, creates its own queue so the React queue isn't blocked waiting on potentially slow disk access."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:'- (dispatch_queue_t)methodQueue\n{\n return dispatch_queue_create("com.facebook.React.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:["The specified ",(0,i.jsx)(n.code,{children:"methodQueue"})," will be shared by all of the methods in your module. If only one of your methods is long-running (or needs to be run on a different queue than the others for some reason), you can use ",(0,i.jsx)(n.code,{children:"dispatch_async"})," inside the method to perform that particular method's code on another queue, without affecting the others:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"RCT_EXPORT_METHOD(doSomethingExpensive:(NSString *)param callback:(RCTResponseSenderBlock)callback)\n{\n dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{\n   // Call long-running code on background thread\n   ...\n   // You can invoke callback from any thread/queue\n   callback(@[...]);\n });\n}\n\n"})}),"\n",(0,i.jsx)(n.admonition,{title:"Sharing dispatch queues between modules",type:"info",children:(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"methodQueue"})," method will be called once when the module is initialized, and then retained by React Native, so there is no need to keep a reference to the queue yourself, unless you wish to make use of it within your module. However, if you wish to share the same queue between multiple modules then you will need to ensure that you retain and return the same queue instance for each of them."]})}),"\n",(0,i.jsx)(n.h3,{id:"dependency-injection",children:"Dependency Injection"}),"\n",(0,i.jsx)(n.p,{children:"React Native will create and initialize any registered native modules automatically. However, you may wish to create and initialize your own module instances to, for example, inject dependencies."}),"\n",(0,i.jsxs)(n.p,{children:["You can do this by creating a class that implements the ",(0,i.jsx)(n.code,{children:"RCTBridgeDelegate"})," Protocol, initializing an ",(0,i.jsx)(n.code,{children:"RCTBridge"})," with the delegate as an argument and initialising a ",(0,i.jsx)(n.code,{children:"RCTRootView"})," with the initialized bridge."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"id<RCTBridgeDelegate> moduleInitialiser = [[classThatImplementsRCTBridgeDelegate alloc] init];\n\nRCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:moduleInitialiser launchOptions:nil];\n\nRCTRootView *rootView = [[RCTRootView alloc]\n                        initWithBridge:bridge\n                            moduleName:kModuleName\n                     initialProperties:nil];\n"})}),"\n",(0,i.jsx)(n.h3,{id:"exporting-swift",children:"Exporting Swift"}),"\n",(0,i.jsxs)(n.p,{children:["Swift doesn't have support for macros, so exposing native modules and their methods to JavaScript inside React Native requires a bit more setup. However, it works relatively the same. Let's say you have the same ",(0,i.jsx)(n.code,{children:"CalendarModule"})," but as a Swift class:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-swift",metastring:'title="Swift"',children:'// CalendarModule.swift\n\n@objc(CalendarModule)\nclass CalendarModule: NSObject {\n\n @objc(addEvent:location:date:)\n func addEvent(_ name: String, location: String, date: NSNumber) -> Void {\n   // Date is ready to use!\n }\n\n @objc\n func constantsToExport() -> [String: Any]! {\n   return ["someKey": "someValue"]\n }\n\n}\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["It is important to use the ",(0,i.jsx)(n.code,{children:"@objc"})," modifiers to ensure the class and functions are exported properly to the Objective-C runtime."]})}
1),"\n",(0,i.jsx)(n.p,{children:"Then create a private implementation file that will register the required information with React Native:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"// CalendarModuleBridge.m\n#import <React/RCTBridgeModule.h>\n\n@interface RCT_EXTERN_MODULE(CalendarModule, NSObject)\n\nRCT_EXTERN_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(nonnull NSNumber *)date)\n\n@end\n"})}),"\n",(0,i.jsxs)(n.p,{children:["For those of you new to Swift and Objective-C, whenever you ",(0,i.jsx)(n.a,{href:"https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html",children:"mix the two languages in an iOS project"}),", you will also need an additional bridging file, known as a bridging header, to expose the Objective-C files to Swift. Xcode will offer to create this header file for you if you add your Swift file to your app through the Xcode ",(0,i.jsx)(n.code,{children:"File>New File"})," menu option. You will need to import ",(0,i.jsx)(n.code,{children:"RCTBridgeModule.h"})," in this header file."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-objectivec",metastring:'title="Objective-C"',children:"// CalendarModule-Bridging-Header.h\n#import <React/RCTBridgeModule.h>\n"})}),"\n",(0,i.jsxs)(n.p,{children:["You can also use ",(0,i.jsx)(n.code,{children:"RCT_EXTERN_REMAP_MODULE"})," and ",(0,i.jsx)(n.code,{children:"_RCT_EXTERN_REMAP_METHOD"})," to alter the JavaScript name of the module or methods you are exporting. For more information see ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/react-native/blob/main/packages/react-native/React/Base/RCTBridgeModule.h",children:(0,i.jsx)(n.code,{children:"RCTBridgeModule"})}),"."]}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsx)(n.p,{children:"Important when making third party modules: Static libraries with Swift are only supported in Xcode 9 and later. In order for the Xcode project to build when you use Swift in the iOS static library you include in the module, your main app project must contain Swift code and a bridging header itself. If your app project does not contain any Swift code, a workaround can be a single empty .swift file and an empty bridging header."})}),"\n",(0,i.jsx)(n.h3,{id:"reserved-method-names",children:"Reserved Method Names"}),"\n",(0,i.jsx)(n.h4,{id:"invalidate",children:"invalidate()"}),"\n",(0,i.jsxs)(n.p,{children:["Native modules can conform to the ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/react-native/blob/main/packages/react-native/React/Base/RCTInvalidating.h",children:"RCTInvalidating"})," protocol on iOS by implementing the ",(0,i.jsx)(n.code,{children:"invalidate()"})," method. This method ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/react-native/blob/0.62-stable/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm#L456",children:"can be invoked"})," when the native bridge is invalidated (i.e.: on devmode reload). Please use this mechanism as necessary to do the required cleanup for your native module."]})]})}function v(e={}){let{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(p,{...e})}):p(e)}},79010(e,n,t){t.d(n,{Ay:()=>l,RM:()=>o});var a=t(62540),i=t(43023);let o=[];function r(e){let n={a:"a",admonition:"admonition",p:"p",...(0,i.R)(),...e.components};return(0,a.jsx)(n.admonition,{type:"info",children:(0,a.jsxs)(n.p,{children:["Native Module and Native Components are our stable technologies used by the legacy architecture.\nThey will be deprecated 
1in the future when the New Architecture will be stable. The New Architecture uses ",(0,a.jsx)(n.a,{href:"https://github.com/reactwg/react-native-new-architecture/blob/main/docs/turbo-modules.md",children:"Turbo Native Module"})," and ",(0,a.jsx)(n.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:n}={...(0,i.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(r,{...e})}):r(e)}},98263(e,n,t){t.d(n,{A:()=>l});var a=t(62540);t(63696);var i=t(11750),o=t(23865);function r({children:e,className:n,hidden:t}){return(0,a.jsx)("div",{role:"tabpanel",className:(0,i.A)("tabItem_wHwb",n),hidden:t,children:e})}function l({children:e,className:n,value:t}){let{selectedValue:i,lazy:s}=(0,o.uc)(),c=t===i;return!c&&s?null:(0,a.jsx)(r,{className:n,hidden:!c,children:e})}},77779(e,n,t){t.d(n,{A:()=>h});var a=t(62540);t(63696);var i=t(11750),o=t(46065),r=t(23865),l=t(45194),s=t(83029);function c({className:e}){let{selectedValue:n,selectValue:t,tabValues:o,block:s}=(0,r.uc)(),d=[],{blockElementScrollPositionUntilNextRender:u}=(0,l.a_)(),h=e=>{let a=e.currentTarget,i=o[d.indexOf(a)].value;i!==n&&(u(a),t(i))},m=e=>{let n=null;switch(e.key){case"Enter":h(e);break;case"ArrowRight":{let t=d.indexOf(e.currentTarget)+1;n=d[t]??d[0];break}case"ArrowLeft":{let t=d.indexOf(e.currentTarget)-1;n=d[t]??d[d.length-1]}}n?.focus()};return(0,a.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,i.A)("tabs",{"tabs--block":s},e),children:o.map(({value:e,label:t,attributes:o})=>(0,a.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{d.push(e)},onKeyDown:m,onClick:h,...o,className:(0,i.A)("tabs__item","tabItem_l0OV",o?.className,{"tabs__item--active":n===e}),children:t??e},e))})}function d({children:e}){return(0,a.jsx)("div",{className:"margin-top--md",children:e})}function u({className:e,children:n}){return(0,a.jsxs)("div",{className:(0,i.A)(o.G.tabs.container,"tabs-container","tabList_J5MA"),children:[(0,a.jsx)(c,{className:e}),(0,a.jsx)(d,{children:n})]})}function h(e){let n=(0,s.A)(),t=(0,r.OC)(e);return(0,a.jsx)(r.O_,{value:t,children:(0,a.jsx)(u,{className:e.className,children:(0,r.vT)(e.children)})},String(n))}},23865(e,n,t){t.d(n,{OC:()=>h,O_:()=>v,uc:()=>p,vT:()=>d});var a=t(62540),i=t(63696),o=t(49519),r=t(6743),l=t(88511),s=t(15396),c=t(89030);function d(e){return i.Children.toArray(e).filter(e=>"\n"!==e)}function u({value:e,tabValues:n}){return n.some(n=>n.value===e)}function h(e){let n,{defaultValue:t,queryString:a=!1,groupId:d}=e,h=function(e){let{values:n,children:t}=e;return(0,i.useMemo)(()=>{let e=n??i.Children.toArray(t).flatMap(e=>{if(!e)return[];if((0,i.isValidElement)(e)&&function(e){let{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];let n="string"==typeof e.type?e.type:e.type.name;throw Error(`Docusaurus error: Bad <Tabs> child <${n}>: 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:n,attributes:t,default:a}})=>({value:e,label:n,attributes:t,default:a})),a=(0,s.XI)(e,(e,n)=>e.value===n.value);if(a.length>0)throw Error(`Docusaurus error: Duplicate values "${a.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`);return e},[n,t])}(e),[m,p]=(0,i.useState)(()=>(function({defaultValue:e,tabValues:n}){if(0===n.length)throw Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!u({value:e,tabValues:n}))throw Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}let t=n.find(e=>e.default)??n[0];if(!t)throw Error("Unexpected error: 0 tabValues");return t.value})({defaultValue:t,tabValues:h})),[v,g]=function({queryString:e=!1,groupId:n}){let t=(0,o.W6)(),a=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)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 n??null}({queryString:e,groupId:n});return[(0,l.aZ)(a),(0,i.useCallback)(e=>{if(!a)return;let n=new URLSearchParams(t.location.search);n.set(a,e),t.replace({...t.location,search:n.toString()})},[a,t])]}({queryString:a,groupId:d}),[j,x]=function({groupId:e}){let n=e?`docusaurus.tab.${e}`:null,[t,a]=(0,c.Dv)(n);return[t,(0,i.useCallback)(e=>{n&&a.set(e)},[n,a])]}({groupId:d}),b=u({value:n=v??j,tabValues:h})?n:null;return(0,r.A)(()=>{b&&p(b)},[b]),{selectedValue:m,selectValue:(0,i.useCallback)(e=>{if(!u({value:e,tabValues:h}))throw Error(`Can't select invalid tab value=${e}`);p(e),g(e),x(e)},[g,x,h]),tabValues:h,lazy:e.lazy??!1,block:e.block??!1}}let m=(0,i.createContext)(null);function p(){let e=i.useContext(m);if(!e)throw Error("useTabsContext() must be used within a Tabs component");return e}function v(e){return(0,a.jsx)(m.Provider,{value:e.value,children:e.children})}},38469(e,n,t){t.d(n,{A:()=>r});var a=t(93227);let i=!!a.A.canUseDOM&&navigator.platform.startsWith("Mac"),o=!!a.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,n,t){t.d(n,{R:()=>r,x:()=>l});var a=t(63696);let i={},o=a.createContext(i);function r(e){let n=a.useContext(o);return a.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),a.createElement(o.Provider,{value:n},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.