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="ViewManager"> 263 264> **warning** **Deprecated**: To better integrate with [React Native's new architecture (Fabric)](https://reactnative.dev/architecture/fabric-renderer) and its recycling mechanism, as of SDK 47 the `ViewManager` component is deprecated in favor of [`View`](#view) with a view class passed as the first argument. This component will be removed in SDK 48. 265 266Enables the module to be used as a view manager. The view manager definition is built from the definition components used in the closure passed to `ViewManager`. Definition components that are accepted as part of the view manager definition: [`View`](#view), [`Prop`](#prop). 267 268<CodeBlocksTable> 269 270```swift 271ViewManager { 272 View { 273 MyNativeView() 274 } 275 276 Prop("isHidden") { (view: UIView, hidden: Bool) in 277 view.isHidden = hidden 278 } 279} 280``` 281 282```kotlin 283ViewManager { 284 View { context -> 285 MyNativeView(context) 286 } 287 288 Prop("isHidden") { view: View, hidden: Bool -> 289 view.isVisible = !hidden 290 } 291} 292``` 293 294</CodeBlocksTable> 295</APIBox> 296<APIBox header="View"> 297 298Enables 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). 299 300#### Arguments 301 302- **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). 303- **definition**: `() -> ViewDefinition` — A builder of the view definition. 304 305<CodeBlocksTable> 306 307```swift 308View(UITextView.self) { 309 Prop("text") { ... } 310} 311``` 312 313```kotlin 314View(TextView::class) { 315 Prop("text") { ... } 316} 317``` 318 319</CodeBlocksTable> 320 321> 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. 322 323</APIBox> 324<APIBox header="Prop"> 325 326Defines a setter for the view prop of given name. 327 328#### Arguments 329 330- **name**: `String` — Name of view prop that you want to define a setter. 331- **setter**: `(view: ViewType, value: ValueType) -> ()` — Closure that is invoked when the view rerenders. 332 333This property can only be used within a [`ViewManager`](#viewmanager) closure. 334 335<CodeBlocksTable> 336 337```swift 338Prop("background") { (view: UIView, color: UIColor) in 339 view.backgroundColor = color 340} 341``` 342 343```kotlin 344Prop("background") { view: View, @ColorInt color: Int -> 345 view.setBackgroundColor(color) 346} 347``` 348 349</CodeBlocksTable> 350 351> **Note** Props of function type (callbacks) are not supported yet. 352 353</APIBox> 354<APIBox header="OnCreate"> 355 356Defines 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. 357 358</APIBox> 359<APIBox header="OnDestroy"> 360 361Defines module's lifecycle listener that is called when the module is about to be deallocated. Use it instead of module's class destructor. 362 363</APIBox> 364<APIBox header="OnStartObserving"> 365 366Defines the function that is invoked when the first event listener is added. 367 368</APIBox> 369<APIBox header="OnStopObserving"> 370 371Defines the function that is invoked when all event listeners are removed. 372 373</APIBox> 374<APIBox header="OnAppContextDestroys"> 375 376Defines module's lifecycle listener that is called when the app context owning the module is about to be deallocated. 377 378</APIBox> 379<APIBox header="OnAppEntersForeground" platforms={["ios"]}> 380 381Defines the listener that is called when the app is about to enter the foreground mode. 382 383> **Note** This function is not available on Android — you may want to use [`OnActivityEntersForeground`](#onactivityentersforeground) instead. 384 385</APIBox> 386<APIBox header="OnAppEntersBackground" platforms={["ios"]}> 387 388Defines the listener that is called when the app enters the background mode. 389 390> **Note** This function is not available on Android — you may want to use [`OnActivityEntersBackground`](#onactivityentersbackground) instead. 391 392</APIBox> 393<APIBox header="OnAppBecomesActive" platforms={["ios"]}> 394 395Defines the listener that is called when the app becomes active again (after `OnAppEntersForeground`). 396 397> **Note** This function is not available on Android — you may want to use [`OnActivityEntersForeground`](#onactivityentersforeground) instead. 398 399</APIBox> 400<APIBox header="OnActivityEntersForeground" platforms={["android"]}> 401 402Defines the activity lifecycle listener that is called right after the activity is resumed. 403 404> **Note** This function is not available on iOS — you may want to use [`OnAppEntersForeground`](#onappentersforeground) instead. 405 406</APIBox> 407<APIBox header="OnActivityEntersBackground" platforms={["android"]}> 408 409Defines the activity lifecycle listener that is called right after the activity is paused. 410 411> **Note** This function is not available on iOS — you may want to use [`OnAppEntersBackground`](#onappentersbackground) instead. 412 413</APIBox> 414<APIBox header="OnActivityDestroys" platforms={["android"]}> 415 416Defines the activity lifecycle listener that is called when the activity owning the JavaScript context is about to be destroyed. 417 418> **Note** This function is not available on iOS — you may want to use [`OnAppEntersBackground`](#onappentersbackground) instead. 419 420</APIBox> 421 422<APIBox header="GroupView" platforms={["android"]}> 423 424Enables 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). 425 426#### Arguments 427 428- **viewType** — The class of the native view. Note that the provided class must inherit from the Android `ViewGroup`. 429- **definition**: `() -> ViewGroupDefinition` — A builder of the view group definition. 430 431This property can only be used within a [`View`](#view) closure. 432 433```kotlin Kotlin 434GroupView<ViewGroup> { 435 AddChildView { parent, child, index -> ... } 436} 437``` 438 439</APIBox> 440 441<APIBox header="AddChildView" platforms={["android"]}> 442 443Defines action that adds a child view to the view group. 444 445#### Arguments 446 447- **action**: `(parent: ParentType, child: ChildType, index: Int) -> ()` — An action that adds a child view to the view group. 448 449This property can only be used within a [`GroupView`](#groupview) closure. 450 451```kotlin Kotlin 452AddChildView { parent, child: View, index -> 453 parent.addView(child, index) 454} 455``` 456 457</APIBox> 458 459<APIBox header="GetChildCount" platforms={["android"]}> 460 461Defines action the retrieves the number of child views in the view group. 462 463#### Arguments 464 465- **action**: `(parent: ParentType) -> Int` — A function that returns number of child views. 466 467This property can only be used within a [`GroupView`](#groupview) closure. 468 469```kotlin Kotlin 470GetChildCount { parent -> 471 return@GetChildCount parent.childCount 472} 473``` 474 475</APIBox> 476 477<APIBox header="GetChildViewAt" platforms={["android"]}> 478 479Defines action that retrieves a child view at a specific index from the view group. 480 481#### Arguments 482 483- **action**: `(parent: ParentType, index: Int) -> ChildType` — A function that retrieves a child view at a specific index from the view group. 484 485This property can only be used within a [`GroupView`](#groupview) closure. 486 487```kotlin Kotlin 488GetChildViewAt { parent, index -> 489 parent.getChildAt(index) 490} 491``` 492 493</APIBox> 494 495<APIBox header="RemoveChildView" platforms={["android"]}> 496 497Defines action that removes a specific child view from the view group. 498 499#### Arguments 500 501- **action**: `(parent: ParentType, child: ChildType) -> ()` — A function that remove a specific child view from the view group. 502 503This property can only be used within a [`GroupView`](#groupview) closure. 504 505```kotlin Kotlin 506RemoveChildView { parent, child: View -> 507 parent.removeView(child) 508} 509``` 510 511</APIBox> 512 513<APIBox header="RemoveChildViewAt" platforms={["android"]}> 514 515Defines action that removes a child view at a specific index from the view group. 516 517#### Arguments 518 519- **action**: `(parent: ParentType, child: ChildType) -> ()` — A function that removes a child view at a specific index from the view group. 520 521This property can only be used within a [`GroupView`](#groupview) closure. 522 523```kotlin Kotlin 524RemoveChildViewAt { parent, index -> 525 parent.removeViewAt(child) 526} 527``` 528 529</APIBox> 530 531## Argument Types 532 533Fundamentally, 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. 534 535<APIBox header="Primitives"> 536 537All 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. 538 539| Language | Supported primitive types | 540| -------- | ------------------------------------------------------------------------------------------------------------------------------ | 541| Swift | `Bool`, `Int`, `Int8`, `Int16`, `Int32`, `Int64`, `UInt`, `UInt8`, `UInt16`, `UInt32`, `UInt64`, `Float32`, `Double`, `String` | 542| Kotlin | `Boolean`, `Int`, `Long`, `UInt`, `Float`, `Double`, `String`, `Pair` | 543 544</APIBox> 545<APIBox header="Convertibles"> 546 547_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. 548 549--- 550 551Some common iOS types from `CoreGraphics` and `UIKit` system frameworks are already made convertible. 552 553| Native iOS Type | TypeScript | 554| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 555| `URL` | `string` with a URL. When scheme is not provided, it's assumed to be a file URL. | 556| `CGFloat` | `number` | 557| `CGPoint` | `{ x: number, y: number }` or `number[]` with _x_ and _y_ coords | 558| `CGSize` | `{ width: number, height: number }` or `number[]` with _width_ and _height_ | 559| `CGVector` | `{ dx: number, dy: number }` or `number[]` with _dx_ and _dy_ vector differentials | 560| `CGRect` | `{ x: number, y: number, width: number, height: number }` or `number[]` with _x_, _y_, _width_ and _height_ values | 561| `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"` | 562 563--- 564 565Similarly, some common Android types from packages like `java.io`, `java.net`, or `android.graphics` are also made convertible. 566 567| Native Android Type | TypeScript | 568| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 569| `java.net.URL` | `string` with a URL. Note that the scheme has to be provided | 570| `android.net.Uri`<br/>`java.net.URI` | `string` with a URI. Note that the scheme has to be provided | 571| `java.io.File`<br/>`java.nio.file.Path` (is only available on Android API 26) | `string` with a path to the file | 572| `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"` | 573| `kotlin.Pair<A, B>` | Array with two values, where the first one is of type _A_ and the second is of type _B_ | 574 575</APIBox> 576<APIBox header="Records"> 577 578_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. 579It is a better way to represent a JavaScript object with the native type-safety. 580 581<CodeBlocksTable> 582 583```swift 584struct FileReadOptions: Record { 585 @Field 586 var encoding: String = "utf8" 587 588 @Field 589 var position: Int = 0 590 591 @Field 592 var length: Int? 593} 594 595// Now this record can be used as an argument of the functions or the view prop setters. 596Function("readFile") { (path: String, options: FileReadOptions) -> String in 597 // Read the file using given `options` 598} 599``` 600 601```kotlin 602class FileReadOptions : Record { 603 @Field 604 val encoding: String = "utf8" 605 606 @Field 607 val position: Int = 0 608 609 @Field 610 val length: Int? = null 611} 612 613// Now this record can be used as an argument of the functions or the view prop setters. 614Function("readFile") { path: String, options: FileReadOptions -> 615 // Read the file using given `options` 616} 617``` 618 619</CodeBlocksTable> 620</APIBox> 621<APIBox header="Enums"> 622 623With 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`. 624 625<CodeBlocksTable> 626 627```swift 628enum FileEncoding: String, Enumerable { 629 case utf8 630 case base64 631} 632 633struct FileReadOptions: Record { 634 @Field 635 var encoding: FileEncoding = .utf8 636 // ... 637} 638``` 639 640```kotlin 641// Note: the constructor must have an argument called value. 642enum class FileEncoding(val value: String) : Enumerable { 643 utf8("utf8"), 644 base64("base64") 645} 646 647class FileReadOptions : Record { 648 @Field 649 val encoding: FileEncoding = FileEncoding.utf8 650 // ... 651} 652``` 653 654</CodeBlocksTable> 655</APIBox> 656<APIBox header="Eithers"> 657 658There 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. 659They act as a container for a value of one of a couple of types. 660 661<CodeBlocksTable> 662 663```swift 664Function("foo") { (bar: Either<String, Int>) in 665 if let bar: String = bar.get() { 666 // `bar` is a String 667 } 668 if let bar: Int = bar.get() { 669 // `bar` is an Int 670 } 671} 672``` 673 674```kotlin 675Function("foo") { bar: Either<String, Int> -> 676 bar.get(String::class).let { 677 // `it` is a String 678 } 679 bar.get(Int::class).let { 680 // `it` is an Int 681 } 682} 683``` 684 685</CodeBlocksTable> 686 687The implementation for three Either types is currently provided out of the box, allowing you to use up to four different subtypes. 688 689- `Either<FirstType, SecondType>` — A container for one of two types. 690- `EitherOfThree<FirstType, SecondType, ThirdType>` — A container for one of three types. 691- `EitherOfFour<FirstType, SecondType, ThirdType, FourthType>` — A container for one of four types. 692 693> Either types are available as of SDK 47. 694 695</APIBox> 696 697## Native Classes 698 699<APIBox header="Module"> 700 701A base class for a native module. 702 703#### Properties 704 705<APIMethod 706 name="appContext" 707 comment="Provides access to the [`AppContext`](#appcontext)." 708 returnTypeName="AppContext" 709 isProperty={true} 710 isReturnTypeReference={true} 711/> 712 713#### Methods 714 715<APIMethod 716 name="sendEvent" 717 comment="Sends an event with a given name and a payload to JavaScript. See [`Sending events`](#sending-events)" 718 returnTypeName="void" 719 parameters={[ 720 { 721 name: 'eventName', 722 comment: 'The name of the JavaScript event', 723 typeName: 'string', 724 }, 725 { 726 name: 'payload', 727 comment: 'The event payload', 728 typeName: 'Android: Map<String, Any?> | Bundle\niOS: [String: Any?]', 729 }, 730 ]} 731/> 732 733</APIBox> 734 735<APIBox header="AppContext"> 736 737The app context is an interface to a single Expo app. 738 739#### Properties 740 741<APIMethod 742 name="constants" 743 comment="Provides access to app's constants from legacy module registry." 744 returnTypeName="Android: ConstantsInterface? iOS: EXConstantsInterface?" 745 isProperty={true} 746/> 747 748<APIMethod 749 name="permissions" 750 comment="Provides access to the permissions manager from legacy module registry." 751 returnTypeName="Android: Permissions? iOS: EXPermissionsInterface?" 752 isProperty={true} 753/> 754 755<APIMethod 756 name="imageLoader" 757 comment="Provides access to the image loader from the legacy module registry." 758 returnTypeName="Android: ImageLoaderInterface? iOS: EXImageLoaderInterface?" 759 isProperty={true} 760/> 761 762<APIMethod 763 name="barcodeScanner" 764 comment="Provides access to the bar code scanner manager from the legacy module registry." 765 returnTypeName="ImageLoaderInterface?" 766 isProperty={true} 767 platforms={['Android']} 768/> 769 770<APIMethod 771 name="camera" 772 comment="Provides access to the camera view manager from the legacy module registry." 773 returnTypeName="CameraViewInterface?" 774 isProperty={true} 775 platforms={['Android']} 776/> 777 778<APIMethod 779 name="font" 780 comment="Provides access to the font manager from the legacy module registry." 781 returnTypeName="FontManagerInterface?" 782 isProperty={true} 783 platforms={['Android']} 784/> 785 786<APIMethod 787 name="sensor" 788 comment="Provides access to the sensor manager from the legacy module registry." 789 returnTypeName="SensorServiceInterface?" 790 isProperty={true} 791 platforms={['Android']} 792/> 793 794<APIMethod 795 name="taskManager" 796 comment="Provides access to the task manager from the legacy module registry." 797 returnTypeName="TaskManagerInterface?" 798 isProperty={true} 799 platforms={['Android']} 800/> 801 802<APIMethod 803 name="activityProvider" 804 comment="Provides access to the activity provider from the legacy module registry." 805 returnTypeName="ActivityProvider?" 806 isProperty={true} 807 platforms={['Android']} 808/> 809 810<APIMethod 811 name="reactContext" 812 comment="Provides access to the react application context." 813 returnTypeName="Context?" 814 isProperty={true} 815 platforms={['Android']} 816/> 817 818<APIMethod 819 name="hasActiveReactInstance" 820 comment="Checks if there is an not-null, alive react native instance." 821 returnTypeName="Boolean" 822 isProperty={true} 823 platforms={['Android']} 824/> 825 826<APIMethod 827 name="utilities" 828 comment="Provides access to the utilities from legacy module registry." 829 returnTypeName="EXUtilitiesInterface?" 830 isProperty={true} 831 platforms={['iOS']} 832/> 833 834</APIBox> 835 836<APIBox header="ExpoView"> 837 838A base class that should be used by all exported views. 839 840On iOS, `ExpoView` extends the `RCTView` which handles some styles (e.g. borders) and accessibility. 841 842#### Properties 843 844<APIMethod 845 name="appContext" 846 comment="Provides access to the [`AppContext`](#appcontext)." 847 returnTypeName="AppContext" 848 isProperty={true} 849 isReturnTypeReference={true} 850/> 851 852#### Extending `ExpoView` 853 854To 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`. 855 856<CodeBlocksTable> 857 858```swift 859class LinearGradientView: ExpoView {} 860 861public class LinearGradientModule: Module { 862 public func definition() -> ModuleDefinition { 863 View(LinearGradientView.self) { 864 // ... 865 } 866 } 867} 868``` 869 870```kotlin 871class LinearGradientView( 872 context: Context, 873 appContext: AppContext, 874) : ExpoView(context, appContext) 875 876class LinearGradientModule : Module() { 877 override fun definition() = ModuleDefinition { 878 View(LinearGradientView::class) { 879 // ... 880 } 881 } 882} 883``` 884 885</CodeBlocksTable> 886 887</APIBox> 888 889## Guides 890 891<APIBox> 892### Sending events 893 894While 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. 895 896To 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: 897 898<CodeBlocksTable> 899 900```swift 901let CLIPBOARD_CHANGED_EVENT_NAME = "onClipboardChanged" 902 903public class ClipboardModule: Module { 904 public func definition() -> ModuleDefinition { 905 Events(CLIPBOARD_CHANGED_EVENT_NAME) 906 907 OnStartObserving { 908 NotificationCenter.default.addObserver( 909 self, 910 selector: #selector(self.clipboardChangedListener), 911 name: UIPasteboard.changedNotification, 912 object: nil 913 ) 914 } 915 916 OnStopObserving { 917 NotificationCenter.default.removeObserver( 918 self, 919 name: UIPasteboard.changedNotification, 920 object: nil 921 ) 922 } 923 } 924 925 @objc 926 private func clipboardChangedListener() { 927 sendEvent(CLIPBOARD_CHANGED_EVENT_NAME, [ 928 "contentTypes": availableContentTypes() 929 ]) 930 } 931} 932``` 933 934```kotlin 935const val CLIPBOARD_CHANGED_EVENT_NAME = "onClipboardChanged" 936 937class ClipboardModule : Module() { 938 override fun definition() = ModuleDefinition { 939 Events(CLIPBOARD_CHANGED_EVENT_NAME) 940 941 OnStartObserving { 942 clipboardManager?.addPrimaryClipChangedListener(listener) 943 } 944 945 OnStopObserving { 946 clipboardManager?.removePrimaryClipChangedListener(listener) 947 } 948 } 949 950 private val clipboardManager: ClipboardManager? 951 get() = appContext.reactContext?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager 952 953 private val listener = ClipboardManager.OnPrimaryClipChangedListener { 954 clipboardManager?.primaryClipDescription?.let { clip -> 955 [email protected]( 956 CLIPBOARD_CHANGED_EVENT_NAME, 957 bundleOf( 958 "contentTypes" to availableContentTypes(clip) 959 ) 960 ) 961 } 962 } 963} 964``` 965 966</CodeBlocksTable> 967 968To subscribe to these events in JavaScript/TypeScript, you need to wrap the native module with `EventEmitter` class as shown: 969 970```ts TypeScript 971import { requireNativeModule, EventEmitter, Subscription } from 'expo-modules-core'; 972 973const ClipboardModule = requireNativeModule('Clipboard'); 974const emitter = new EventEmitter(ClipboardModule); 975 976export function addClipboardListener(listener: (event) => void): Subscription { 977 return emitter.addListener('onClipboardChanged', listener); 978} 979``` 980 981</APIBox> 982 983<APIBox> 984### View callbacks 985 986Some 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. 987 988To 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. 989 990> **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>}`. 991 992<CodeBlocksTable> 993 994```swift 995class CameraViewModule: Module { 996 public func definition() -> ModuleDefinition { 997 View(CameraView.self) { 998 Events( 999 "onCameraReady" 1000 ) 1001 1002 // ... 1003 } 1004 } 1005} 1006 1007class CameraView: ExpoView { 1008 let onCameraReady = EventDispatcher() 1009 1010 func callOnCameraReady() { 1011 onCameraReady([ 1012 "message": "Camera was mounted" 1013 ]); 1014 } 1015} 1016``` 1017 1018```kotlin 1019class CameraViewModule : Module() { 1020 override fun definition() = ModuleDefinition { 1021 View(ExpoCameraView::class) { 1022 Events( 1023 "onCameraReady" 1024 ) 1025 1026 // ... 1027 } 1028 } 1029} 1030 1031class CameraView( 1032 context: Context, 1033 appContext: AppContext 1034) : ExpoView(context, appContext) { 1035 val onCameraReady by EventDispatcher() 1036 1037 fun callOnCameraReady() { 1038 onCameraReady(mapOf( 1039 "message" to "Camera was mounted" 1040 )); 1041 } 1042} 1043``` 1044 1045</CodeBlocksTable> 1046 1047To subscribe to these events in JavaScript/TypeScript, you need to pass a function to the native view as shown: 1048 1049```ts TypeScript 1050import { requireNativeViewManager } from 'expo-modules-core'; 1051 1052const CameraView = requireNativeViewManager('CameraView'); 1053 1054export default function MainView() { 1055 const onCameraReady = event => { 1056 console.log(event.nativeEvent); 1057 }; 1058 1059 return <CameraView onCameraReady={onCameraReady} />; 1060} 1061``` 1062 1063Provided payload is available under the `nativeEvent` key. 1064 1065</APIBox> 1066 1067## Examples 1068 1069<CodeBlocksTable> 1070 1071```swift 1072public class MyModule: Module { 1073 public func definition() -> ModuleDefinition { 1074 Name("MyFirstExpoModule") 1075 1076 Function("hello") { (name: String) in 1077 return "Hello \(name)!" 1078 } 1079 } 1080} 1081``` 1082 1083```kotlin 1084class MyModule : Module() { 1085 override fun definition() = ModuleDefinition { 1086 Name("MyFirstExpoModule") 1087 1088 Function("hello") { name: String -> 1089 return "Hello $name!" 1090 } 1091 } 1092} 1093``` 1094 1095</CodeBlocksTable> 1096 1097For more examples from real modules, you can refer to Expo modules that already use this API on GitHub: 1098 1099- `expo-battery` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-battery/ios)) 1100- `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)) 1101- `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)) 1102- `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)) 1103- `expo-device` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-device/ios)) 1104- `expo-haptics` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-haptics/ios)) 1105- `expo-image-manipulator` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-image-manipulator/ios)) 1106- `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)) 1107- `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)) 1108- `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)) 1109- `expo-store-review` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-store-review/ios)) 1110- `expo-system-ui` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-system-ui/ios/ExpoSystemUI)) 1111- `expo-video-thumbnails` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-video-thumbnails/ios)) 1112- `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)) 1113