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