Reference version

This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.

Expo AgeRange

一个通过 Android 上的 Play Age Signals API 和 iOS 上的 Declared Age Range 框架提供年龄范围信息访问功能的库

Android
iOS
Included in Expo Go

expo-age-range 提供访问用户年龄范围信息的功能。在 Android 上,它使用 Google 的 Play Age Signals API,在 iOS 上使用 Apple 的 Declared Age Range framework。

此库允许你向应用用户请求年龄范围信息,以帮助你遵守适龄内容法规(例如美国德克萨斯州的相关法规),并在应用中提供适龄体验。

限制

我们建议在真实设备上测试此功能,因为模拟器运行时可能无法按预期工作。

安装

Terminal
- npx expo install expo-age-range
- yarn expo install expo-age-range
- pnpm expo install expo-age-range
- bun expo install expo-age-range

If you are installing this in an existing React Native app, make sure to install expo in your project.

在应用配置中进行配置

设置 iOS 项目

要在 iOS 上使用年龄范围 API,你需要使用 Xcode 26.0 或更高版本构建项目,但我们建议使用最新版本的 Xcode,以便访问最新的 API。

需要使用 com.apple.developer.declared-age-range 权利。将其添加到你的应用配置文件中:

app.json
{ "expo": { "ios": { "entitlements": { "com.apple.developer.declared-age-range": true } } } }
Are you using this library in an existing React Native app?

对于现有的 React Native 项目,将该权利添加到项目的 ios/[app]/[app].entitlements 文件中:

<key>com.apple.developer.declared-age-range</key> <true/>

使用

在 Android 上,Play Age Signals 仅在用户同意分享年龄范围时报告该信息。请先调用 requestAgeSignalsAccessAsync,并仅在其解析为 'SHARED' 时继续。在 iOS 上,同意提示是 requestAgeRangeAsync 的一部分,因此该调用会解析为 null,下面的示例会继续执行。

Age Range usage
import * as AgeRange from 'expo-age-range'; import { useState } from 'react'; import { StyleSheet, Text, View, Button } from 'react-native'; export default function App() { const [result, setResult] = useState<AgeRange.AgeRangeResponse | { error: string } | null>(null); const requestAgeRange = async () => { try { // On Android, ask the user to share their age signals first. Resolves with null on iOS. const status = await AgeRange.requestAgeSignalsAccessAsync(); if (status !== null && status !== 'SHARED') { setResult({ error: `Age signals are not shared: ${status}` }); return; } const ageRange = await AgeRange.requestAgeRangeAsync({ threshold1: 10, threshold2: 13, threshold3: 18, }); setResult(ageRange); } catch (error) { setResult({ error: error instanceof Error ? error.message : String(error) }); } }; return ( <View style={styles.container}> <Button title="Request age range" onPress={requestAgeRange} /> {result && ( <Text style={styles.result}> {'error' in result ? `Error: ${result.error}` : `Lower age bound: ${result.lowerBound}`} </Text> )} </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', padding: 20, }, result: { marginTop: 20, fontSize: 16, }, });

在 Android 上测试年龄信号

Google Play 仅向已启用年龄信号功能的帐户报告年龄信号,因此你的测试帐户可能没有所需的年龄范围。请改用 setFakeAgeSignals 来伪造年龄信号,该方法通过 Google Play 的 FakeAgeSignalsManager 实现。

只有可调试构建才能伪造信号,因为伪造的信号会让应用中的任何代码绕过年龄限制。对于不可调试的构建,传入任何非 null 的值都会抛出 ERR_AGE_RANGE_FAKE_SIGNALS_NOT_DEBUGGABLE。

你的请求不会改变,只有请求报告的内容会改变:

// Report a supervised 13 to 15 year old with a change waiting for approval. AgeRange.setFakeAgeSignals({ ageSignalsStatus: 'SHARED', lowerBound: 13, upperBound: 15, ageRangeSource: 'TIER_B', significantChangeStatus: 'PENDING', }); const status = await AgeRange.requestAgeSignalsAccessAsync(); const { lowerBound } = await AgeRange.requestAgeRangeAsync({ threshold1: 18 }); // Report the real signals again. AgeRange.setFakeAgeSignals(null);

要伪造失败,请传入错误代码,而不是信号:

AgeRange.setFakeAgeSignals({ errorCode: -4 });

其他资源

API

import * as AgeRange from 'expo-age-range';

Methods

AgeRange.getRequiredRegulatoryFeaturesAsync()

iOS 26.4+

Returns the set of regulatory features that the OS reports as required for the current user.

Use this to discover which age-assurance obligations apply.

Resolves with null on iOS earlier than 26.4 and on Android and web — treat null as "unknown" rather than "no features required".

AgeRange.isEligibleForAgeFeaturesAsync()

iOS 26.2+

Asks the OS whether age-assurance regulation applies to the current user. Apple uses this to signal that the account region is covered by a law such as Utah's or Louisiana's age-assurance requirements, so apps can avoid gating users in jurisdictions where the rules do not apply.

  • Resolves with true only when Apple confirms regulation applies.
  • Resolves with false when the OS confirms regulation does not apply.
  • Resolves with null on iOS earlier than 26.2, and on Android and web. Treat null as "unknown" rather than a definitive false.
  • Rejects when the request fails — see AgeRangeService.Error for more information. Treat rejection as "unknown" and fall through to requestAgeRangeAsync or your own gating logic.

Recommended pattern: call this first and only prompt the user for their age range when the result is not false. When it is false, the user is outside a regulated jurisdiction and you can skip the age gate entirely.

Returns:
Promise<boolean | null>

Example

try { const eligible = await isEligibleForAgeFeaturesAsync(); if (eligible === false) { // Regulation does not apply — no age gate needed. return; } } catch { // Treat errors as "unknown" and fall through to the prompt below or your own gating logic. } const ageRange = await requestAgeRangeAsync({ threshold1: 18 });

AgeRange.requestAgeRangeAsync(options)

Android
iOS 26.0+
ParameterType
optionsAgeRangeRequest

Prompts the user to share their age range with the app. Responses may be cached by the OS for future requests.

A promise that resolves with user's age range response, or rejects with an error. The user needs to be signed in on the device to get a valid response. When not supported (earlier than iOS 26 and web), the call returns lowerBound: 18, which is equivalent to the response of an adult user.

On Android, call requestAgeSignalsAccessAsync first and only call this function when it resolves with 'SHARED'. Play Age Signals reports every field as null otherwise.

AgeRange.requestAgeSignalsAccessAsync()

Android

Asks the user to consent to sharing their age signals, showing the Play Age Signals in-app age sharing consent screen. Play Age Signals requires this before requestAgeRangeAsync: age signals are only reported while the status is 'SHARED'.

  • Resolves with 'SHARED' when the user agrees to share their age signals. Only then does requestAgeRangeAsync report an age range.
  • Resolves with 'NOT_SHARED' when the user does not agree. requestAgeRangeAsync reports every field as null until the user consents.
  • Resolves with 'VERIFICATION_REQUIRED' when the user's age is unknown and they are in a region where age verification is mandatory. Ask the user to visit the Play Store to resolve their status.
  • Resolves with null when Play Age Signals reports no status, and on iOS and web. On iOS the consent prompt is part of requestAgeRangeAsync itself, so there is nothing separate to call.
  • Rejects when the request fails.
Returns:
Promise<AgeSignalsStatus | null>

AgeRange.setFakeAgeSignals(fake)

Android
ParameterTypeDescription
fakeFakeAgeSignals | null

The signals or an error to report, or null to report the real signals.


Fakes the age signals that requestAgeRangeAsync and requestAgeSignalsAccessAsync report, using Play Age Signals FakeAgeSignalsManager. Pass null to go back to the real signals.

Only debuggable builds can fake signals. Passing anything other than null in a build that is not debuggable throws.

Returns:
void

Example

// A supervised 13 to 15 year old with a change waiting for approval. setFakeAgeSignals({ ageSignalsStatus: 'SHARED', lowerBound: 13, upperBound: 15, ageRangeSource: 'TIER_B', significantChangeStatus: 'PENDING', });

AgeRange.showSignificantUpdateAcknowledgmentAsync(updateDescription)

iOS 26.4+
ParameterTypeDescription
updateDescriptionstring

A description of the significant update to show to the user.


Displays a system-provided interface for people to acknowledge a significant app update.

Only on iOS 26.4+, this presents an update acknowledgement dialog and resolves once the user confirms it, or rejects with an error. On unsupported platforms this resolves immediately without showing any UI.

Call getRequiredRegulatoryFeaturesAsync first to determine whether the user actually needs to acknowledge a significant change — only invoke this function when the returned features include 'significantAppChangeRequiresAdultNotification'. Doing so avoids prompting users who are not subject to the regulation.

Returns:
Promise<void>

Types

AgeRangeRegulatoryFeature

iOS 26.4+

Literal type: string

A regulatory feature that your app may need to support for the current user.

Mirrors AgeRangeService.RegulatoryFeature.

Acceptable values are: 'declaredAgeRangeRequired' | 'significantAppChangeRequiresAdultNotification' | 'significantAppChangeRequiresParentalConsent'

AgeRangeRequest

iOS

Options for requesting age range information from the user.

PropertyTypeDescription
threshold1number

The required minimum age for your app.

threshold2(optional)number

An optional additional minimum age for your app.

threshold3(optional)number

An optional additional minimum age for your app.

AgeRangeResponse

Android
iOS

Response containing the user's age range information.

Contains age boundaries and platform-specific metadata.

PropertyTypeDescription
activeParentalControls(optional)string[]
Only for: 
iOS

List of parental controls enabled and shared as a part of age range declaration.

ageRangeDeclaration(optional)'selfDeclared' | 'guardianDeclared' | 'confirmed' | null
Only for: 
iOS

Indicates how the age range was declared:

  • 'selfDeclared' — declared by the user themselves.
  • 'guardianDeclared' — declared by someone else (parent, guardian, or Family Organizer in a Family Sharing group).
  • 'confirmed' — confirmed by the system (for example, verified against a government ID or payment method). Only reported on iOS 26.2+.

See ageRangeSource for the Android equivalent.

ageRangeSource(optional)'TIER_A' | 'TIER_B' | 'TIER_C' | 'TIER_D' | null
Only for: 
Android

The methodology Play Age Signals used to determine the user's age range:

  • 'TIER_A' — the user self-declared their age.
  • 'TIER_B' — a parent or guardian manages the user's age.
  • 'TIER_C' — the age was assessed using a credit card, email address, selfie assessment, government ID, or tax ID.
  • 'TIER_D' — the age was checked using a combination of government ID and selfie assessment, or a digital ID.

null when the sharing status reported by requestAgeSignalsAccessAsync is 'NOT_SHARED' or 'VERIFICATION_REQUIRED'.

installId(optional)string | null
Only for: 
Android

An ID assigned to supervised user installs by Google Play, used to notify you of revoked app approval.

lowerBoundnumber | null

The lower limit of the person’s age range.

mostRecentApprovalDate(optional)number | null
Android

The effective date (timestamp) of the most recent significant change that was approved.

significantChangeApprovalDate(optional)number | null
Only for: 
Android

The effective date (timestamp) of the most recently approved significant change.

null when no changes have been recorded for your app.

significantChangeStatus(optional)'APPROVED' | 'PENDING' | 'DECLINED' | null
Only for: 
Android

Whether a guardian has approved the significant changes recorded for your app:

  • 'APPROVED' — the most recent significant change, and all earlier ones, are approved.
  • 'PENDING' — one or more significant changes are waiting for approval.
  • 'DECLINED' — approval was denied for one or more significant changes.

null for unsupervised accounts, and for supervised accounts with no significant changes yet.

upperBoundnumber | null

The upper limit of the person’s age range.

AgeSignalsStatus

Android

Literal type: string

The sharing status of age signals, returned by requestAgeSignalsAccessAsync.

Acceptable values are: 'SHARED' | 'NOT_SHARED' | 'VERIFICATION_REQUIRED'

FakeAgeSignals

Android

Fake age signals for setFakeAgeSignals: either a response or an error, never both.

The response fields match AgeRangeResponse, with ageSignalsStatus for requestAgeSignalsAccessAsync. Omitted fields are reported as null.

Type: object shaped as below:

PropertyTypeDescription
ageRangeSource(optional)'TIER_A' | 'TIER_B' | 'TIER_C' | 'TIER_D' | null
-
ageSignalsStatus(optional)AgeSignalsStatus | null
-
errorCode(optional)never
-
installId(optional)string | null
-
lowerBound(optional)number | null
-
significantChangeApprovalDate(optional)number | null
-
significantChangeStatus(optional)'APPROVED' | 'PENDING' | 'DECLINED' | null
-
upperBound(optional)number | null
-

Or object shaped as below:

PropertyTypeDescription
ageRangeSource(optional)never
-
ageSignalsStatus(optional)never
-
errorCodenumber

The Google Play Age Signals error code to fail both requests with.

installId(optional)never
-
lowerBound(optional)never
-
significantChangeApprovalDate(optional)never
-
significantChangeStatus(optional)never
-
upperBound(optional)never
-

错误代码

可在原生模块抛出的任何错误的 code 属性中获取。有关 Android 特定的错误代码,请参阅 Use Play Age Signals API docs 中的“错误代码参考”。

CodePlatformDescription
ERR_AGE_RANGE_USER_DECLINED
iOS
用户拒绝分享其年龄范围。
ERR_AGE_RANGE_NOT_AVAILABLE
iOS
年龄范围不可用。最可能的原因是用户未在设备上登录其 Apple 帐户。
ERR_AGE_RANGE_INVALID_REQUEST
iOS
提供的参数无效。年龄范围之间需要至少相差 2 年。
ERR_AGE_RANGE_TASK_CANCELLED
Android
用户关闭了 Play Age Signals 年龄分享同意界面。
ERR_AGE_RANGE_FAKE_SIGNALS_CONFLICT
Android
setFakeAgeSignals 同时传入了 errorCode 和年龄信号。
ERR_AGE_RANGE_FAKE_SIGNALS_NOT_DEBUGGABLE
Android
setFakeAgeSignals 被要求在不可调试的构建中伪造信号。