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