Flutter: マルチプラットフォーム公開とストア審査
📋 前提条件: 以下を先にマスターしている必要があります
- レッスン24:CI/CD自動化
1. このレッスンで学ぶこと
2. 審査却下のリアルなストーリー
3. マルチプラットフォーム公開プロセス
4. Android公開
5. iOS公開
6. Web公開
7. プライバシー準拠
8. 完全な例:ShopApp公開チェックリスト
9. ShopAppリリースチェックリスト v1.2.3
Bobはチェックリストで準備した後、App StoreとGoogle Playの両方が初回提出で通過し、Web版は1時間以内にローンチされました。
graph TD
REL[Release Process] --> ANDROID2[Android AAB]
REL --> IOS2[iOS IPA]
REL --> WEB2[Web PWA]
ANDROID2 --> PC[Play Console]
IOS2 --> ASC[App Store Connect]
WEB2 --> FH[Firebase Hosting / Vercel]
PC --> REVIEW1[Google Review]
ASC --> REVIEW2[Apple Review]
REVIEW1 --> LIVE1[Live on Google Play]
REVIEW2 --> LIVE2[Live on App Store]
FH --> LIVE3[Live on Web]
(1) 署名とビルド
▶ サンプル:キーストアの生成
BASH
# Generate signing keystore
keytool -genkey -v -keystore shopapp-release.jks \
-keyalg RSA -keysize 2048 -validity 10000 \
-alias shopapp
# Verify keystore
keytool -list -v -keystore shopapp-release.jks -alias shopapp
build.gradle署名設定
GROOVY
// android/app/build.gradle
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
defaultConfig {
applicationId "com.shopapp.app"
minSdkVersion 21
targetSdkVersion 34
versionCode 42
versionName "1.2.3"
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['keyFile'] ? file(keystoreProperties['keyFile']) : null
storePassword keystoreProperties['keyPassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
ビルドと公開
BASH
# Build release AAB (Android App Bundle)
flutter build appbundle --release
# Output: build/app/outputs/bundle/release/app-release.aab
# Build release APK (for testing)
flutter build apk --release
# Upload to Play Console manually or via CI/CD
(2) Play Consoleローンチチェックリスト
| 項目 | 要件 |
|---|---|
| アプリケーションID | 一意のパッケージ名 com.shopapp.app |
| AABファイル | 署名済みリリースバンドル |
| アイコン | 512x512 PNG |
| スクリーンショット | スマホ + 7インチタブレット各2-8枚 |
| プライバシーポリシーURL | アクセス可能であること |
| コンテンツレーティング | IARCアンケート完了 |
| ターゲット層 | 年齢層を選択 |
(1) Apple Developer設定
| ステップ | 作業 |
|---|---|
| 1. 登録 | Apple Developer Program($99/年) |
| 2. 証明書 | Distribution Certificateを作成 |
| 3. プロファイル | App Store Provisioning Profileを作成 |
| 4. App ID | Bundle Identifierを登録 |
| 5. アプリレコード | App Store Connectでアプリを作成 |
Xcodeビルド設定
XML
<!-- ios/Runner/Info.plist -->
`<key>`CFBundleShortVersionString</key>
`<string>`1.2.3</string>
`<key>`CFBundleVersion</key>
`<string>`42</string>
`<key>`NSAppTransportSecurity</key>
`<dict>`
`<key>`NSAllowsArbitraryLoads</key>
`<false/>`
`</dict>`
`<key>`NSUserTrackingUsageDescription</key>
`<string>`We use tracking to personalize your shopping experience.</string>
ビルドとアップロード
BASH
# Build IPA
flutter build ipa --release \
--export-options-plist=ios/ExportOptions.plist
# Output: build/ios/ipa/shopapp.ipa
# Upload via Xcode or command line
xcrun altool --upload-app \
--type ios \
--file build/ios/ipa/shopapp.ipa \
--apiKey YOUR_API_KEY \
--apiIssuer YOUR_ISSUER_ID
# Or via Transporter app
(2) App Store Connectローンチチェックリスト
| Bundle ID | Xcodeプロジェクトと一致すること | | IPAファイル | リリースビルド | | スクリーンショット | 6.7"/6.5"/5.5"の3サイズ | | プレビュー動画 | 任意、30秒以内 | | 説明文 | 4000文字以内 |
▶ サンプル:Appleプライバシーリスト準拠
DART
import 'package:flutter/material.dart';
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
// Dependencies: app_tracking_transparency: ^0.2.0, firebase_analytics: ^10.0.0 (add to dependencies)
Future<void> requestTrackingPermission() async {
final status = await AppTrackingTransparency.requestTrackingAuthorization();
if (status == TrackingStatus.authorized) {
await FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(true);
} else {
await FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(false);
}
}
出力:
TEXT
App Tracking Transparency dialog displayed
Status: authorized (or denied)
Analytics collection enabled/disabled accordingly
(1) 準拠要件比較
| プラットフォーム | 必須項目 |
|---|---|
| Google Play | プライバシーポリシー / データセーフティフォーム / コンテンツレーティング |
| App Store | プライバシーポリシー / ATT / 暗号化準拠宣言 |
| Web (PWA) | GDPR Cookie同意 / プライバシーポリシー |
(2) プライバシー準拠チェックリスト
リリース前チェック
- [ ] プライバシーポリシーページがデプロイ済みでリンクがアクセス可能
- [ ] データ収集宣言が明確(目的、保持期間、第三者共有)
- [ ] ユーザーがデータエクスポートと削除を要求可能
- [ ] Cookieバナー / ATTポップアップが実装済み
- [ ] 13歳未満ユーザーの特別保護(COPPA)
- [ ] App Storeデータタイプラベルが確認済み
- [ ] Google Playデータセーフティフォームが提出済み
- [ ] GDPR / CCPA準拠レビューが通過
3プラットフォーム公開チェックリスト
(1) Android
- [ ] アプリケーションIDが一意
- [ ] キーストア署名が確認済み
- [ ] AABファイル ≤150MB
- [ ] プライバシーポリシーURLがアクセス可能
- [ ] contentRatingアンケートが完了
- [ ] Target API Level 34
- [ ]
bundletoolでデバイス上でAABをテスト - [ ] AABをPlay Consoleにアップロード
- [ ] ストア掲載情報を記入(スクリーンショット、説明、プライバシーポリシー)
- [ ] 10% → 50% → 100%の段階的ロールアウトを開始
(2) iOS
- [ ] Distribution証明書が有効
- [ ] Provisioning ProfileがBundle IDと一致
- [ ] IPAビルド:
flutter build ipa --release - [ ] TransporterでIPAをアップロード
- [ ] App Store Connectの掲載情報を記入
- [ ] 審査に提出(24-48時間を許容)
(3) Web
- [ ] ビルド:
flutter build web --wasm - [ ] Chrome/Safari/Firefoxでテスト
- [ ] Firebase Hostingにデプロイ
- [ ] URLルーティングが動作することを確認
- [ ] PWAインストールプロンプトをテスト
(4) リリース後
- [ ] GitHub Releaseをノート付きで作成
- [ ] Slackでチームに通知
- [ ] Crashlyticsを24時間監視
- [ ] ユーザーレビューを監視
❓ よくある質問
Q AABとAPKの違いは何ですか?
A AAB(Android App Bundle)はアップロード形式 — Google Playが自動的に最適化されたAPKを生成します。AABはデバイスに必要なリソースのみを含むためサイズが小さくなります。
Q App Store審査は通常どのくらいかかりますか?
A 初回提出は24-48時間、更新は通常12-24時間。却下後の再提出は再度待ち時間が必要。
Q versionCodeとversionNameはどう管理すべきですか?
A versionCodeは増分する整数(リリースごとに+1)、versionNameはユーザーに表示される値(例:1.2.3)。SemVer規約を使用してください。
Q Web版にICP届け出は必要ですか?
A 中国本土のサーバーはICP届け出が必要。海外サーバー(Firebase/Vercel)は不要。
Q GDPR準拠には何が必要ですか?
A 1) 初回起動時にプライバシー同意を表示、2) データ削除オプションの提供、3) プライバシーポリシーリンク、4) データ収集の最小化、5) ユーザーが同意を取り消し可能。
Q App Store審査で却下された場合は?
A 却下理由を慎重に読み、問題を修正して再提出。一般的な却下理由:プライバシーポリシー未記載、ATTポップアップ未表示、クラッシュ、コンテンツポリシー違反。
📖 まとめ
(1) ▶ サンプル:バージョン番号リリースサイクル
TEXT
1.0.0 → MVP launch (first release)
1.0.1 → Urgent bug fix (patch)
1.1.0 → New feature (minor)
1.2.3 → ShopApp release (minor + patch)
2.0.0 → Major refactoring (major)
出力:
TEXT
SemVer: MAJOR.MINOR.PATCH
- MAJOR breaking changes
- MINOR compatible additions
- PATCH compatible fixes
(2) ▶ サンプル:Firebase Crashlytics連携
DART
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'dart:ui' show PlatformDispatcher;
// Dependencies: firebase_core: ^2.0.0, firebase_crashlytics: ^3.0.0 (add to dependencies)
// ShopApp comes from project lib/app.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FlutterError.onError = (details) {
FirebaseCrashlytics.instance.recordFlutterFatalError(details);
};
PlatformDispatcher.instance.onError = (error, stack) {
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
return true;
};
runApp(const ShopApp());
}
出力:
TEXT
Crashlytics initialized
Fatal errors auto-reported
Non-fatal errors auto-reported
(3) ▶ サンプル:リリース監視ダッシュボード
BASH
# Firebase Crashlytics
firebase functions:log --only publishMonitor
# Expected output:
# {"status":"ok","version":"1.2.3","crashRate":"0.05%"}
出力:
TEXT
Crash rate after rollout:
- 1h: 0.05% (baseline 0.10%)
- 24h: 0.04% (healthy)
- ANR rate: 0.01% (acceptable)
📝 練習問題
- 基本 (⭐):ShopApp用のAndroidキーストアを生成し、build.gradle署名を設定し、リリースAABのビルドに成功してください。
- 中級 (⭐⭐):ShopAppのデュアルプラットフォーム公開準備を完了してください — Android AAB署名 + iOS証明書設定、マルチプラットフォーム公開チェックリストを作成。
- チャレンジ (⭐⭐⭐):ShopAppの完全なクロスプラットフォーム公開プロセス(Android + iOS + Web)を実装してください。プライバシー準拠宣言、マルチチャンネルスクリーンショット生成、App Store説明文の作成を含む。