1--- 2title: Module API Reference 3description: An API reference of Expo modules API. 4sidebar_title: Module API 5--- 6 7import { CodeBlocksTable } from '~/components/plugins/CodeBlocksTable'; 8import { APIBox } from '~/components/plugins/APIBox'; 9import { PlatformTags } from '~/ui/components/Tag'; 10import { APIMethod } from '~/components/plugins/api/APISectionMethods'; 11 12The native modules API is an abstraction layer on top of [JSI](https://reactnative.dev/architecture/glossary#javascript-interfaces-jsi) and other low-level primitives that React Native is built upon. It is built with modern languages (Swift and Kotlin) and provides an easy-to-use and convenient API that is consistent across platforms where possible. 13 14## Definition components 15 16As you might have noticed in the snippets on the [Get Started](/modules/get-started) page, each module class must implement the `definition` function. 17The module definition consists of the DSL components that describe the module's functionality and behavior. 18 19<APIBox header="Name"> 20 21Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument. This can be inferred from the module's class name, but it's recommended to set it explicitly for clarity. 22 23```swift Swift / Kotlin 24Name("MyModuleName") 25``` 26 27</APIBox> 28<APIBox header="Constants"> 29 30Sets constant properties on the module. Can take a dictionary or a closure that returns a dictionary. 31 32<CodeBlocksTable> 33 34```swift 35// Created from the dictionary 36Constants([ 37 "PI": Double.pi 38]) 39 40// or returned by the closure 41Constants { 42 return [ 43 "PI": Double.pi 44 ] 45} 46``` 47 48```kotlin 49// Passed as arguments 50Constants( 51 "PI" to kotlin.math.PI 52) 53 54// or returned by the closure 55Constants { 56 return@Constants mapOf( 57 "PI" to kotlin.math.PI 58 ) 59} 60``` 61 62</CodeBlocksTable> 63</APIBox> 64<APIBox header="Function"> 65 66Defines a native synchronous function that will be exported to JavaScript. Synchronous means that when the function is executed in JavaScript, its native code is run on the same thread and blocks further execution of the script until the native function returns. 67 68#### Arguments 69 70- **name**: `String` — Name of the function that you'll call from JavaScript. 71- **body**: `(args...) -> ReturnType` — The closure to run when the function is called. 72 73The function can receive up to 8 arguments. This is due to the limitations of generics in both Swift and Kotlin because this component must be implemented separately for each arity. 74 75See the [Argument types](#argument-types) section for more details on what types can be used in the function body. 76 77<CodeBlocksTable> 78 79```swift 80Function("syncFunction") { (message: String) in 81 return message 82} 83``` 84 85```kotlin 86Function("syncFunction") { message: String -> 87 return@Function message 88} 89``` 90 91</CodeBlocksTable> 92 93```js JavaScript 94import { requireNativeModule } from 'expo-modules-core'; 95 96// Assume that we have named the module "MyModule" 97const MyModule = requireNativeModule('MyModule'); 98 99function getMessage() { 100 return MyModule.syncFunction('bar'); 101} 102``` 103 104</APIBox> 105<APIBox header="AsyncFunction"> 106 107Defines a JavaScript function that always returns a `Promise` and whose native code is by default dispatched on a different thread than the JavaScript runtime runs on. 108 109#### Arguments 110 111- **name**: `String` — Name of the function that you'll call from JavaScript. 112- **body**: `(args...) -> ReturnType` — The closure to run when the function is called. 113 114If the type of the last argument is `Promise`, the function will wait for the promise to be resolved or rejected before the response is passed back to JavaScript. Otherwise, the function is immediately resolved with the returned value or rejected if it throws an exception. 115The function can receive up to 8 arguments (including the promise). 116 117See the [Argument types](#argument-types) section for more details on what types can be used in the function body. 118 119It is recommended to use `AsyncFunction` over `Function` when it: 120 121- does I/O bound tasks such as sending network requests or interacting with the file system 122- needs to be run on a different thread, for example, the main UI thread for UI-related tasks 123- is an extensive or long-lasting operation that would block the JavaScript thread which in turn would reduce the responsiveness of the application 124 125<CodeBlocksTable> 126 127```swift 128AsyncFunction("asyncFunction") { (message: String, promise: Promise) in 129 DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { 130 promise.resolve(message) 131 } 132} 133``` 134 135```kotlin 136AsyncFunction("asyncFunction") { message: String, promise: Promise -> 137 launch(Dispatchers.Main) { 138 promise.resolve(message) 139 } 140} 141``` 142 143</CodeBlocksTable> 144 145```js JavaScript 146import { requireNativeModule } from 'expo-modules-core'; 147 148// Assume that we have named the module "MyModule" 149const MyModule = requireNativeModule('MyModule'); 150 151async function getMessageAsync() { 152 return await MyModule.asyncFunction('bar'); 153} 154``` 155 156--- 157 158#### Kotlin coroutines <PlatformTags prefix="" platforms={['android']} /> 159 160`AsyncFunction` can receive a suspendable body on Android. However, it has to be passed in the infix notation after the `Coroutine` block. You can read more about suspendable functions and coroutines on [coroutine overview](https://kotlinlang.org/docs/coroutines-overview.html). 161 162`AsyncFunction` with a suspendable body can't receive `Promise` as an argument. It uses a suspension mechanism to execute asynchronous calls. 163The function is immediately resolved with the returned value of the provided suspendable block or rejected if it throws an exception. The function can receive up to 8 arguments. 164 165By default, suspend functions are dispatched on the module's coroutine scope. Moreover, every other suspendable function called from the body block is run within the same scope. 166This scope's lifecycle is bound to the module's lifecycle - all unfinished suspend functions will be canceled when the module is deallocated. 167 168```kotlin Kotlin 169AsyncFunction("suspendFunction") Coroutine { message: String -> 170 launch { 171 return@Coroutine message 172 } 173} 174``` 175 176</APIBox> 177<APIBox header="Events"> 178 179Defines event names that the module can send to JavaScript. 180 181> **Note**: This component can be used inside of the [`View`](#view) block to define callback names. See [`View callbacks`](#view-callbacks) 182 183<CodeBlocksTable> 184 185```swift 186Events("onCameraReady", "onPictureSaved", "onBarCodeScanned") 187``` 188 189```kotlin 190Events("onCameraReady", "onPictureSaved", "onBarCodeScanned") 191``` 192 193</CodeBlocksTable> 194 195See [Sending events](#sending-events) to learn how to send events from the native code to JavaScript/TypeScript. 196 197</APIBox> 198<APIBox header="Property"> 199 200Defines a new property directly on the JavaScript object that represents a native module. It is the same as calling [`Object.defineProperty`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) on the module object. 201 202To declare a read-only property, you can use a shorthanded syntax that requires two arguments: 203 204- **name**: `String` — Name of the property that you'll use from JavaScript. 205- **getter**: `() -> PropertyType` — The closure to run when the getter for a property was called. 206 207<CodeBlocksTable> 208 209```swift 210Property("foo") { 211 return "bar" 212} 213``` 214 215```kotlin 216Property("foo") { 217 return@Property "bar" 218} 219``` 220 221</CodeBlocksTable> 222 223In the case of the mutable property, both the getter and the setter closure are needed (using the syntax below is also possible to declare a property with only a setter): 224 225- **name**: `String` — Name of the property that you'll use from JavaScript. 226- **getter**: `() -> PropertyType` — The closure to run when the getter for a property was called. 227- **setter**: `(newValue: PropertyType) -> void` — The closure to run when the setter for a property was called. 228 229<CodeBlocksTable> 230 231```swift 232Property("foo") 233 .get { return "bar" } 234 .set { (newValue: String) in 235 // do something with new value 236 } 237``` 238 239```kotlin 240Property("foo") 241 .get { return@get "bar" } 242 .set { newValue: String -> 243 // do something with new value 244 } 245``` 246 247</CodeBlocksTable> 248 249```js JavaScript 250import { requireNativeModule } from 'expo-modules-core'; 251 252// Assume that we have named the module "MyModule" 253const MyModule = requireNativeModule('MyModule'); 254 255// Obtain the property value 256MyModule.foo; 257 258// Set a new value 259MyModule.foo = 'foobar'; 260``` 261 262</APIBox> 263<APIBox header="View"> 264 265Enables the module to be used as a native view. Definition components that are accepted as part of the view definition: [`Prop`](#prop), [`Events`](#events), [`GroupView`](#groupview) and [`AsyncFunction`](#asyncfunction). 266 267[`AsyncFunction`](#asyncfunction) in the view definition is added to the React ref of the React component representing the native view. 268Such async functions automatically receive an instance of the native view as the first argument and run on the UI thread by default. 269 270#### Arguments 271 272- **viewType** — The class of the native view that will be rendered. Note: On Android, the provided class must inherit from the [`ExpoView`](#expoview), on iOS it's optional. See [`Extending ExpoView`](#extending--expoview). 273- **definition**: `() -> ViewDefinition` — A builder of the view definition. 274 275<CodeBlocksTable> 276 277```swift 278View(UITextView.self) { 279 Prop("text") { ... } 280 281 AsyncFunction("focus") { (view: UITextView) in 282 view.becomeFirstResponder() 283 } 284} 285``` 286 287```kotlin 288View(TextView::class) { 289 Prop("text") { ... } 290 291 AsyncFunction("focus") { view: TextView -> 292 view.requestFocus() 293 } 294} 295``` 296 297</CodeBlocksTable> 298 299> **Info** Support for rendering SwiftUI views is planned. For now, you can use [`UIHostingController`](https://developer.apple.com/documentation/swiftui/uihostingcontroller) and add its content view to your UIKit view. 300 301> **Info** View functions are available as of SDK 49. 302 303</APIBox> 304<APIBox header="Prop"> 305 306Defines a setter for the view prop of given name. 307 308#### Arguments 309 310- **name**: `String` — Name of view prop that you want to define a setter. 311- **setter**: `(view: ViewType, value: ValueType) -> ()` — Closure that is invoked when the view rerenders. 312 313This property can only be used within a [`ViewManager`](#viewmanager) closure. 314 315<CodeBlocksTable> 316 317```swift 318Prop("background") { (view: UIView, color: UIColor) in 319 view.backgroundColor = color 320} 321``` 322 323```kotlin 324Prop("background") { view: View, @ColorInt color: Int -> 325 view.setBackgroundColor(color) 326} 327``` 328 329</CodeBlocksTable> 330 331> **Note** Props of function type (callbacks) are not supported yet. 332 333</APIBox> 334<APIBox header="OnCreate"> 335 336Defines module's lifecycle listener that is called right after module initialization. If you need to set up something when the module gets initialized, use this instead of module's class initializer. 337 338</APIBox> 339<APIBox header="OnDestroy"> 340 341Defines module's lifecycle listener that is called when the module is about to be deallocated. Use it instead of module's class destructor. 342 343</APIBox> 344<APIBox header="OnStartObserving"> 345 346Defines the function that is invoked when the first event listener is added. 347 348</APIBox> 349<APIBox header="OnStopObserving"> 350 351Defines the function that is invoked when all event listeners are removed. 352 353</APIBox> 354<APIBox header="OnAppContextDestroys"> 355 356Defines module's lifecycle listener that is called when the app context owning the module is about to be deallocated. 357 358</APIBox> 359<APIBox header="OnAppEntersForeground" platforms={["ios"]}> 360 361Defines the listener that is called when the app is about to enter the foreground mode. 362 363> **Note** This function is not available on Android — you may want to use [`OnActivityEntersForeground`](#onactivityentersforeground) instead. 364 365</APIBox> 366<APIBox header="OnAppEntersBackground" platforms={["ios"]}> 367 368Defines the listener that is called when the app enters the background mode. 369 370> **Note** This function is not available on Android — you may want to use [`OnActivityEntersBackground`](#onactivityentersbackground) instead. 371 372</APIBox> 373<APIBox header="OnAppBecomesActive" platforms={["ios"]}> 374 375Defines the listener that is called when the app becomes active again (after `OnAppEntersForeground`). 376 377> **Note** This function is not available on Android — you may want to use [`OnActivityEntersForeground`](#onactivityentersforeground) instead. 378 379</APIBox> 380<APIBox header="OnActivityEntersForeground" platforms={["android"]}> 381 382Defines the activity lifecycle listener that is called right after the activity is resumed. 383 384> **Note** This function is not available on iOS — you may want to use [`OnAppEntersForeground`](#onappentersforeground) instead. 385 386</APIBox> 387<APIBox header="OnActivityEntersBackground" platforms={["android"]}> 388 389Defines the activity lifecycle listener that is called right after the activity is paused. 390 391> **Note** This function is not available on iOS — you may want to use [`OnAppEntersBackground`](#onappentersbackground) instead. 392 393</APIBox> 394<APIBox header="OnActivityDestroys" platforms={["android"]}> 395 396Defines the activity lifecycle listener that is called when the activity owning the JavaScript context is about to be destroyed. 397 398> **Note** This function is not available on iOS — you may want to use [`OnAppEntersBackground`](#onappentersbackground) instead. 399 400</APIBox> 401 402<APIBox header="GroupView" platforms={["android"]}> 403 404Enables the view to be used as a view group. Definition components that are accepted as part of the group view definition: [`AddChildView`](#addchildview), [`GetChildCount`](#getchildcount), [`GetChildViewAt`](#getchildviewat), [`RemoveChildView`](#removechildview), [`RemoveChildViewAt`](#removechildviewat). 405 406#### Arguments 407 408- **viewType** — The class of the native view. Note that the provided class must inherit from the Android `ViewGroup`. 409- **definition**: `() -> ViewGroupDefinition` — A builder of the view group definition. 410 411This property can only be used within a [`View`](#view) closure. 412 413```kotlin Kotlin 414GroupView<ViewGroup> { 415 AddChildView { parent, child, index -> ... } 416} 417``` 418 419</APIBox> 420 421<APIBox header="AddChildView" platforms={["android"]}> 422 423Defines action that adds a child view to the view group. 424 425#### Arguments 426 427- **action**: `(parent: ParentType, child: ChildType, index: Int) -> ()` — An action that adds a child view to the view group. 428 429This property can only be used within a [`GroupView`](#groupview) closure. 430 431```kotlin Kotlin 432AddChildView { parent, child: View, index -> 433 parent.addView(child, index) 434} 435``` 436 437</APIBox> 438 439<APIBox header="GetChildCount" platforms={["android"]}> 440 441Defines action the retrieves the number of child views in the view group. 442 443#### Arguments 444 445- **action**: `(parent: ParentType) -> Int` — A function that returns number of child views. 446 447This property can only be used within a [`GroupView`](#groupview) closure. 448 449```kotlin Kotlin 450GetChildCount { parent -> 451 return@GetChildCount parent.childCount 452} 453``` 454 455</APIBox> 456 457<APIBox header="GetChildViewAt" platforms={["android"]}> 458 459Defines action that retrieves a child view at a specific index from the view group. 460 461#### Arguments 462 463- **action**: `(parent: ParentType, index: Int) -> ChildType` — A function that retrieves a child view at a specific index from the view group. 464 465This property can only be used within a [`GroupView`](#groupview) closure. 466 467```kotlin Kotlin 468GetChildViewAt { parent, index -> 469 parent.getChildAt(index) 470} 471``` 472 473</APIBox> 474 475<APIBox header="RemoveChildView" platforms={["android"]}> 476 477Defines action that removes a specific child view from the view group. 478 479#### Arguments 480 481- **action**: `(parent: ParentType, child: ChildType) -> ()` — A function that remove a specific child view from the view group. 482 483This property can only be used within a [`GroupView`](#groupview) closure. 484 485```kotlin Kotlin 486RemoveChildView { parent, child: View -> 487 parent.removeView(child) 488} 489``` 490 491</APIBox> 492 493<APIBox header="RemoveChildViewAt" platforms={["android"]}> 494 495Defines action that removes a child view at a specific index from the view group. 496 497#### Arguments 498 499- **action**: `(parent: ParentType, child: ChildType) -> ()` — A function that removes a child view at a specific index from the view group. 500 501This property can only be used within a [`GroupView`](#groupview) closure. 502 503```kotlin Kotlin 504RemoveChildViewAt { parent, index -> 505 parent.removeViewAt(child) 506} 507``` 508 509</APIBox> 510 511## Argument types 512 513Fundamentally, only primitive and serializable data can be passed back and forth between the runtimes. However, usually native modules need to receive custom data structures — more sophisticated than just the dictionary/map where the values are of unknown (`Any`) type and so each value has to be validated and cast on its own. The Expo Modules API provides protocols to make it more convenient to work with data objects, to provide automatic validation, and finally, to ensure native type-safety on each object member. 514 515<APIBox header="Primitives"> 516 517All functions and view prop setters accept all common primitive types in Swift and Kotlin as the arguments. This includes arrays, dictionaries/maps and optionals of these primitive types. 518 519| Language | Supported primitive types | 520| -------- | ------------------------------------------------------------------------------------------------------------------------------ | 521| Swift | `Bool`, `Int`, `Int8`, `Int16`, `Int32`, `Int64`, `UInt`, `UInt8`, `UInt16`, `UInt32`, `UInt64`, `Float32`, `Double`, `String` | 522| Kotlin | `Boolean`, `Int`, `Long`, `UInt`, `Float`, `Double`, `String`, `Pair` | 523 524</APIBox> 525<APIBox header="Convertibles"> 526 527_Convertibles_ are native types that can be initialized from certain specific kinds of data received from JavaScript. Such types are allowed to be used as an argument type in `Function`'s body. For example, when the `CGPoint` type is used as a function argument type, its instance can be created from an array of two numbers `(x, y)` or a JavaScript object with numeric `x` and `y` properties. 528 529--- 530 531Some common iOS types from `CoreGraphics` and `UIKit` system frameworks are already made convertible. 532 533| Native iOS Type | TypeScript | 534| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 535| `URL` | `string` with a URL. When a scheme is not provided, it's assumed to be a file URL. | 536| `CGFloat` | `number` | 537| `CGPoint` | `{ x: number, y: number }` or `number[]` with _x_ and _y_ coords | 538| `CGSize` | `{ width: number, height: number }` or `number[]` with _width_ and _height_ | 539| `CGVector` | `{ dx: number, dy: number }` or `number[]` with _dx_ and _dy_ vector differentials | 540| `CGRect` | `{ x: number, y: number, width: number, height: number }` or `number[]` with _x_, _y_, _width_ and _height_ values | 541| `CGColor`<br/>`UIColor` | Color hex strings (`#RRGGBB`, `#RRGGBBAA`, `#RGB`, `#RGBA`), named colors following the [CSS3/SVG specification](https://www.w3.org/TR/css-color-3/#svg-color) or `"transparent"` | 542 543--- 544 545Similarly, some common Android types from packages like `java.io`, `java.net`, or `android.graphics` are also made convertible. 546 547| Native Android Type | TypeScript | 548| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 549| `java.net.URL` | `string` with a URL. Note that the scheme has to be provided | 550| `android.net.Uri`<br/>`java.net.URI` | `string` with a URI. Note that the scheme has to be provided | 551| `java.io.File`<br/>`java.nio.file.Path` (is only available on Android API 26) | `string` with a path to the file | 552| `android.graphics.Color` | Color hex strings (`#RRGGBB`, `#RRGGBBAA`, `#RGB`, `#RGBA`), named colors following the [CSS3/SVG specification](https://www.w3.org/TR/css-color-3/#svg-color) or `"transparent"` | 553| `kotlin.Pair<A, B>` | Array with two values, where the first one is of type _A_ and the second is of type _B_ | 554 555</APIBox> 556<APIBox header="Records"> 557 558_Record_ is a convertible type and an equivalent of the dictionary (Swift) or map (Kotlin), but represented as a struct where each field can have its type and provide a default value. 559It is a better way to represent a JavaScript object with the native type safety. 560 561<CodeBlocksTable> 562 563```swift 564struct FileReadOptions: Record { 565 @Field 566 var encoding: String = "utf8" 567 568 @Field 569 var position: Int = 0 570 571 @Field 572 var length: Int? 573} 574 575// Now this record can be used as an argument of the functions or the view prop setters. 576Function("readFile") { (path: String, options: FileReadOptions) -> String in 577 // Read the file using given `options` 578} 579``` 580 581```kotlin 582class FileReadOptions : Record { 583 @Field 584 val encoding: String = "utf8" 585 586 @Field 587 val position: Int = 0 588 589 @Field 590 val length: Int? = null 591} 592 593// Now this record can be used as an argument of the functions or the view prop setters. 594Function("readFile") { path: String, options: FileReadOptions -> 595 // Read the file using given `options` 596} 597``` 598 599</CodeBlocksTable> 600</APIBox> 601<APIBox header="Enums"> 602 603With enums, we can go even further with the above example (with `FileReadOptions` record) and limit supported encodings to `"utf8"` and `"base64"`. To use an enum as an argument or record field, it must represent a primitive value (for example, `String`, `Int`) and conform to `Enumerable`. 604 605<CodeBlocksTable> 606 607```swift 608enum FileEncoding: String, Enumerable { 609 case utf8 610 case base64 611} 612 613struct FileReadOptions: Record { 614 @Field 615 var encoding: FileEncoding = .utf8 616 // ... 617} 618``` 619 620```kotlin 621// Note: the constructor must have an argument called value. 622enum class FileEncoding(val value: String) : Enumerable { 623 utf8("utf8"), 624 base64("base64") 625} 626 627class FileReadOptions : Record { 628 @Field 629 val encoding: FileEncoding = FileEncoding.utf8 630 // ... 631} 632``` 633 634</CodeBlocksTable> 635</APIBox> 636<APIBox header="Eithers"> 637 638There are some use cases where you want to pass various types for a single function argument. This is where Either types might come in handy. 639They act as a container for a value of one of a couple of types. 640 641<CodeBlocksTable> 642 643```swift 644Function("foo") { (bar: Either<String, Int>) in 645 if let bar: String = bar.get() { 646 // `bar` is a String 647 } 648 if let bar: Int = bar.get() { 649 // `bar` is an Int 650 } 651} 652``` 653 654```kotlin 655Function("foo") { bar: Either<String, Int> -> 656 bar.get(String::class).let { 657 // `it` is a String 658 } 659 bar.get(Int::class).let { 660 // `it` is an Int 661 } 662} 663``` 664 665</CodeBlocksTable> 666 667The implementation for three Either types is currently provided out of the box, allowing you to use up to four different subtypes. 668 669- `Either<FirstType, SecondType>` — A container for one of two types. 670- `EitherOfThree<FirstType, SecondType, ThirdType>` — A container for one of three types. 671- `EitherOfFour<FirstType, SecondType, ThirdType, FourthType>` — A container for one of four types. 672 673> **Info** Either types are available as of SDK 47. 674 675</APIBox> 676<APIBox header="JavaScript values"> 677 678It's also possible to use a `JavaScriptValue` type which is a holder for any value that can be represented in JavaScript. 679This type is useful when you want to mutate the given argument or when you want to omit type validations and conversions. 680Note that using JavaScript-specific types is restricted to synchronous functions as all reads and writes in the JavaScript runtime must happen on the JavaScript thread. 681Any access to these values from different threads will result in a crash. 682 683In addition to the raw value, the `JavaScriptObject` type can be used to allow only object types and `JavaScriptFunction<ReturnType>` for callbacks. 684 685<CodeBlocksTable> 686 687```swift 688Function("mutateMe") { (value: JavaScriptValue) in 689 if value.isObject() { 690 let jsObject = value.getObject() 691 jsObject.setProperty("expo", value: "modules") 692 } 693} 694 695// or 696 697Function("mutateMe") { (jsObject: JavaScriptObject) in 698 jsObject.setProperty("expo", value: "modules") 699} 700``` 701 702```kotlin 703Function("mutateMe") { value: JavaScriptValue -> 704 if (value.isObject()) { 705 val jsObject = value.getObject() 706 jsObject.setProperty("expo", "modules") 707 } 708} 709 710// or 711 712Function("mutateMe") { jsObject: JavaScriptObject -> 713 jsObject.setProperty("expo", "modules") 714} 715``` 716 717</CodeBlocksTable> 718 719> **Info** JavaScript types are available as of SDK 49. 720 721</APIBox> 722 723## Native classes 724 725<APIBox header="Module"> 726 727A base class for a native module. 728 729#### Properties 730 731<APIMethod 732 name="appContext" 733 comment="Provides access to the [`AppContext`](#appcontext)." 734 returnTypeName="AppContext" 735 isProperty={true} 736 isReturnTypeReference={true} 737/> 738 739#### Methods 740 741<APIMethod 742 name="sendEvent" 743 comment="Sends an event with a given name and a payload to JavaScript. See [`Sending events`](#sending-events)" 744 returnTypeName="void" 745 parameters={[ 746 { 747 name: 'eventName', 748 comment: 'The name of the JavaScript event', 749 typeName: 'string', 750 }, 751 { 752 name: 'payload', 753 comment: 'The event payload', 754 typeName: 'Android: Map<String, Any?> | Bundle\niOS: [String: Any?]', 755 }, 756 ]} 757/> 758 759</APIBox> 760 761<APIBox header="AppContext"> 762 763The app context is an interface to a single Expo app. 764 765#### Properties 766 767<APIMethod 768 name="constants" 769 comment="Provides access to app's constants from legacy module registry." 770 returnTypeName="Android: ConstantsInterface? iOS: EXConstantsInterface?" 771 isProperty={true} 772/> 773 774<APIMethod 775 name="permissions" 776 comment="Provides access to the permissions manager from legacy module registry." 777 returnTypeName="Android: Permissions? iOS: EXPermissionsInterface?" 778 isProperty={true} 779/> 780 781<APIMethod 782 name="imageLoader" 783 comment="Provides access to the image loader from the legacy module registry." 784 returnTypeName="Android: ImageLoaderInterface? iOS: EXImageLoaderInterface?" 785 isProperty={true} 786/> 787 788<APIMethod 789 name="barcodeScanner" 790 comment="Provides access to the bar code scanner manager from the legacy module registry." 791 returnTypeName="ImageLoaderInterface?" 792 isProperty={true} 793 platforms={['Android']} 794/> 795 796<APIMethod 797 name="camera" 798 comment="Provides access to the camera view manager from the legacy module registry." 799 returnTypeName="CameraViewInterface?" 800 isProperty={true} 801 platforms={['Android']} 802/> 803 804<APIMethod 805 name="font" 806 comment="Provides access to the font manager from the legacy module registry." 807 returnTypeName="FontManagerInterface?" 808 isProperty={true} 809 platforms={['Android']} 810/> 811 812<APIMethod 813 name="sensor" 814 comment="Provides access to the sensor manager from the legacy module registry." 815 returnTypeName="SensorServiceInterface?" 816 isProperty={true} 817 platforms={['Android']} 818/> 819 820<APIMethod 821 name="taskManager" 822 comment="Provides access to the task manager from the legacy module registry." 823 returnTypeName="TaskManagerInterface?" 824 isProperty={true} 825 platforms={['Android']} 826/> 827 828<APIMethod 829 name="activityProvider" 830 comment="Provides access to the activity provider from the legacy module registry." 831 returnTypeName="ActivityProvider?" 832 isProperty={true} 833 platforms={['Android']} 834/> 835 836<APIMethod 837 name="reactContext" 838 comment="Provides access to the react application context." 839 returnTypeName="Context?" 840 isProperty={true} 841 platforms={['Android']} 842/> 843 844<APIMethod 845 name="hasActiveReactInstance" 846 comment="Checks if there is an not-null, alive react native instance." 847 returnTypeName="Boolean" 848 isProperty={true} 849 platforms={['Android']} 850/> 851 852<APIMethod 853 name="utilities" 854 comment="Provides access to the utilities from legacy module registry." 855 returnTypeName="EXUtilitiesInterface?" 856 isProperty={true} 857 platforms={['iOS']} 858/> 859 860</APIBox> 861 862<APIBox header="ExpoView"> 863 864A base class that should be used by all exported views. 865 866On iOS, `ExpoView` extends the `RCTView` which handles some styles (for example, borders) and accessibility. 867 868#### Properties 869 870<APIMethod 871 name="appContext" 872 comment="Provides access to the [`AppContext`](#appcontext)." 873 returnTypeName="AppContext" 874 isProperty={true} 875 isReturnTypeReference={true} 876/> 877 878#### Extending `ExpoView` 879 880To export your view using the [`View`](#view) component, your custom class must inherit from the `ExpoView`. By doing that you will get access to the [`AppContext`](#appcontext) object. It's the only way of communicating with other modules and the JavaScript runtime. Also, you can't change constructor parameters, because provided view will be initialized by `expo-modules-core`. 881 882<CodeBlocksTable> 883 884```swift 885class LinearGradientView: ExpoView {} 886 887public class LinearGradientModule: Module { 888 public func definition() -> ModuleDefinition { 889 View(LinearGradientView.self) { 890 // ... 891 } 892 } 893} 894``` 895 896```kotlin 897class LinearGradientView( 898 context: Context, 899 appContext: AppContext, 900) : ExpoView(context, appContext) 901 902class LinearGradientModule : Module() { 903 override fun definition() = ModuleDefinition { 904 View(LinearGradientView::class) { 905 // ... 906 } 907 } 908} 909``` 910 911</CodeBlocksTable> 912 913</APIBox> 914 915## Guides 916 917<APIBox> 918### Sending events 919 920While JavaScript/TypeScript to Native communication is mostly covered by native functions, you might also want to let the JavaScript/TypeScript code know about certain system events, for example, when the clipboard content changes. 921 922To do this, in the module definition, you need to provide the event names that the module can send using the [Events](#events) definition component. After that, you can use the `sendEvent(eventName, payload)` function on the module instance to send the actual event with some payload. For example, a minimal clipboard implementation that sends native events may look like this: 923 924<CodeBlocksTable> 925 926```swift 927let CLIPBOARD_CHANGED_EVENT_NAME = "onClipboardChanged" 928 929public class ClipboardModule: Module { 930 public func definition() -> ModuleDefinition { 931 Events(CLIPBOARD_CHANGED_EVENT_NAME) 932 933 OnStartObserving { 934 NotificationCenter.default.addObserver( 935 self, 936 selector: #selector(self.clipboardChangedListener), 937 name: UIPasteboard.changedNotification, 938 object: nil 939 ) 940 } 941 942 OnStopObserving { 943 NotificationCenter.default.removeObserver( 944 self, 945 name: UIPasteboard.changedNotification, 946 object: nil 947 ) 948 } 949 } 950 951 @objc 952 private func clipboardChangedListener() { 953 sendEvent(CLIPBOARD_CHANGED_EVENT_NAME, [ 954 "contentTypes": availableContentTypes() 955 ]) 956 } 957} 958``` 959 960```kotlin 961const val CLIPBOARD_CHANGED_EVENT_NAME = "onClipboardChanged" 962 963class ClipboardModule : Module() { 964 override fun definition() = ModuleDefinition { 965 Events(CLIPBOARD_CHANGED_EVENT_NAME) 966 967 OnStartObserving { 968 clipboardManager?.addPrimaryClipChangedListener(listener) 969 } 970 971 OnStopObserving { 972 clipboardManager?.removePrimaryClipChangedListener(listener) 973 } 974 } 975 976 private val clipboardManager: ClipboardManager? 977 get() = appContext.reactContext?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager 978 979 private val listener = ClipboardManager.OnPrimaryClipChangedListener { 980 clipboardManager?.primaryClipDescription?.let { clip -> 981 [email protected]( 982 CLIPBOARD_CHANGED_EVENT_NAME, 983 bundleOf( 984 "contentTypes" to availableContentTypes(clip) 985 ) 986 ) 987 } 988 } 989} 990``` 991 992</CodeBlocksTable> 993 994To subscribe to these events in JavaScript/TypeScript, you need to wrap the native module with `EventEmitter` class as shown: 995 996```ts TypeScript 997import { requireNativeModule, EventEmitter, Subscription } from 'expo-modules-core'; 998 999const ClipboardModule = requireNativeModule('Clipboard'); 1000const emitter = new EventEmitter(ClipboardModule); 1001 1002export function addClipboardListener(listener: (event) => void): Subscription { 1003 return emitter.addListener('onClipboardChanged', listener); 1004} 1005``` 1006 1007</APIBox> 1008 1009<APIBox> 1010### View callbacks 1011 1012Some events are connected to a certain view. For example, the touch event should be sent only to the underlying JavaScript view which was pressed. In that case, you can't use `sendEvent` described in [`Sending events`](#sending-events). The `expo-modules-core` introduces a view callbacks mechanism to handle view-bound events. 1013 1014To use it, in the view definition, you need to provide the event names that the view can send using the [Events](#events) definition component. After that, you need to declare a property of type `EventDispatcher` in your view class. The name of the declared property has to be the same as the name exported in the `Events` component. Later, you can call it as a function and pass a payload of type `[String: Any?]` on iOS and `Map<String, Any?>` on Android. 1015 1016> **Note**: On Android, it's possible to specify the payload type. In case of types that don't convert into objects, the payload will be encapsulated and stored under the `payload` key: `{payload: <provided value>}`. 1017 1018<CodeBlocksTable> 1019 1020```swift 1021class CameraViewModule: Module { 1022 public func definition() -> ModuleDefinition { 1023 View(CameraView.self) { 1024 Events( 1025 "onCameraReady" 1026 ) 1027 1028 // ... 1029 } 1030 } 1031} 1032 1033class CameraView: ExpoView { 1034 let onCameraReady = EventDispatcher() 1035 1036 func callOnCameraReady() { 1037 onCameraReady([ 1038 "message": "Camera was mounted" 1039 ]); 1040 } 1041} 1042``` 1043 1044```kotlin 1045class CameraViewModule : Module() { 1046 override fun definition() = ModuleDefinition { 1047 View(ExpoCameraView::class) { 1048 Events( 1049 "onCameraReady" 1050 ) 1051 1052 // ... 1053 } 1054 } 1055} 1056 1057class CameraView( 1058 context: Context, 1059 appContext: AppContext 1060) : ExpoView(context, appContext) { 1061 val onCameraReady by EventDispatcher() 1062 1063 fun callOnCameraReady() { 1064 onCameraReady(mapOf( 1065 "message" to "Camera was mounted" 1066 )); 1067 } 1068} 1069``` 1070 1071</CodeBlocksTable> 1072 1073To subscribe to these events in JavaScript/TypeScript, you need to pass a function to the native view as shown: 1074 1075```ts TypeScript 1076import { requireNativeViewManager } from 'expo-modules-core'; 1077 1078const CameraView = requireNativeViewManager('CameraView'); 1079 1080export default function MainView() { 1081 const onCameraReady = event => { 1082 console.log(event.nativeEvent); 1083 }; 1084 1085 return <CameraView onCameraReady={onCameraReady} />; 1086} 1087``` 1088 1089Provided payload is available under the `nativeEvent` key. 1090 1091</APIBox> 1092 1093## Examples 1094 1095<CodeBlocksTable> 1096 1097```swift 1098public class MyModule: Module { 1099 public func definition() -> ModuleDefinition { 1100 Name("MyFirstExpoModule") 1101 1102 Function("hello") { (name: String) in 1103 return "Hello \(name)!" 1104 } 1105 } 1106} 1107``` 1108 1109```kotlin 1110class MyModule : Module() { 1111 override fun definition() = ModuleDefinition { 1112 Name("MyFirstExpoModule") 1113 1114 Function("hello") { name: String -> 1115 return "Hello $name!" 1116 } 1117 } 1118} 1119``` 1120 1121</CodeBlocksTable> 1122 1123For more examples from real modules, you can refer to Expo modules that already use this API on GitHub: 1124 1125- `expo-battery` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-battery/ios)) 1126- `expo-cellular` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-cellular/ios), [Kotlin](https://github.com/expo/expo/tree/main/packages/expo-cellular/android/src/main/java/expo/modules/cellular)) 1127- `expo-clipboard` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-clipboard/ios), [Kotlin](https://github.com/expo/expo/tree/main/packages/expo-clipboard/android/src/main/java/expo/modules/clipboard)) 1128- `expo-crypto` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-crypto/ios), [Kotlin](https://github.com/expo/expo/tree/main/packages/expo-crypto/android/src/main/java/expo/modules/crypto)) 1129- `expo-device` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-device/ios)) 1130- `expo-haptics` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-haptics/ios)) 1131- `expo-image-manipulator` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-image-manipulator/ios)) 1132- `expo-image-picker` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-image-picker/ios), [Kotlin](https://github.com/expo/expo/tree/main/packages/expo-image-picker/android/src/main/java/expo/modules/imagepicker)) 1133- `expo-linear-gradient` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-linear-gradient/ios), [Kotlin](https://github.com/expo/expo/tree/main/packages/expo-linear-gradient/android/src/main/java/expo/modules/lineargradient)) 1134- `expo-localization` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-localization/ios), [Kotlin](https://github.com/expo/expo/tree/main/packages/expo-localization/android/src/main/java/expo/modules/localization)) 1135- `expo-store-review` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-store-review/ios)) 1136- `expo-system-ui` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-system-ui/ios/ExpoSystemUI)) 1137- `expo-video-thumbnails` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-video-thumbnails/ios)) 1138- `expo-web-browser` ([Swift](https://github.com/expo/expo/blob/main/packages/expo-web-browser/ios), [Kotlin](https://github.com/expo/expo/blob/main/packages/expo-web-browser/android/src/main/java/expo/modules/webbrowser)) 1139