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