App Store Connect を開いて、いつもどおり軽微な修正を出そうとした夜のことでした。年齢別レーティングの画面に、見覚えのない一問が増えていました。あなたのアプリはソーシャルメディア機能を持っていますか、という質問です。
個人開発で出しているのは壁紙のアプリとヒーリング音源のアプリで、SNS のようなものは作っておりません。ですから最初は、自分には関係のない質問だと思って読み飛ばしかけました。
けれど、そこで手が止まりました。壁紙アプリには共有シートがあります。お気に入りの一覧もあります。それは「ソーシャルメディア機能」に入るのでしょうか。Apple の定義を読み直すまで、私はこの一問に自信を持って答えられませんでした。
読み直したうえで実際に答えを保存し、その過程で気づいたことを順に書き残します。Rork で作ったアプリを 2026年9月以降にストアへ出す方には、そのまま関わってくる話です。
Apple が引いた線は「共有」ではなく「広がり方」にあります
Apple は「ソーシャルメディア機能」を、ユーザーが作ったコンテンツを再配布したり、増幅したり、そこに関与したりできることと定義しています。ただし条件が付いています。ソーシャルフィード、あるいはそれに類する発見の仕組みを通じて、目に見える形で多くの人へ広がること、という条件です(Introducing Time Allowances)。
この後半こそが線引きの本体だと、私は読んでいます。「他人が作ったものを扱えるか」だけでは足りず、「それが見知らぬ多くの人の画面に並ぶ経路があるか」までを見ています。
判断の基準は、共有できるかどうかではなく、他人が作ったものが見知らぬ人の画面に並ぶかどうかです。
この読み方で自分のアプリの画面を仕分けると、迷いがだいぶ減りました。参考までに、私が並べた分け方を載せておきます。あくまで私の読み方であり、Apple の裁定ではありませんので、際どい機能をお持ちの場合は App Review へ照会なさるほうが確実です。
| アプリの機能 | 私の読み | 理由 |
|---|---|---|
| OS の共有シートで1対1に送る | 当たらない見込み | 受け手が1人で、アプリ内に広がる経路がありません |
| 自分の端末内だけのお気に入り | 当たらない見込み | 他人が作ったものを扱っていません |
| 他のユーザーの投稿が並ぶ一覧 | 当たる | 発見の仕組みそのものです |
| 投稿への公開コメント・返信 | 当たる | 関与が他の閲覧者にも見えます |
| ユーザー投稿の人気ランキング | 当たる | 増幅の経路として働きます |
| 問い合わせフォームから運営だけに届く投稿 | 当たらない見込み | 他のユーザーへ広がりません |
App Store Connect でどのカテゴリを選んでいるかは、この判定に関係しません。ユーティリティでも写真でも、フィードを持てば同じ扱いになります。
「はい」と答えた時点で、年齢の下限が 13+ になります
ここが、質問の重さをいちばん実感した部分でした。
ソーシャルメディア機能を持つと答えたアプリは、Time Allowance の Social Media カテゴリに入り、あわせて年齢別レーティングの下限が 13+ になります。他の設問でどれだけ穏やかな内容だと答えていても、この一問が下限を押し上げます。
さらに、App Store の製品ページに Social Media のコンテンツ記述子が表示されるようになります(Age rating questionnaire now includes social media questions)。ストアの見え方が変わる、という意味では表示上の影響もあります。
小さなコミュニティ機能を試しに足そうとしていた方にとっては、判断が変わりうる変更だと感じています。機能を1つ増やすことと、対象年齢を1段上げることが、同じ操作で起きるためです。
「13歳未満には無効」を選ぶと、年齢範囲の取得が付いてきます
Apple は逃げ道も用意しています。ソーシャルメディア機能はあるが 13歳未満に対しては無効にしている、と答える選択肢です。これを選ぶと 13歳未満のユーザーについては Social Media カテゴリから外れ、レーティングも他の設問の回答で決まりますので、13+ を下回る余地が残ります。
ただし条件があります。この選択肢を取る場合、最低でも Declared Age Range API を使ってユーザーの年齢範囲を確認する必要があると Apple は明記しています。13歳以上のユーザーについては、引き続き Social Media カテゴリに入ります。
逃げ道に見えるほうの選択肢が、実装の宿題は重いのです。 私はここを読み違えていて、チェックの付け替えだけで済む話だと思っておりました。実際には、年齢を確かめる仕組みをアプリの中に持つ、という約束をしたことになります。
expo-age-range で年齢範囲を受け取るところまで
Rork の標準版が生成するのは Expo 経由の React Native です。幸い、この用途の Expo モジュールが用意されています。expo-age-range は iOS で Apple の Declared Age Range を、Android で Google の Play Age Signals を呼び分けてくれます(Expo AgeRange のドキュメント)。
まず導入と、iOS のエンタイトルメントです。エンタイトルメントを書き忘れると、実機で呼び出した瞬間に失敗しますので、ここは先に入れておくほうが安全です。
npx expo install expo-age-range{
"expo": {
"ios": {
"entitlements": {
"com.apple.developer.declared-age-range": true
}
}
}
}呼び出し側です。iOS と Android で同意の取り方が違うため、そこを吸収する形で書きます。
import * as AgeRange from 'expo-age-range';
// 13歳未満かどうかだけを知りたい場合の最小構成です。
// しきい値は 2 歳以上離す必要があります(近すぎると INVALID_REQUEST になります)。
export async function resolveAgeGate(): Promise<'under13' | 'over13' | 'unknown'> {
try {
// iOS 26.2 以降のみ true/false を返します。null は「不明」であって「規制対象外」ではありません。
const eligible = await AgeRange.isEligibleForAgeFeaturesAsync();
if (eligible === false) {
// 規制の対象外だと OS が明言した場合だけ、年齢の確認を省けます。
return 'over13';
}
} catch {
// 失敗は「不明」として扱い、下の確認へ進みます。
}
try {
// Android は先に同意画面が要ります。iOS はここが null で返り、そのまま進みます。
const status = await AgeRange.requestAgeSignalsAccessAsync();
if (status !== null && status !== 'SHARED') {
return 'unknown';
}
const range = await AgeRange.requestAgeRangeAsync({
threshold1: 13,
threshold2: 16,
threshold3: 18,
});
if (range.lowerBound === null) {
return 'unknown';
}
return range.lowerBound >= 13 ? 'over13' : 'under13';
} catch (error) {
// ERR_AGE_RANGE_USER_DECLINED / ERR_AGE_RANGE_NOT_AVAILABLE などが飛んできます。
// 共有を断られた場合も「不明」です。拒否=成人ではありません。
return 'unknown';
}
}
// 期待する戻り値の例
// 13歳未満の子どものアカウント → 'under13'
// 共有をユーザーが断った → 'unknown'
// Apple アカウント未サインイン → 'unknown'iOS 側は Xcode 26.0 以降でのビルドが必要です。シミュレータでは期待どおりに動かないことがありますので、確認は実機でお願いします。私はここを後回しにして、シミュレータの結果を信じかけました。
対応していない環境では lowerBound が 18 で返ります
これが、ドキュメントを流し読みしていたら踏んでいたであろう落とし穴でした。
requestAgeRangeAsync は、対応していない環境(iOS 26 より前や web)では lowerBound: 18 を返します。成人のユーザーが答えた場合と同じ値です。つまり、素朴に「13以上なら機能を開く」と書いてしまうと、古い OS の端末では全員に開いてしまいます。
// ❌ 未対応環境では全員が 18 として通ってしまいます
if (range.lowerBound >= 13) {
enableCommunityFeed();
}
// ✅ 「13歳未満でないと確認できた」ときだけ開きます
const gate = await resolveAgeGate();
if (gate === 'over13') {
enableCommunityFeed();
} else {
// 'under13' も 'unknown' も、フィードは閉じたままにします。
disableCommunityFeed();
}年齢の判定は「13歳以上か」ではなく「13歳未満でないと確認できたか」で書きます。 同じことを言っているようで、不明のときの挙動が逆になります。安全側に倒れるのは後者だけです。
isEligibleForAgeFeaturesAsync の戻り値も同じ性質を持っています。iOS 26.2 より前と Android では null が返り、これは「規制の対象外」ではなく「分からない」です。null を偽として扱うと、確認が要る相手をそのまま通してしまいます。
私が自分のアプリで見た画面と、出した答え
定義を読み終えたあと、実際にやったのは単純な作業でした。アプリを1本ずつ開いて、他人が作ったものが並ぶ画面がいくつあるかを数えただけです。
壁紙のアプリには、共有シートと、端末内に閉じたお気に入りがありました。ユーザーが投稿した画像を並べる場所はありませんでした。ヒーリング音源のアプリも同じで、他人の録音が流れてくる経路はありません。数え終えて、どちらも「いいえ」で保存しました。
拍子抜けするほど短い作業でしたが、意味があったのは答えそのものより、なぜそう答えたかを画面の名前つきで書き残したことのほうでした。次の更新でまた同じ問いに向き合うとき、あるいは将来コミュニティ機能を足したくなったときに、どこを見直せばよいかが一行で分かります。
ストアの提出要件が変わるたびに、いちばん時間を取られるのは実装ではなく、既存のアプリすべての情報を揃え直す作業でした。今回の質問も、2026年9月からは新規アプリだけでなく更新の提出にも回答が必須になります。代替配布のための公証申請でも同じです。バグ修正を1つ出すだけの日に、この質問に初めて向き合うのは、あまり気持ちのよいものではありません。
公開前に Apple 側で自分が済ませておく手続きは他にもありますので、初めて出される方はRork のアプリを公開する前に、Apple 側で自分が済ませておく手続きもあわせてご覧ください。提出物の申告まわりではRork 生成 Expo アプリの Privacy Manifest を SDK 連鎖まで監査する手順が、実際に差し戻された場合はRork で作ったアプリが App Store でリジェクトされた時の対処辞典が近い話になります。
まずは App Store Connect の年齢別レーティングの画面を開いて、増えた一問を読むところからにしてみてください。答えを保存する前に、自分のアプリのどの画面に他人が作ったものが並ぶのかを、一度だけ数えていただければと思います。私はその数分で、迷いが要らないことに気づけました。
最後までお読みくださり、ありがとうございました。