Flutter: النشر عبر المنصات ومراجعة المتجر
📋 المتطلبات السابقة: يجب أن تكون قد أتقنت ما يلي أولًا
- الدرس 24: أتمتة CI/CD
1. ما ستتعلمه
2. قصة حقيقية عن تقديم مرفوض
3. عملية النشر عبر المنصات
4. نشر أندرويد
5. نشر iOS
6. نشر الويب
7. التوافق مع الخصوصية
8. مثال كامل: قائمة فحص نشر ShopApp
9. قائمة فحص إصدار ShopApp v1.2.3
بعد استعداد بوب باستخدام القائمة، اجتاز كل من App Store وGoogle Play من التقديم الأول، ونسخة الويب أُطلقت خلال ساعة واحدة.
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) التوقيع والبناء
▶ مثال: توليد Keystore
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
| العنصر | المتطلب |
|---|---|
| مُعرِّف التطبيق | اسم حزمة فريد com.shopapp.app |
| ملف AAB | حزمة إصدار موقعة |
| الأيقونة | PNG بحجم 512×512 |
| لقطات الشاشة | 2-8 لكل من هاتف + جهاز لوحي 7 بوصة |
| رابط سياسة الخصوصية | يجب أن يكون قابلًا للوصول |
| تصنيف المحتوى | أكمل استبيان IARC |
| الجمهور المستهدف | حدد النطاق العمري |
(1) إعدادات مطور Apple
| الخطوة | الإجراء |
|---|---|
| 1. التسجيل | Apple Developer Program (99$/سنة) |
| 2. الشهادة | أنشئ Distribution Certificate |
| 3. الملف الشخصي | أنشئ App Store Provisioning Profile |
| 4. مُعرِّف التطبيق | سجّل 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" | | فيديو معاينة | اختياري، ≤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 | سياسة الخصوصية / نموذج Data Safety / تصنيف المحتوى |
| App Store | سياسة الخصوصية / ATT / إقران توافق التشفير |
| الويب (PWA) | موافقة ملفات تعريف الارتباط GDPR / سياسة الخصوصية |
(2) قائمة فحص التوافق مع الخصوصية
فحوصات ما قبل الإطلاق
- [ ] صفحة سياسة الخصوصية منشورة والرابط قابل للوصول
- [ ] إقرانات جمع البيانات واضحة (الغرض، فترة الاحتفاظ، المشاركة مع أطراف ثالثة)
- [ ] يمكن للمستخدمين طلب تصدير وحذف بياناتهم
- [ ] شعار ملفات تعريف الارتباط / نافذة ATT مُنفذة
- [ ] حمايات خاصة للمستخدمين دون 13 سنة (COPPA)
- [ ] فحص تسميات أنواع بيانات App Store
- [ ] تقديم نموذج Google Play Data Safety
- [ ] اجتاز مراجعة التوافق مع GDPR / CCPA
قائمة فحص النشر عبر ثلاث منصات
(1) أندرويد
- [ ] مُعرِّف التطبيق فريد
- [ ] توقيع Keystore مُتحقق
- [ ] ملف AAB ≤150MB
- [ ] رابط سياسة الخصوصية قابل للوصول
- [ ] استبيان contentRating مكتمل
- [ ] Target API Level 34
- [ ] اختبار AAB على جهاز عبر
bundletool - [ ] رفع AAB لـ Play Console
- [ ] ملء قائمة المتجر (لقطات شاشة، وصف، سياسة خصوصية)
- [ ] بدء الإطلاق التدريجي 10% ← 50% ← 100%
(2) iOS
- [ ] شهادة التوزيع صالحة
- [ ] الملف الشخصي يتطابق مع Bundle ID
- [ ] بناء IPA:
flutter build ipa --release - [ ] رفع IPA عبر Transporter
- [ ] ملء قائمة App Store Connect
- [ ] التقديم للمراجعة (اسمح بـ 24-48 ساعة)
(3) الويب
- [ ] بناء:
flutter build web --wasm - [ ] اختبار في Chrome/Safari/Firefox
- [ ] نشر على Firebase Hosting
- [ ] التحقق من عمل توجيه URL
- [ ] اختبار مطالبة تثبيت PWA
(4) ما بعد الإطلاق
- [ ] إنشاء GitHub Release بملاحظات
- [ ] إشعار الفريق عبر Slack
- [ ] مراقبة Crashlytics لمدة 24 ساعة
- [ ] مراقبة تقييمات المستخدمين
❓ أسئلة شائعة
س ما الفرق بين AAB وAPK؟
ج AAB (Android App Bundle) هو تنسيق الرفع — Google Play يُولد تلقائيًا APKs محسنة منه. AABs أصغر لأنها تتضمن فقط الموارد التي يحتاجها الجهاز.
س كم تستغرق مراجعة App Store عادة؟
ج التقديمات الأولى تستغرق عادة 24-48 ساعة، التحديثات عادة 12-24 ساعة. إعادة التقديم بعد الرفض تتطلب انتظارًا آخر.
س كيف أدير versionCode وversionName؟
ج versionCode يجب أن يكون عددًا صحيحًا متزايدًا (+1 لكل إصدار)؛ versionName مرئي للمستخدم (مثل 1.2.3). استخدم اصطلاحات SemVer.
س هل نسخة الويب تحتاج تسجيل ICP؟
ج الخوادم في الصين القارية تحتاج تسجيل ICP. الخوادم الخارجية (Firebase/Vercel) لا تحتاج.
س ماذا يتطلب التوافق مع GDPR؟
ج 1) إظهار موافقة الخصوصية عند أول تشغيل؛ 2) توفير خيار حذف البيانات؛ 3) رابط سياسة الخصوصية؛ 4) تقليل جمع البيانات؛ 5) يمكن للمستخدمين إلغاء الموافقة.
س ماذا أفعل إذا رفض App Store تقديمي؟
ج اقرأ سبب الرفض بعناية، أصلح المشاكل، وأعد التقديم. أسباب شائعة: سياسة خصوصية مفقودة، نافذة 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)
📝 تمارين
- أساسي (الصعوبة ⭐): أنشئ Keystore أندرويد لـ ShopApp، أعد توقيع build.gradle، وابنِ AAB إصدار بنجاح.
- متوسط (الصعوبة ⭐⭐): أكمل تحضير نشر ShopApp ثنائي المنصة — توقيع AAB أندرويد + إعدادات شهادة iOS، واكتب قائمة فحص نشر متعددة المنصات.
- متقدم (الصعوبة ⭐⭐⭐): نفذ عملية نشر ShopApp الكاملة عبر المنصات (أندرويد + iOS + الويب)، بما في ذلك إقرانات التوافق مع الخصوصية، توليد لقطات شاشة متعددة القنوات، وكتابة وصف App Store.