xref: /expo/docs/pages/modules/module-api.mdx (revision 5d715522)
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<hr />
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
375Some common iOS types from `CoreGraphics` and `UIKit` system frameworks are already made convertible.
376
377| Native iOS Type         | TypeScript                                                                                                                                                                        |
378| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
379| `URL`                   | `string` with a URL. When scheme is not provided, it's assumed to be a file URL.                                                                                                  |
380| `CGFloat`               | `number`                                                                                                                                                                          |
381| `CGPoint`               | `{ x: number, y: number }` or `number[]` with _x_ and _y_ coords                                                                                                                  |
382| `CGSize`                | `{ width: number, height: number }` or `number[]` with _width_ and _height_                                                                                                       |
383| `CGVector`              | `{ dx: number, dy: number }` or `number[]` with _dx_ and _dy_ vector differentials                                                                                                |
384| `CGRect`                | `{ x: number, y: number, width: number, height: number }` or `number[]` with _x_, _y_, _width_ and _height_ values                                                                |
385| `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"` |
386
387Similarly, some common Android types from packages like `java.io`, `java.net`, or `android.graphics` are also made convertible.
388
389| Native Android Type                                                           | TypeScript                                                                                                                                                                        |
390| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
391| `java.net.URL`                                                                | `string` with a URL. Note that the scheme has to be provided                                                                                                                      |
392| `android.net.Uri`<br/>`java.net.URI`                                          | `string` with a URI. Note that the scheme has to be provided                                                                                                                      |
393| `java.io.File`<br/>`java.nio.file.Path` (is only available on Android API 26) | `string` with a path to the file                                                                                                                                                  |
394| `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"` |
395| `kotlin.Pair<A, B>`                                                           | Array with two values, where the first one is of type _A_ and the second is of type _B_                                                                                           |
396
397</APIBox>
398<APIBox header="Records">
399
400_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.
401It is a better way to represent a JavaScript object with the native type-safety.
402
403<CodeBlocksTable>
404
405```swift
406struct FileReadOptions: Record {
407  @Field
408  var encoding: String = "utf8"
409
410  @Field
411  var position: Int = 0
412
413  @Field
414  var length: Int?
415}
416
417// Now this record can be used as an argument of the functions or the view prop setters.
418Function("readFile") { (path: String, options: FileReadOptions) -> String in
419  // Read the file using given `options`
420}
421```
422
423```kotlin
424class FileReadOptions : Record {
425  @Field
426  val encoding: String = "utf8"
427
428  @Field
429  val position: Int = 0
430
431  @Field
432  val length: Int?
433}
434
435// Now this record can be used as an argument of the functions or the view prop setters.
436Function("readFile") { path: String, options: FileReadOptions ->
437  // Read the file using given `options`
438}
439```
440
441</CodeBlocksTable>
442</APIBox>
443<APIBox header="Enums">
444
445With 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`.
446
447<CodeBlocksTable>
448
449```swift
450enum FileEncoding: String, Enumerable {
451  case utf8
452  case base64
453}
454
455struct FileReadOptions: Record {
456  @Field
457  var encoding: FileEncoding = .utf8
458  // ...
459}
460```
461
462```kotlin
463// Note: the constructor must have an argument called value.
464enum class FileEncoding(val value: String) : Enumerable {
465  utf8("utf8"),
466  base64("base64")
467}
468
469class FileReadOptions : Record {
470  @Field
471  val encoding: FileEncoding = FileEncoding.utf8
472  // ...
473}
474```
475
476</CodeBlocksTable>
477</APIBox>
478<APIBox header="Eithers">
479
480There 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.
481They act as a container for a value of one of a couple of types.
482
483<CodeBlocksTable>
484
485```swift
486Function("foo") { (bar: Either<String, Int>) in
487  if let bar: String = bar.get() {
488    // `bar` is a String
489  }
490  if let bar: Int = bar.get() {
491    // `bar` is an Int
492  }
493}
494```
495
496```kotlin
497Function("foo") { bar: Either<String, Int> ->
498  bar.get(String::class).let {
499    // `it` is a String
500  }
501  bar.get(Int::class).let {
502    // `it` is an Int
503  }
504}
505```
506
507</CodeBlocksTable>
508
509The implementation for three Either types is currently provided out of the box, allowing you to use up to four different subtypes.
510
511- `Either<FirstType, SecondType>` — A container for one of two types.
512- `EitherOfThree<FirstType, SecondType, ThirdType>` — A container for one of three types.
513- `EitherOfFour<FirstType, SecondType, ThirdType, FourthType>` — A container for one of four types.
514
515> Either types are available as of SDK 47.
516
517</APIBox>
518
519## Native Classes
520
521<APIBox header="Module">
522
523A base class for a native module.
524
525#### Properties
526
527<APIMethod
528  name="appContext"
529  comment="Provides access to the [`AppContext`](#appcontext)."
530  returnTypeName="AppContext"
531  isProperty={true}
532  isReturnTypeReference={true}
533/>
534
535#### Methods
536
537<APIMethod
538  name="sendEvent"
539  comment="Sends an event with a given name and a payload to JavaScript. See [`Sending events`](#sending-events)"
540  returnTypeName="void"
541  parameters={[
542    {
543      name: 'eventName',
544      comment: 'The name of the JavaScript event',
545      typeName: 'string',
546    },
547    {
548      name: 'payload',
549      comment: 'The event payload',
550      typeName: 'Android: Map<String, Any?> | Bundle\niOS: [String: Any?]',
551    },
552  ]}
553/>
554
555</APIBox>
556
557<APIBox header="AppContext">
558
559The app context is an interface to a single Expo app.
560
561#### Properties
562
563<APIMethod
564  name="constants"
565  comment="Provides access to app's constants from legacy module registry."
566  returnTypeName="Android: ConstantsInterface? iOS: EXConstantsInterface?"
567  isProperty={true}
568/>
569
570<APIMethod
571  name="permissions"
572  comment="Provides access to the permissions manager from legacy module registry."
573  returnTypeName="Android: Permissions? iOS: EXPermissionsInterface?"
574  isProperty={true}
575/>
576
577<APIMethod
578  name="imageLoader"
579  comment="Provides access to the image loader from the legacy module registry."
580  returnTypeName="Android: ImageLoaderInterface? iOS: EXImageLoaderInterface?"
581  isProperty={true}
582/>
583
584<APIMethod
585  name="barcodeScanner"
586  comment="Provides access to the bar code scanner manager from the legacy module registry."
587  returnTypeName="ImageLoaderInterface?"
588  isProperty={true}
589  platforms={['Android']}
590/>
591
592<APIMethod
593  name="camera"
594  comment="Provides access to the camera view manager from the legacy module registry."
595  returnTypeName="CameraViewInterface?"
596  isProperty={true}
597  platforms={['Android']}
598/>
599
600<APIMethod
601  name="font"
602  comment="Provides access to the font manager from the legacy module registry."
603  returnTypeName="FontManagerInterface?"
604  isProperty={true}
605  platforms={['Android']}
606/>
607
608<APIMethod
609  name="sensor"
610  comment="Provides access to the sensor manager from the legacy module registry."
611  returnTypeName="SensorServiceInterface?"
612  isProperty={true}
613  platforms={['Android']}
614/>
615
616<APIMethod
617  name="taskManager"
618  comment="Provides access to the task manager from the legacy module registry."
619  returnTypeName="TaskManagerInterface?"
620  isProperty={true}
621  platforms={['Android']}
622/>
623
624<APIMethod
625  name="activityProvider"
626  comment="Provides access to the activity provider from the legacy module registry."
627  returnTypeName="ActivityProvider?"
628  isProperty={true}
629  platforms={['Android']}
630/>
631
632<APIMethod
633  name="reactContext"
634  comment="Provides access to the react application context."
635  returnTypeName="Context?"
636  isProperty={true}
637  platforms={['Android']}
638/>
639
640<APIMethod
641  name="hasActiveReactInstance"
642  comment="Checks if there is an not-null, alive react native instance."
643  returnTypeName="Boolean"
644  isProperty={true}
645  platforms={['Android']}
646/>
647
648<APIMethod
649  name="utilities"
650  comment="Provides access to the utilities from legacy module registry."
651  returnTypeName="EXUtilitiesInterface?"
652  isProperty={true}
653  platforms={['iOS']}
654/>
655
656</APIBox>
657
658<APIBox header="ExpoView">
659
660A base class that should be used by all exported views.
661
662On iOS, `ExpoView` extends the `RCTView` which handles some styles (e.g. borders) and accessibility.
663
664#### Properties
665
666<APIMethod
667  name="appContext"
668  comment="Provides access to the [`AppContext`](#appcontext)."
669  returnTypeName="AppContext"
670  isProperty={true}
671  isReturnTypeReference={true}
672/>
673
674<hr />
675
676#### Extending `ExpoView`
677
678To 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`.
679
680<CodeBlocksTable>
681
682```swift
683class LinearGradientView: ExpoView {}
684
685public class LinearGradientModule: Module {
686  public func definition() -> ModuleDefinition {
687    View(LinearGradientView.self) {
688      // ...
689    }
690  }
691}
692```
693
694```kotlin
695class LinearGradientView(
696  context: Context,
697  appContext: AppContext,
698) : ExpoView(context, appContext)
699
700class LinearGradientModule : Module() {
701  override fun definition() = ModuleDefinition {
702    View(LinearGradientView::class) {
703      // ...
704    }
705  }
706}
707```
708
709</CodeBlocksTable>
710
711</APIBox>
712
713## Guides
714
715<APIBox header="Sending events">
716
717While 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.
718
719To 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:
720
721<CodeBlocksTable>
722
723```swift
724let CLIPBOARD_CHANGED_EVENT_NAME = "onClipboardChanged"
725
726public class ClipboardModule: Module {
727  public func definition() -> ModuleDefinition {
728    Events(CLIPBOARD_CHANGED_EVENT_NAME)
729
730    OnStartObserving {
731      NotificationCenter.default.addObserver(
732        self,
733        selector: #selector(self.clipboardChangedListener),
734        name: UIPasteboard.changedNotification,
735        object: nil
736      )
737    }
738
739    OnStopObserving {
740      NotificationCenter.default.removeObserver(
741        self,
742        name: UIPasteboard.changedNotification,
743        object: nil
744      )
745    }
746  }
747
748  @objc
749  private func clipboardChangedListener() {
750    sendEvent(CLIPBOARD_CHANGED_EVENT_NAME, [
751      "contentTypes": availableContentTypes()
752    ])
753  }
754}
755```
756
757```kotlin
758const val CLIPBOARD_CHANGED_EVENT_NAME = "onClipboardChanged"
759
760class ClipboardModule : Module() {
761  override fun definition() = ModuleDefinition {
762    Events(CLIPBOARD_CHANGED_EVENT_NAME)
763
764    OnStartObserving {
765      clipboardManager?.addPrimaryClipChangedListener(listener)
766    }
767
768    OnStopObserving {
769      clipboardManager?.removePrimaryClipChangedListener(listener)
770    }
771  }
772
773  private val clipboardManager: ClipboardManager?
774    get() = appContext.reactContext?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
775
776  private val listener = ClipboardManager.OnPrimaryClipChangedListener {
777    clipboardManager?.primaryClipDescription?.let { clip ->
778      [email protected](
779        CLIPBOARD_CHANGED_EVENT_NAME,
780        bundleOf(
781          "contentTypes" to availableContentTypes(clip)
782        )
783      )
784    }
785  }
786}
787```
788
789</CodeBlocksTable>
790
791To subscribe to these events in JavaScript/TypeScript, you need to wrap the native module with `EventEmitter` class as shown:
792
793```ts TypeScript
794import { requireNativeModule, EventEmitter, Subscription } from 'expo-modules-core';
795
796const ClipboardModule = requireNativeModule('Clipboard');
797const emitter = new EventEmitter(ClipboardModule);
798
799export function addClipboardListener(listener: (event) => void): Subscription {
800  return emitter.addListener('onClipboardChanged', listener);
801}
802```
803
804</APIBox>
805
806<APIBox header="View callbacks">
807
808Some 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.
809
810To 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.
811
812> **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>}`.
813
814<CodeBlocksTable>
815
816```swift
817class CameraViewModule: Module {
818  public func definition() -> ModuleDefinition {
819    View(CamerView.self) {
820      Events(
821        "onCameraReady"
822      )
823
824      // ...
825    }
826  }
827}
828
829class CameraView: ExpoView {
830  let onCameraReady = EventDispatcher()
831
832  func callOnCameraReady() {
833    onCameraReady([
834      "message": "Camera was mounted"
835    ]);
836  }
837}
838```
839
840```kotlin
841class CameraViewModule : Module() {
842  override fun definition() = ModuleDefinition {
843    View(ExpoCameraView::class) {
844      Events(
845        "onCameraReady"
846      )
847
848      // ...
849    }
850  }
851}
852
853class CameraView(
854  context: Context,
855  appContext: AppContext
856) : ExpoView(context, appContext) {
857  val onCameraReady by EventDispatcher()
858
859  fun callOnCameraReady() {
860    onCameraReady(mapOf(
861      "message" to "Camera was mounted"
862    ));
863  }
864}
865```
866
867</CodeBlocksTable>
868
869To subscribe to these events in JavaScript/TypeScript, you need to pass a function to the native view as shown:
870
871```ts TypeScript
872import { requireNativeViewManager } from 'expo-modules-core';
873
874const CameraView = requireNativeViewManager('CameraView');
875
876export default function MainView() {
877  const onCameraReady = event => {
878    console.log(event.nativeEvent);
879  };
880
881  return <CameraView onCameraReady={onCameraReady} />;
882}
883```
884
885Provided payload is available under the `nativeEvent` key.
886
887</APIBox>
888
889## Examples
890
891<CodeBlocksTable>
892
893```swift
894public class MyModule: Module {
895  public func definition() -> ModuleDefinition {
896    Name("MyFirstExpoModule")
897
898    Function("hello") { (name: String) in
899      return "Hello \(name)!"
900    }
901  }
902}
903```
904
905```kotlin
906class MyModule : Module() {
907  override fun definition() = ModuleDefinition {
908    Name("MyFirstExpoModule")
909
910    Function("hello") { name: String ->
911      return "Hello $name!"
912    }
913  }
914}
915```
916
917</CodeBlocksTable>
918
919For more examples from real modules, you can refer to Expo modules that already use this API on GitHub:
920
921- `expo-battery` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-battery/ios))
922- `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))
923- `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))
924- `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))
925- `expo-device` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-device/ios))
926- `expo-haptics` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-haptics/ios))
927- `expo-image-manipulator` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-image-manipulator/ios))
928- `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))
929- `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))
930- `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))
931- `expo-store-review` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-store-review/ios))
932- `expo-system-ui` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-system-ui/ios/ExpoSystemUI))
933- `expo-video-thumbnails` ([Swift](https://github.com/expo/expo/tree/main/packages/expo-video-thumbnails/ios))
934- `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))
935