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