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