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