模块 API 参考

Expo 模块 API 的 API 参考。


原生模块 API 是建立在 JSI 和 React Native 所依赖的其他低级原语之上的抽象层。它使用现代语言(Swift 和 Kotlin)构建,并提供易于使用且方便的 API,并在可能的情况下在各个平台上保持一致。

🌐 The native modules API is an abstraction layer on top of 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.

定义组件

🌐 Definition components

正如你可能在 Get Started 页面上的片段中注意到的,每个模块类都必须实现 definition 函数。 模块定义由描述模块功能和行为的 DSL 组件组成。

🌐 As you might have noticed in the snippets on the Get Started page, each module class must implement the definition function. The module definition consists of the DSL components that describe the module's functionality and behavior.

Name

设置 JavaScript 代码将用于引用模块的模块名称。参数是一个字符串。虽然可以从模块的类名推断出名称,但建议明确设置,以提高清晰度。

🌐 Sets 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.

Swift / Kotlin
Name("MyModuleName")

Constant

在 JavaScript 对象上定义一个常量属性。该属性仅在首次访问时计算一次,之后的访问会返回缓存的值。

🌐 Defines a constant property on the JavaScript object. The property is computed only once when it's first accessed, and subsequent accesses return the cached value.

Swift
Constant("PI") { Double.pi }
Kotlin
Constant("PI") { Math.PI }

Function

定义一个将导出到 JavaScript 的本地同步函数。同步意味着当函数在 JavaScript 中执行时,其本地代码将在同一线程上运行,并阻塞脚本的进一步执行,直到本地函数返回。

🌐 Defines 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.

参数

🌐 Arguments

  • 名称String — 你将在 JavaScript 中调用的函数名称。
  • body(args...) -> ReturnType — 调用函数时要执行的闭包。

该函数最多可以接受 8 个参数。这是由于 Swift 和 Kotlin 中泛型的限制,因为该组件必须针对每个参数个数单独实现。

🌐 The 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.

有关函数体中可以使用的类型的更多详细信息,请参阅参数类型部分。

🌐 See the Argument types section for more details on what types can be used in the function body.

Swift
Function("mySyncFunction") { (message: String) in return message }
Kotlin
Function("mySyncFunction") { message: String -> return@Function message }
JavaScript
import { requireNativeModule } from 'expo-modules-core'; // Assume that we have named the module "MyModule" const MyModule = requireNativeModule('MyModule'); function getMessage() { return MyModule.mySyncFunction('bar'); }

AsyncFunction

定义了一个 JavaScript 函数,该函数总是返回 Promise,并且其本地代码默认在与 JavaScript 运行时不同的线程上执行。

🌐 Defines 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.

参数

🌐 Arguments

  • 名称String — 你将在 JavaScript 中调用的函数名称。
  • body(args...) -> ReturnType — 调用函数时要执行的闭包。

如果最后一个参数的类型是 Promise,函数会等待 promise 被解决或拒绝后才将响应传回 JavaScript。否则,函数会立即用返回值解决,或者如果抛出异常则被拒绝。 函数最多可以接收 8 个参数(包括 promise)。

🌐 If 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. The function can receive up to 8 arguments (including the promise).

有关函数体中可以使用的类型的更多详细信息,请参阅参数类型部分。

🌐 See the Argument types section for more details on what types can be used in the function body.

当出现以下情况时,建议使用 AsyncFunction 而不是 Function

🌐 It is recommended to use AsyncFunction over Function when it:

  • 执行 I/O 绑定任务,例如发送网络请求或与文件系统交互
  • 需要在不同的线程上运行,例如 UI 相关任务的主 UI 线程
  • 是一个广泛或持久的操作,会阻塞 JavaScript 线程,从而降低应用的响应能力
Swift
AsyncFunction("myAsyncFunction") { (message: String) in return message } // or AsyncFunction("myAsyncFunction") { (message: String, promise: Promise) in promise.resolve(message) }
Kotlin
AsyncFunction("myAsyncFunction") { message: String -> return@AsyncFunction message } // or // Make sure to import `Promise` class from `expo.modules.kotlin` instead of `expo.modules.core`. AsyncFunction("myAsyncFunction") { message: String, promise: Promise -> promise.resolve(message) }
JavaScript
import { requireNativeModule } from 'expo-modules-core'; // Assume that we have named the module "MyModule" const MyModule = requireNativeModule('MyModule'); async function getMessageAsync() { return await MyModule.myAsyncFunction('bar'); }

可以通过在该组件的结果上调用 .runOnQueue 函数来更改 AsyncFunction 的本地队列。

🌐 It is possible to change the native queue of AsyncFunction by calling the .runOnQueue function on the result of that component.

Swift
AsyncFunction("myAsyncFunction") { (message: String) in return message }.runOnQueue(.main)
Kotlin
AsyncFunction("myAsyncFunction") { message: String -> return@AsyncFunction message }.runOnQueue(Queues.MAIN)

Kotlin 协程
Android

🌐 Kotlin coroutines

Android

AsyncFunction 可以在 Android 上接收一个可挂起的主体。然而,它必须在 Coroutine 块之后以中缀表示法传入。你可以在 协程概览 中阅读更多关于可挂起函数和协程的内容。

AsyncFunction 带有可挂起的主体,不能将 Promise 作为参数。它使用挂起机制来执行异步调用。该函数会立即以所提供可挂起块返回的值解析,如果抛出异常则会被拒绝。该函数最多可以接收 8 个参数。

默认情况下,挂起函数在模块的协程作用域中调度。此外,从主体块调用的每个其他可挂起函数也将在相同的作用域内运行。这个作用域的生命周期与模块的生命周期绑定——当模块被释放时,所有未完成的挂起函数都会被取消。

🌐 By 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. This scope's lifecycle is bound to the module's lifecycle - all unfinished suspend functions will be canceled when the module is deallocated.

Kotlin
AsyncFunction("suspendFunction") Coroutine { message: String -> // You can execute other suspendable functions here. // For example, you can use `kotlinx.coroutines.delay` to delay resolving the underlying promise. delay(5000) return@Coroutine message }

Events

定义模块可以发送到 JavaScript 的事件名称。

🌐 Defines event names that the module can send to JavaScript.

注意: 该组件可以在 View 块内使用,以定义回调名称。请参阅 View callbacks

Swift
Events("onCameraReady", "onPictureSaved", "onBarCodeScanned")
Kotlin
Events("onCameraReady", "onPictureSaved", "onBarCodeScanned")

请参阅 发送事件 以了解如何从原生代码向 JavaScript/TypeScript 发送事件。

🌐 See Sending events to learn how to send events from the native code to JavaScript/TypeScript.

Property

在表示原生模块的 JavaScript 对象上直接定义一个新属性。这与在模块对象上调用 Object.defineProperty 是一样的。

🌐 Defines a new property directly on the JavaScript object that represents a native module. It is the same as calling Object.defineProperty on the module object.

要声明只读属性,你可以使用需要两个参数的简写语法:

🌐 To declare a read-only property, you can use a shorthanded syntax that requires two arguments:

  • 名称String — 在 JavaScript 中使用的属性名称。
  • getter: () -> PropertyType — 当调用某个属性的getter时要执行的闭包。
Swift
Property("foo") { return "bar" }
Kotlin
Property("foo") { return@Property "bar" }

对于可变属性,getter 和 setter 闭包都是需要的(使用下面的语法也可以声明仅具有 setter 的属性):

🌐 In 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):

  • 名称String — 在 JavaScript 中使用的属性名称。
  • getter: () -> PropertyType — 当调用某个属性的getter时要执行的闭包。
  • setter: (newValue: PropertyType) -> void — 当属性的 setter 被调用时要执行的闭包。
Swift
Property("foo") .get { return "bar" } .set { (newValue: String) in // do something with new value }
Kotlin
Property("foo") .get { return@get "bar" } .set { newValue: String -> // do something with new value }
JavaScript
import { requireNativeModule } from 'expo-modules-core'; // Assume that we have named the module "MyModule" const MyModule = requireNativeModule('MyModule'); // Obtain the property value MyModule.foo; // Set a new value MyModule.foo = 'foobar';

View

使模块可以作为原生视图使用。作为视图定义一部分被接受的定义组件包括:PropEventsGroupViewAsyncFunction

🌐 Enables the module to be used as a native view. Definition components that are accepted as part of the view definition: Prop, Events, GroupView and AsyncFunction.

在视图定义中,AsyncFunction 会被添加到表示原生视图的 React 组件的 React ref 中。 这样的异步函数会自动将原生视图的实例作为第一个参数,并默认在 UI 线程上运行。

参数

🌐 Arguments

  • viewType — 将要渲染的原生视图的类。注意:在 Android 上,提供的类必须继承自 ExpoView,在 iOS 上则是可选的。参见 Extending ExpoView
  • 定义() -> ViewDefinition — 视图定义的构建器。
Swift
View(UITextView.self) { Prop("text") { %%placeholder-start%%... %%placeholder-end%% } AsyncFunction("focus") { (view: UITextView) in view.becomeFirstResponder() } }
Kotlin
View(TextView::class) { Prop("text") { %%placeholder-start%%... %%placeholder-end%% } AsyncFunction("focus") { view: TextView -> view.requestFocus() } }

信息 计划支持渲染 SwiftUI 视图。目前,你可以使用 UIHostingController 并将其内容视图添加到你的 UIKit 视图中。

OnCreate

定义模块的生命周期监听器,会在模块初始化之后立即调用。如果你需要在模块初始化时进行某些设置,请使用此方法,而不是模块类的初始化器。

🌐 Defines 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.

OnDestroy

定义模块的生命周期监听器,当模块即将被释放时调用它。请使用它而不是模块的类析构函数。

🌐 Defines module's lifecycle listener that is called when the module is about to be deallocated. Use it instead of module's class destructor.

OnStartObserving

定义添加第一个事件监听器时调用的函数。

🌐 Defines the function that is invoked when the first event listener is added.

OnStopObserving

定义删除所有事件监听器时调用的函数。

🌐 Defines the function that is invoked when all event listeners are removed.

OnAppContextDestroys

定义模块的生命周期监听器,当拥有该模块的应用上下文即将被释放时调用该监听器。

🌐 Defines module's lifecycle listener that is called when the app context owning the module is about to be deallocated.

OnAppEntersForeground

Only for:
iOS

定义当应用即将进入前台模式时调用的监听器。

🌐 Defines the listener that is called when the app is about to enter the foreground mode.

注意: 该功能在 Android 上不可用——你可能需要改用 OnActivityEntersForeground

OnAppEntersBackground

Only for:
iOS

定义当应用进入后台模式时调用的监听器。

🌐 Defines the listener that is called when the app enters the background mode.

注意: 该功能在 Android 上不可用——你可能需要使用 OnActivityEntersBackground 代替。

OnAppBecomesActive

Only for:
iOS

定义当应用再次变为活动状态(在 OnAppEntersForeground 之后)时调用的监听器。

🌐 Defines the listener that is called when the app becomes active again (after OnAppEntersForeground).

注意: 该功能在 Android 上不可用——你可能需要改用 OnActivityEntersForeground

OnActivityEntersForeground

Only for:
Android

定义活动恢复后立即调用的活动生命周期监听器。

🌐 Defines the activity lifecycle listener that is called right after the activity is resumed.

注意: 此功能在 iOS 上不可用——你可能需要改用 OnAppEntersForeground

OnActivityEntersBackground

Only for:
Android

定义活动暂停后立即调用的活动生命周期监听器。

🌐 Defines the activity lifecycle listener that is called right after the activity is paused.

注意: 此功能在 iOS 上不可用——你可能需要改用 OnAppEntersBackground

OnActivityDestroys

Only for:
Android

定义当拥有 JavaScript 上下文的活动即将被销毁时调用的活动生命周期监听器。

🌐 Defines the activity lifecycle listener that is called when the activity owning the JavaScript context is about to be destroyed.

注意: 此功能在 iOS 上不可用——你可能需要改用 OnAppEntersBackground

OnActivityResult

Only for:
Android

定义活动生命周期监听器,当使用 startActivityForResult 启动的活动返回结果时调用该监听器。

🌐 Defines the activity lifecycle listener that is called when the activity launched with startActivityForResult returns a result.

参数

🌐 Arguments

  • activity — 接收结果的 Android 活动。
  • payload — 一个包含活动结果数据的对象。
    • requestCode: Int — 最初传递给 startActivityForResult 的请求代码,用于标识结果的来源。
    • resultCode: Int — 子活动返回的结果代码(例如,Activity.RESULT_OKActivity.RESULT_CANCELED)。
    • data — 一个可选的 Intent,用于携带从启动的活动返回的结果数据。可以是 null
Kotlin
AsyncFunction('someFunc') { %%placeholder-start%%... %%placeholder-end%% activity.startActivityForResult(someIntent, SOME_REQUEST_CODE) } OnActivityResult { activity, payload -> %%placeholder-start%%... %%placeholder-end%% }

Constants

警告 **已弃用:**请改用 Constant

在模块上设置常量属性。可以使用字典或返回字典的闭包。

🌐 Sets constant properties on the module. Can take a dictionary or a closure that returns a dictionary.

Swift
// Created from the dictionary Constants([ "PI": Double.pi ]) // or returned by the closure Constants { return [ "PI": Double.pi ] }
Kotlin
// Passed as arguments Constants( "PI" to kotlin.math.PI ) // or returned by the closure Constants { return@Constants mapOf( "PI" to kotlin.math.PI ) }

查看定义组件

🌐 View definition components

视图定义由描述视图功能和行为的 DSL 组件组成。这些组件只能在 View 闭包中使用。

🌐 The view definition consists of the DSL components that describe the view's functionality and behavior. Those components can only be used within a View closure.

Name

设置 JavaScript 代码将用于引用视图的视图名称。接受一个字符串作为参数。该名称可以从视图的类名中推断出来,但建议为清晰起见显式设置它。

🌐 Sets the name of the view that JavaScript code will use to refer to the view. Takes a string as an argument. This can be inferred from the view's class name, but it's recommended to set it explicitly for clarity.

Swift / Kotlin
Name("MyViewName")

Prop

为给定名称的 view prop 定义一个 setter。

🌐 Defines a setter for the view prop of given name.

参数

🌐 Arguments

  • nameString — 你想要定义 setter 的视图属性名称。
  • defaultValue: ValueType — 可选默认值,当调用 setter 并传入 null 时使用。
  • setter(view: ViewType, value: ValueType) -> () — 当视图重新渲染时调用的闭包。

此属性只能在 View 闭包中使用。

🌐 This property can only be used within a View closure.

Swift
Prop("background") { (view: UIView, color: UIColor) in view.backgroundColor = color }
Kotlin
Prop("background") { view: View, @ColorInt color: Int -> view.setBackgroundColor(color) }

具有默认值的 Prop 定义。

🌐 Prop definition with default value.

Swift
Prop("background", UIColor.black) { (view: UIView, color: UIColor) in view.backgroundColor = color }
Kotlin
Prop("background", Color.BLACK) { view: View, @ColorInt color: Int -> view.setBackgroundColor(color) }

注意: 目前尚不支持函数类型(回调)的属性。

OnViewDidUpdateProps

定义视图生命周期方法,当视图完成所有 props 更新后会调用该方法。

🌐 Defines the view lifecycle method that is called when the view finished updating all props.

View(MyView.self) { OnViewDidUpdateProps { view: MyView in %%placeholder-start%%... %%placeholder-end%% } }
View(MyView::class) { OnViewDidUpdateProps { view: MyView -> %%placeholder-start%%... %%placeholder-end%% } }

OnViewDestroys

Only for:
Android

创建一个视图的生命周期监听器,该监听器会在 React Native 不再使用该视图时立即被调用。

🌐 Creates a view's lifecycle listener that is called right after the view is no longer used by React Native.

View(MyView::class) { OnViewDestroys { view: MyView -> %%placeholder-start%%... %%placeholder-end%% } }

注意: 此功能在 iOS 上不可用。你可能需要使用原生视图的析构函数来实现类似效果。

(View) AsyncFunction

与模块定义中的 AsyncFunction 类似,你可以定义附加到视图引用的函数,以允许直接修改原生视图。

🌐 Similarly to the AsyncFunction inside the module definition, you can define functions attached to the view ref to allow direct modification of the native view.

视图异步函数将始终在主队列中调度,并可以接收视图实例作为第一个参数。

🌐 View async functions will always be dispatched on the main queue and can receive the view instance as the first argument.

Swift
View(MyView.self) { AsyncFunction("myAsyncFunction") { (view: MyView, message: String) in view.displayMessage(message) } }
Kotlin
View(MyView::class) { AsyncFunction("myAsyncFunction") { view: MyView, message: String -> view.displayMessage(message); } }
JavaScript
const MyNativeView = requireNativeViewManager('MyView'); function MyComponent() { const ref = React.useRef(null); React.useEffect(() => { ref.current?.myAsyncFunction(); }, [ref]); return <MyNativeView ref={ref} />; }

GroupView

Only for:
Android

使该视图可用作视图组。作为组视图定义的一部分被接受的定义组件包括:AddChildViewGetChildCountGetChildViewAtRemoveChildViewRemoveChildViewAt

🌐 Enables the view to be used as a view group. Definition components that are accepted as part of the group view definition: AddChildView, GetChildCount, GetChildViewAt, RemoveChildView, RemoveChildViewAt.

参数

🌐 Arguments

  • viewType — 原生视图的类。请注意,提供的类必须继承自 Android 的 ViewGroup
  • 定义() -> ViewGroupDefinition — 视图组定义的构建器。

此属性只能在 View 闭包中使用。

🌐 This property can only be used within a View closure.

Kotlin
GroupView<ViewGroup> { AddChildView { parent, child, index -> %%placeholder-start%%... %%placeholder-end%%} }

AddChildView

Only for:
Android

定义将子视图添加到视图组的操作。

🌐 Defines action that adds a child view to the view group.

参数

🌐 Arguments

  • 操作(parent: ParentType, child: ChildType, index: Int) -> () — 一个将子视图添加到视图组的操作。

此属性只能在 GroupView 闭包中使用。

🌐 This property can only be used within a GroupView closure.

Kotlin
AddChildView { parent, child: View, index -> parent.addView(child, index) }

GetChildCount

Only for:
Android

定义检索视图组中子视图数量的操作。

🌐 Defines action the retrieves the number of child views in the view group.

参数

🌐 Arguments

  • 动作: (parent: ParentType) -> Int — 一个返回子视图数量的函数。

此属性只能在 GroupView 闭包中使用。

🌐 This property can only be used within a GroupView closure.

Kotlin
GetChildCount { parent -> return@GetChildCount parent.childCount }

GetChildViewAt

Only for:
Android

定义从视图组中检索特定索引处的子视图的操作。

🌐 Defines action that retrieves a child view at a specific index from the view group.

参数

🌐 Arguments

  • 操作(parent: ParentType, index: Int) -> ChildType — 一个从视图组中按指定索引获取子视图的函数。

此属性只能在 GroupView 闭包中使用。

🌐 This property can only be used within a GroupView closure.

Kotlin
GetChildViewAt { parent, index -> parent.getChildAt(index) }

RemoveChildView

Only for:
Android

定义从视图组中删除特定子视图的操作。

🌐 Defines action that removes a specific child view from the view group.

参数

🌐 Arguments

  • 操作: (parent: ParentType, child: ChildType) -> () — 一个从视图组中移除指定子视图的函数。

此属性只能在 GroupView 闭包中使用。

🌐 This property can only be used within a GroupView closure.

Kotlin
RemoveChildView { parent, child: View -> parent.removeView(child) }

RemoveChildViewAt

Only for:
Android

定义从视图组中删除特定索引处的子视图的操作。

🌐 Defines action that removes a child view at a specific index from the view group.

参数

🌐 Arguments

  • 操作(parent: ParentType, child: ChildType) -> () — 一个从视图组中移除特定索引子视图的函数。

此属性只能在 GroupView 闭包中使用。

🌐 This property can only be used within a GroupView closure.

Kotlin
RemoveChildViewAt { parent, index -> parent.removeViewAt(child) }

参数类型

🌐 Argument types

从根本上讲,只有原始数据和可序列化数据可以在运行时之间传递。然而,通常本地模块需要接收自定义数据结构——比仅仅是值类型未知(Any)的字典/映射更复杂,因此每个值都必须单独验证和转换。Expo 模块 API 提供了协议,使处理数据对象更方便,提供自动验证,并最终确保每个对象成员的本地类型安全。

🌐 Fundamentally, 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.

Primitives

所有函数和视图属性设置器都接受 Swift 和 Kotlin 中的所有常见基本类型作为参数。这包括这些基本类型的数组、字典/映射以及可选类型。

🌐 All 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.

语言支持的原始类型
SwiftBool, Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Float32, Double, String
KotlinBoolean, Int, Long, Float, Double, String, Pair

Convertibles

可转换类型 是可以从从 JavaScript 接收到的某些特定类型的数据初始化的本地类型。这类类型可以作为 Function 函数体中的参数类型。例如,当 CGPoint 类型被用作函数参数类型时,其实例可以由一个包含两个数字的数组 (x, y) 或具有数字 xy 属性的 JavaScript 对象创建。

🌐 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.

内置的可转换类型在下方有更详细的文档。你可以通过让原生 Swift 类型遵循 Convertible 协议来定义额外的可转换类型:

🌐 The built-in Convertibles are documented further below. You can define additional Convertibles by making native Swift types conform to the Convertible protocol:

Convertible

Only for:
iOS

Convertible 是一个包含一个静态方法的 Swift 协议:

convert(value, appContext)

ParameterTypeDescription
valueAny?

要转换的来自 JavaScript 的值

appContextAppContext

当前运行的 Expo 应用实例的上下文对象


一个静态方法,用于将来自 JavaScript 的动态类型值转换为符合 Convertible 的 Swift 类型实例。当给定的值无效或类型不支持时,调用者应抛出异常。

Returns:
Self

示例

🌐 Example

Swift
import ExpoModulesCore extension CMTime: @retroactive Convertible { public static func convert(from value: Any?, appContext: AppContext) throws -> CMTime { if let seconds = value as? Double { return CMTime(seconds: seconds, preferredTimescale: .max) } throw Conversions.ConvertingException<CMTime>(value) } }

信息 计划在 SDK 53 提供对使用 Kotlin 定义可转换类型的支持。


内置可转换程序

🌐 Built-in Convertibles

CoreGraphicsUIKit 系统框架中的一些常见 iOS 类型已经可以转换。

🌐 Some common iOS types from the CoreGraphics and UIKit system frameworks are already made convertible.

Native iOS TypeTypeScript
URLstring with a URL. When a scheme is not provided, it's assumed to be a file URL.
CGFloatnumber
CGPoint{ x: number, y: number } or number[] with x and y coords
CGSize{ width: number, height: number } or number[] with width and height
CGVector{ dx: number, dy: number } or number[] with dx and dy vector differentials
CGRect{ x: number, y: number, width: number, height: number } or number[] with x, y, width and height values
CGColor
UIColor
Color hex strings (#RRGGBB, #RRGGBBAA, #RGB, #RGBA), named colors following the CSS3/SVG specification or "transparent"
DataUint8Array
SDK 50+

同样,来自 java.iojava.netandroid.graphics 等包的一些常见 Android 类型也可以转换。

🌐 Similarly, some common Android types from packages like java.io, java.net, or android.graphics are also made convertible.

注意: 在 Android 上,应尽可能使用原始数组。

Native Android TypeTypeScript
java.net.URLstring with a URL. Note that the scheme has to be provided (URL should not contain any unencoded % character)
android.net.Uri
java.net.URI
string with a URI. Note that the scheme has to be provided (URI should not contain any unencoded % character)
java.io.File
java.nio.file.Path (is only available on Android API 26)
string with a path to the file
android.graphics.ColorColor hex strings (#RRGGBB, #RRGGBBAA, #RGB, #RGBA), named colors following the CSS3/SVG specification or "transparent"
kotlin.Pair<A, B>Array with two values, where the first one is of type A and the second is of type B
kotlin.ByteArrayUint8Array
SDK 50+
kotlin.BooleanArrayboolean[]
kotlin.IntArray
kotlin.FloatArray
kotlin.LongArray
kotlin.DoubleArray
number[]
kotlin.time.Durationnumber represents a duration in seconds
SDK 52+

Records

Record 是一种可转换类型,相当于字典(Swift)或映射(Kotlin),但它被表示为一个结构体,其中每个字段都可以有自己的类型并提供默认值。
它是一种更好的方式来表示具有原生类型安全性的 JavaScript 对象。

🌐 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. It is a better way to represent a JavaScript object with the native type safety.

Swift
struct FileReadOptions: Record { @Field var encoding: String = "utf8" @Field var position: Int = 0 @Field var length: Int? } // Now this record can be used as an argument of the functions or the view prop setters. Function("readFile") { (path: String, options: FileReadOptions) -> String in // Read the file using given `options` }
Kotlin
class FileReadOptions : Record { @Field val encoding: String = "utf8" @Field val position: Int = 0 @Field val length: Int? = null } // Now this record can be used as an argument of the functions or the view prop setters. Function("readFile") { path: String, options: FileReadOptions -> // Read the file using given `options` }

Formatter

重要 此功能为试验性功能。

Formatter API 允许你自定义从本地函数返回时记录的序列化方式。这在你需要在将属性值发送到 JavaScript 之前进行转换,或有条件地从输出中排除某些属性时非常有用。

🌐 The Formatter API allows you to customize how a Record is serialized when returned from a native function. This is useful when you need to transform property values before sending them to JavaScript, or conditionally exclude certain properties from the output.

操作

🌐 Operations

  • map:在序列化之前转换属性的值。
  • 跳过:完全排除某个属性的输出。

基本用法

🌐 Basic usage

Swift
struct UserInfo: Record { @Field var id: Int = 0 @Field var email: String = "" @Field var password: String = "" } Function("getUser") { let user = UserInfo(id: 1, email: "user@example.com", password: "secret123") // Return user without exposing the password return user.format { formatter in formatter.property("password", keyPath: \.password).skip() } }
Kotlin
class UserInfo( @Field val id: Int = 0, @Field val email: String = "", @Field val password: String = "" ) : Record Function("getUser") { val user = UserInfo(id = 1, email = "user@example.com", password = "secret123") // Return user without exposing the password formatter { property(UserInfo::password).skip() }.format(user) }
JavaScript
const user = MyModule.getUser(); console.log(user); // Output: { id: 1, email: "user@example.com" } // Note: password is not present in the object

使用 map 转换值

🌐 Transforming values with map

在将属性值发送到 JavaScript 之前,使用 map 进行转换:

🌐 Use map to transform property values before they are sent to JavaScript:

Swift
struct Product: Record { @Field var name: String = "" @Field var price: Double = 0.0 } Function("getProduct") { let product = Product(name: "Widget", price: 19.99) return product.format { formatter in // Transform price to include currency symbol formatter.property("price", keyPath: \.price).map { value in "$\(String(format: "%.2f", value))" } } }
Kotlin
class Product( @Field val name: String = "", @Field val price: Double = 0.0 ) : Record Function("getProduct") { val product = Product(name = "Widget", price = 19.99) formatter { // Transform price to include currency symbol property(Product::price).map { value -> "${"$"}${String.format("%.2f", value)}" } }.format(product) }

条件跳过

🌐 Conditional skipping

你可以根据属性的值或记录的状态有条件地跳过某些属性:

🌐 You can conditionally skip properties based on their values or the record's state:

Swift
struct Settings: Record { @Field var theme: String = "light" @Field var debugMode: Bool = false @Field var apiKey: String? = nil } Function("getSettings") { let settings = Settings(theme: "dark", debugMode: true, apiKey: "secret") return settings.format { formatter in // Skip apiKey if nil formatter.property("apiKey", keyPath: \.apiKey).skip { value in value == nil } } }
Kotlin
class Settings( @Field val theme: String = "light", @Field val debugMode: Boolean = false, @Field val apiKey: String? = null ) : Record Function("getSettings") { val settings = Settings(theme = "dark", debugMode = true, apiKey = "secret") formatter { // Skip apiKey if null property(Settings::apiKey).skip { value -> value == null } }.format(settings) }

链式操作

🌐 Chaining operations

你可以在同一个属性上链接多个操作:

🌐 You can chain multiple operations on the same property:

Swift
struct Data: Record { @Field var value: Int? = nil } Function("getData") { let data = Data(value: nil) return data.format { formatter in formatter.property("value", keyPath: \.value) .map { $0 ?? 0 } // Default to 0 if nil .map { $0 * 2 } // Double the value } }
Kotlin
class Data( @Field val value: Int? = null ) : Record Function("getData") { val data = Data(value = null) formatter { property(Data::value) .map { it ?: 0 } // Default to 0 if null .map { it * 2 } // Double the value }.format(data) }

Enums

通过枚举,我们可以在上述示例(使用 FileReadOptions 记录)基础上更进一步,将支持的编码限制为 "utf8""base64"。要将枚举用作参数或记录字段,它必须表示一个基本值(例如 StringInt)并符合 Enumerable

🌐 With 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.

Swift
enum FileEncoding: String, Enumerable { case utf8 case base64 } struct FileReadOptions: Record { @Field var encoding: FileEncoding = .utf8 %%placeholder-start%%... %%placeholder-end%% }
Kotlin
// Note: the constructor must have an argument called value. enum class FileEncoding(val value: String) : Enumerable { utf8("utf8"), base64("base64") } class FileReadOptions : Record { @Field val encoding: FileEncoding = FileEncoding.utf8 %%placeholder-start%%... %%placeholder-end%% }

Eithers

有些使用场景中,你可能希望为单个函数参数传递多种类型。这时 Either 类型可能会派上用场。它们可以作为一个容器,存放几种类型中的某一种值。

🌐 There 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. They act as a container for a value of one of a couple of types.

Swift
Function("foo") { (bar: Either<String, Int>) in if let bar: String = bar.get() { // `bar` is a String } if let bar: Int = bar.get() { // `bar` is an Int } }
Kotlin
Function("foo") { bar: Either<String, Int> -> bar.get(String::class).let { // `it` is a String } bar.get(Int::class).let { // `it` is an Int } }

当前提供了开箱即用的三种 Either 类型的实现,允许你使用最多四种不同的子类型。

🌐 The implementation for three Either types is currently provided out of the box, allowing you to use up to four different subtypes.

  • Either<FirstType, SecondType> — 一个容纳两种类型之一的容器。
  • EitherOfThree<FirstType, SecondType, ThirdType> — 三种类型之一的容器。
  • EitherOfFour<FirstType, SecondType, ThirdType, FourthType> — 四种类型之一的容器。

ValueOrUndefined

重要 此功能为试验性功能。

ValueOrUndefined 是一种封装类型,允许你区分 JavaScript 的 undefined 值和实际值。

使用常规可选类型时,JavaScript 中的 undefinednull 在本地端都会被转换为 null,因此无法区分它们。ValueOrUndefined 通过保留这种区别来解决了这个问题。

🌐 With regular optional types, both undefined and null from JavaScript are converted to null on the native side, making it impossible to tell them apart. ValueOrUndefined solves this by preserving the distinction.

属性

🌐 Properties

isUndefined

如果 JavaScript 值是 undefined,则返回 true,否则返回 false

Returns:
Bool

optional

如果值存在,则返回解包后的值;如果值为 undefined,则返回 null

Returns:
InnerType?
Swift
Function("configure") { (timeout: ValueOrUndefined<Int>) in if timeout.isUndefined { // Argument was not provided, use default behavior } else if let value = timeout.optional { // Argument was provided with a value } }
Kotlin
Function("configure") { timeout: ValueOrUndefined<Int> -> if (timeout.isUndefined) { // Argument was not provided, use default behavior } else { timeout.optional?.let { value -> // Argument was provided with a value } } }

区分 undefinednull

🌐 Distinguishing undefined from null

在使用带有可选内部类型的 ValueOrUndefined 时,你可以区分三种状态:

🌐 When using ValueOrUndefined with an optional inner type, you can distinguish between three states:

Swift
Function("setName") { (name: ValueOrUndefined<String?>) in switch name { case .undefined: // name argument was not provided break case .value(let unwrapped) where unwrapped == nil: // name was explicitly set to null break case .value(let unwrapped): // name was set to a string value print("Name: \(unwrapped!)") } }
Kotlin
Function("setName") { name: ValueOrUndefined<String?> -> when { name.isUndefined -> { // name argument was not provided } name.optional == null -> { // name was explicitly set to null } else -> { // name was set to a string value println("Name: ${name.optional}") } } }
JavaScript
import { requireNativeModule } from 'expo-modules-core'; const MyModule = requireNativeModule('MyModule'); MyModule.setName('Alice'); // name is a value MyModule.setName(null); // name is null (but not undefined) MyModule.setName(undefined); // name is undefined

JavaScript values

也可以使用 JavaScriptValue 类型,它是一个可以容纳任何可在 JavaScript 中表示的值的容器。这个类型在你想修改给定参数或者想省略类型验证和转换时非常有用。请注意,使用特定于 JavaScript 的类型仅限于同步函数,因为 JavaScript 运行时的所有读取和写入操作都必须在 JavaScript 线程上进行。从不同线程访问这些值将导致程序崩溃。

🌐 It's also possible to use a JavaScriptValue type which is a holder for any value that can be represented in JavaScript. This type is useful when you want to mutate the given argument or when you want to omit type validations and conversions. Note 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. Any access to these values from different threads will result in a crash.

除了原始值外,JavaScriptObject 类型可以用来只允许对象类型,而 JavaScriptFunction<ReturnType> 用于回调函数。

🌐 In addition to the raw value, the JavaScriptObject type can be used to allow only object types and JavaScriptFunction<ReturnType> for callbacks.

Swift
Function("mutateMe") { (value: JavaScriptValue) in if value.isObject() { let jsObject = value.getObject() jsObject.setProperty("expo", value: "modules") } } // or Function("mutateMe") { (jsObject: JavaScriptObject) in jsObject.setProperty("expo", value: "modules") }
Kotlin
Function("mutateMe") { value: JavaScriptValue -> if (value.isObject()) { val jsObject = value.getObject() jsObject.setProperty("expo", "modules") } } // or Function("mutateMe") { jsObject: JavaScriptObject -> jsObject.setProperty("expo", "modules") }

原生类

🌐 Native classes

Module

原生模块的基类。

🌐 A base class for a native module.

属性

🌐 Properties

appContext

提供对 AppContext 的访问。

Returns:
AppContext

方法

🌐 Methods

sendEvent(eventName, payload)

ParameterTypeDescription
eventNamestring

JavaScript 事件的名称

payloadAndroid: Map<String, Any?> | Bundle iOS: [String: Any?]

事件的负载


将一个具有指定名称和负载的事件发送到 JavaScript。参见 Sending events

Returns:
void

AppContext

应用上下文是单个 Expo 应用的接口。

🌐 The app context is an interface to a single Expo app.

属性

🌐 Properties

constants

Provides access to app's constants from legacy module registry.

Returns:
Android: ConstantsInterface? iOS: EXConstantsInterface?

permissions

Provides access to the permissions manager from legacy module registry.

Returns:
Android: Permissions? iOS: EXPermissionsInterface?

imageLoader

Provides access to the image loader from the legacy module registry.

Returns:
Android: ImageLoaderInterface? iOS: EXImageLoaderInterface?

barcodeScanner

Provides access to the bar code scanner manager from the legacy module registry.

Returns:
ImageLoaderInterface?

camera

Provides access to the camera view manager from the legacy module registry.

Returns:
CameraViewInterface?

font

Provides access to the font manager from the legacy module registry.

Returns:
FontManagerInterface?

sensor

Provides access to the sensor manager from the legacy module registry.

Returns:
SensorServiceInterface?

taskManager

Provides access to the task manager from the legacy module registry.

Returns:
TaskManagerInterface?

activityProvider

Provides access to the activity provider from the legacy module registry.

Returns:
ActivityProvider?

reactContext

Provides access to the react application context.

Returns:
Context?

hasActiveReactInstance

Checks if there is an not-null, alive react native instance.

Returns:
Boolean

utilities

Provides access to the utilities from legacy module registry.

Returns:
EXUtilitiesInterface?

ExpoView

所有导出视图都应使用的基类。

🌐 A base class that should be used by all exported views.

在 iOS 上,ExpoView 扩展了 RCTView,后者处理某些样式(例如边框)和无障碍功能。

🌐 On iOS, ExpoView extends the RCTView which handles some styles (for example, borders) and accessibility.

属性

🌐 Properties

appContext

提供对 AppContext 的访问。

Returns:
AppContext

扩展 ExpoView

🌐 Extending ExpoView

要使用 View 组件导出视图,你的自定义类必须继承自 ExpoView。这样你就可以访问 AppContext 对象。这是与其他模块和 JavaScript 运行时通信的唯一方式。此外,你不能更改构造函数参数,因为提供的视图将由 expo-modules-core 初始化。

🌐 To export your view using the View component, your custom class must inherit from the ExpoView. By doing that you will get access to the 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.

Swift
class LinearGradientView: ExpoView {} public class LinearGradientModule: Module { public func definition() -> ModuleDefinition { View(LinearGradientView.self) { %%placeholder-start%%... %%placeholder-end%% } } }
Kotlin
class LinearGradientView( context: Context, appContext: AppContext, ) : ExpoView(context, appContext) class LinearGradientModule : Module() { override fun definition() = ModuleDefinition { View(LinearGradientView::class) { %%placeholder-start%%... %%placeholder-end%% } } }

指南

🌐 Guides

发送事件

🌐 Sending events

虽然 JavaScript/TypeScript 到 Native 的通信主要由原生函数覆盖,但你可能还想让 JavaScript/TypeScript 代码了解某些系统事件,例如,当剪贴板内容发生更改时。

🌐 While 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.

要做到这一点,在模块定义中,你需要使用 Events 定义组件提供模块可以发送的事件名称。之后,你可以在模块实例上使用 sendEvent(eventName, payload) 函数发送实际事件及其负载。例如,一个发送原生事件的最小化剪贴板实现可能如下所示:

🌐 To do this, in the module definition, you need to provide the event names that the module can send using the 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:

Swift
let CLIPBOARD_CHANGED_EVENT_NAME = "onClipboardChanged" public class ClipboardModule: Module { public func definition() -> ModuleDefinition { Events(CLIPBOARD_CHANGED_EVENT_NAME) OnStartObserving { NotificationCenter.default.addObserver( self, selector: #selector(self.clipboardChangedListener), name: UIPasteboard.changedNotification, object: nil ) } OnStopObserving { NotificationCenter.default.removeObserver( self, name: UIPasteboard.changedNotification, object: nil ) } } @objc private func clipboardChangedListener() { sendEvent(CLIPBOARD_CHANGED_EVENT_NAME, [ "contentTypes": availableContentTypes() ]) } }
Kotlin
const val CLIPBOARD_CHANGED_EVENT_NAME = "onClipboardChanged" class ClipboardModule : Module() { override fun definition() = ModuleDefinition { Events(CLIPBOARD_CHANGED_EVENT_NAME) OnStartObserving { clipboardManager?.addPrimaryClipChangedListener(listener) } OnStopObserving { clipboardManager?.removePrimaryClipChangedListener(listener) } } private val clipboardManager: ClipboardManager? get() = appContext.reactContext?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager private val listener = ClipboardManager.OnPrimaryClipChangedListener { clipboardManager?.primaryClipDescription?.let { clip -> this@ClipboardModule.sendEvent( CLIPBOARD_CHANGED_EVENT_NAME, bundleOf( "contentTypes" to availableContentTypes(clip) ) ) } } }

要在 JavaScript/TypeScript 中订阅这些事件,请在 requireNativeModule 返回的模块对象上使用 addListener。模块正在扩展内置的 EventEmitter 类。 或者,你也可以使用 useEventuseEventListener 钩子。

🌐 To subscribe to these events in JavaScript/TypeScript, use addListener on the module object returned by requireNativeModule. Modules are extending the built-in EventEmitter class. Alternatively, you can use useEvent or useEventListener hooks.

TypeScript
import { requireNativeModule, NativeModule } from 'expo'; type ClipboardChangeEvent = { contentTypes: string[]; }; type ClipboardModuleEvents = { onClipboardChanged(event: ClipboardChangeEvent): void; }; declare class ClipboardModule extends NativeModule<ClipboardModuleEvents> {} const Clipboard = requireNativeModule<ClipboardModule>('Clipboard'); Clipboard.addListener('onClipboardChanged', (event: ClipboardChangeEvent) => { alert('Clipboard has changed'); });

查看回调

🌐 View callbacks

有些事件与特定视图相关。例如,触摸事件应该只发送到被按下的底层 JavaScript 视图。在这种情况下,你不能使用 [#sending-events](Sending events) 中描述的 sendEventexpo-modules-core 引入了一个视图回调机制来处理与视图绑定的事件。

🌐 Some 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. The expo-modules-core introduces a view callbacks mechanism to handle view-bound events.

要使用它,在视图定义中,你需要使用 Events 定义组件提供视图可以发送的事件名称。之后,你需要在视图类中声明一个类型为 EventDispatcher 的属性。声明的属性名称必须与 Events 组件中导出的名称相同。之后,你可以将其作为函数调用,并在 iOS 上传递类型为 [String: Any?] 的负载,在 Android 上传递类型为 Map<String, Any?> 的负载。

🌐 To use it, in the view definition, you need to provide the event names that the view can send using the 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.

注意: 在 Android 上,可以指定有效负载类型。如果类型无法转换为对象,有效负载将被封装并存储在 payload 键下:{payload: <provided value>}

Swift
class CameraViewModule: Module { public func definition() -> ModuleDefinition { View(CameraView.self) { Events( "onCameraReady" ) %%placeholder-start%%... %%placeholder-end%% } } } class CameraView: ExpoView { let onCameraReady = EventDispatcher() func callOnCameraReady() { onCameraReady([ "message": "Camera was mounted" ]); } }
Kotlin
class CameraViewModule : Module() { override fun definition() = ModuleDefinition { View(ExpoCameraView::class) { Events( "onCameraReady" ) %%placeholder-start%%... %%placeholder-end%% } } } class CameraView( context: Context, appContext: AppContext ) : ExpoView(context, appContext) { val onCameraReady by EventDispatcher() fun callOnCameraReady() { onCameraReady(mapOf( "message" to "Camera was mounted" )); } }

要在 JavaScript/TypeScript 中订阅这些事件,你需要将一个函数传递给原生视图,如下所示:

🌐 To subscribe to these events in JavaScript/TypeScript, you need to pass a function to the native view as shown:

TypeScript
import { requireNativeViewManager } from 'expo-modules-core'; const CameraView = requireNativeViewManager('CameraView'); export default function MainView() { const onCameraReady = event => { console.log(event.nativeEvent); }; return <CameraView onCameraReady={onCameraReady} />; }

提供的有效负载可在 nativeEvent 键下找到。

🌐 Provided payload is available under the nativeEvent key.

示例

🌐 Examples

Swift
public class MyModule: Module { public func definition() -> ModuleDefinition { Name("MyFirstExpoModule") Function("hello") { (name: String) in return "Hello \(name)!" } } }
Kotlin
class MyModule : Module() { override fun definition() = ModuleDefinition { Name("MyFirstExpoModule") Function("hello") { name: String -> return "Hello $name!" } } }

有关实际模块的更多示例,你可以参考 GitHub 上已经使用此 API 的 Expo 模块:

🌐 For more examples from real modules, you can refer to Expo modules that already use this API on GitHub:

expo-batterySwift
expo-cellular

Kotlin, Swift

expo-clipboard

Kotlin, Swift

expo-crypto

Kotlin, Swift

expo-deviceSwift
expo-hapticsSwift
expo-image-manipulatorSwift
expo-image-picker

Kotlin, Swift

expo-linear-gradient

Kotlin, Swift

expo-localization

Kotlin, Swift

expo-store-reviewSwift
expo-system-uiSwift
expo-video-thumbnailsSwift
expo-web-browser

Kotlin, Swift