Fastlane: Make and Ship App Store Screenshots (2026 Guide)
أداة fastlane هي عبارة عن سلسلة أدوات بلغة Ruby لأتمتة الأجزاء المملة من عملية شحن تطبيقات iOS وAndroid. وتغطي إجراءاتها الثلاثة المتعلقة بلقطات الشاشة — snapshot وframeit وdeliver — خط الأنابيب بالكامل: قم بتشغيل تطبيقك داخل اختبار XCUITest لالتقاط صور أولية، ثم لفها في إطارات الأجهزة بنصوص تسويقية، وادفع النتيجة إلى App Store Connect. يستعرض هذا الدليل خط الأنابيب بالكامل من البداية إلى النهاية مع ملفات التكوين، وتعاريف المسارات (lanes)، وسير عمل التكامل المستمر (CI) الذي تحتاجه لتشغيله.
في النهاية سيكون لديك: إعداد مثبت بواسطة Ruby Bundler، ومفتاح واجهة برمجة تطبيقات App Store Connect، وملف Snapfile وملف SnapshotHelper.swift يعملان بشكل صحيح، وملف Framefile.json يحتوي على كلمات مفتاحية وعناوين لكل لقطة شاشة، وملف Deliverfile تم ضبطه لعمليات رفع لقطات الشاشة فقط، وFastfile بأربعة مسارات، وسير عمل لـ GitHub Actions يقوم بتشغيل العملية بالكامل على بيئة تشغيل macos-26.
1. المتطلبات الأساسية والنموذج الفكري
قبل تثبيت أي شيء يخص Ruby، تأكد من وضوح الصورة المفاهيمية. يتكون خط أنابيب لقطات الشاشة من ثلاث مراحل مستقلة، كل منها يتبع لإجراء fastlane منفصل:
- الالتقاط — يتم تشغيل اختبار XCUITest مقابل محاكي ويقوم باستدعاء
snapshot("01-Home")في اللحظات التي تريد تسجيلها. يقوم إجراءsnapshotمن fastlane بتشغيل المحاكيات الصحيحة، وتبديل كل منها إلى اللغة والمنطقة الصحيحة، وتشغيل الاختبار، وسحب صور PNG الناتجة إلىfastlane/screenshots/<locale>/. - تأطير — يقرأ
frameitكل ملف PNG، ويختار إطار جهاز بناءً على دقة الصورة، وبشكل اختياري يقوم بتركيب خلفية وعنوان تسويقي، ويكتب ملف_framed.pngبجانب الملف الأصلي. - الرفع — يتنقل
deliverفي مجلد لقطات الشاشة، ويطابق كل صورة مع عائلة عرض في App Store Connect (على سبيل المثال، iPhone مقاس 6.9 بوصة، وiPad مقاس 13 بوصة)، ويستبدل مجموعة لقطات الشاشة على إصدار App Store القابل للتعديل عبر واجهة برمجة تطبيقات App Store Connect.
كل مرحلة قابلة للتشغيل بشكل مستقل. يمكنك أخذ لقطات شاشة دون رفعها، أو تأطير لقطات شاشة تم إنتاجها بواسطة أداة أخرى، أو رفع لقطات شاشة تم إنشاؤها مسبقًا دون استدعاء المحاكي على الإطلاق.
2. تثبيت fastlane بالطريقة العقلانية: Bundler
لا تستخدم الأمر brew install fastlane. قم بتثبيت إصدار fastlane لكل مشروع باستخدام Bundler بحيث تشغل كل بيئة عمل تبني مشروعك — سواء كان جهاز الكمبيوتر المحمول الخاص بك، أو الخاص بزميلك، أو بيئة التطوير المستمر (CI) — نفس الإصدار. من جذر المشروع:
# system Ruby on macOS 14+ is fine, but rbenv/asdf is cleaner
gem install bundler
bundle init
echo 'gem "fastlane"' >> Gemfile
bundle install --path vendor/bundle
# from now on, run fastlane via:
bundle exec fastlane <lane>قم بعمل commit لملفي Gemfile وGemfile.lock. أضف vendor/bundle إلى ملف .gitignore. الآن قم بتهيئة fastlane في المشروع:
bundle exec fastlane init
# choose option 4: "Manual setup"يؤدي هذا إلى إنشاء مجلد fastlane/ يحتوي على Fastfile وAppfile. املأ معرف الحزمة (bundle identifier) ومعرف الفريق (team ID) في Appfile:
# fastlane/Appfile
app_identifier("com.example.myapp")
apple_id("[email protected]") # only needed if you fall back to legacy auth
team_id("ABCDE12345") # Developer Portal team ID3. مفتاح واجهة برمجة تطبيقات App Store Connect
تم إيقاف مصادقة اسم المستخدم/كلمة المرور للحسابات الجديدة وتمت حمايتها بالتحقق بخطوتين للجميع، مما يجعلها غير صالحة للاستخدام في بيئات التكامل المستمر (CI). استخدم مفتاح واجهة برمجة تطبيقات App Store Connect بدلاً من ذلك. في App Store Connect ← المستخدمون والوصول ← عمليات التكامل ← واجهة برمجة تطبيقات App Store Connect:
- إنشاء مفتاح API (تقوم بذلك مرة واحدة فقط لكل فريق؛ لا يمكن إعادة تنزيل ملفات
.p8المفقودة). - امنحه دور مدير التطبيق. دور المطور لا يكفي لرفع لقطات الشاشة؛ ودور المسؤول هو أكثر مما تحتاج إليه.
- دون معرف المفتاح (10 أحرف، على سبيل المثال
ABCD1234EF) ومعرف الجهة المصدرة للفريق (UUID في الجزء العلوي من الصفحة نفسها). - قم بتنزيل ملف
AuthKey_ABCD1234EF.p8.
يقرأ fastlane المفتاح من ملف JSON. احفظه باسم fastlane/asc_api_key.json (وضع هذا المسار in .gitignore):
// fastlane/asc_api_key.json
{
"key_id": "ABCD1234EF",
"issuer_id": "57246542-96fe-1a63-e053-0824d011072a",
"key": "-----BEGIN PRIVATE KEY-----\nMIGTAg...truncated...A==\n-----END PRIVATE KEY-----",
"duration": 1200,
"in_house": false
}duration هي مدة صلاحية كل رمز JWT يتم إنشاؤه بالثواني؛ والحد الأقصى الذي تقبله Apple هو 1200 (20 دقيقة). حقل key هو المحتوى الكامل لملف .p8، بما في ذلك سطور BEGIN/END PRIVATE KEY، مع استخدام \n حرفية لفواصل السطور.
في بيئة CI، لا ترفع ملف JSON إلى المستودع مطلقًا. احفظ محتويات JSON كسر واحد (مثل ASC_API_KEY_JSON) واكتب الملف في وقت التشغيل — هناك مثال في قسم GitHub Actions أدناه.
4. snapshot — التقاط لقطات الشاشة في XCUITest
snapshot يعمل عن طريق حقن مساعد Swift صغير في هدف اختبار واجهة المستخدم (UI Test target) الخاص بك. يقوم المساعد بالارتباط ببيئة تشغيل الاختبار بحيث تأخذ كل مكالمة إلى snapshot("name") من اختبارك لقطة شاشة لمحاكي التشغيل، وتسميها، وتكتبها في موقع على القرص يعرف fastlane بالفعل كيفية العثور عليه.
إنشاء المساعد
bundle exec fastlane snapshot initيؤدي هذا إلى إنشاء fastlane/Snapfile و fastlane/SnapshotHelper.swift. أضف SnapshotHelper.swift إلى هدف اختبار واجهة المستخدم في Xcode (اسحبه إلى الداخل، وتأكد من أن "MyAppUITests" هو الهدف الوحيد المحدد — ولا تقم بدمجه أبدًا في ملف التطبيق الثنائي).
Snapfile
يخبر Snapfile إجراء snapshot بالأجهزة التي يجب تشغيلها، واللغات التي يجب تشغيل كل اختبار بها، ومخطط اختبار واجهة المستخدم الذي يجب استدعاؤه:
# fastlane/Snapfile
devices([
"iPhone 17 Pro Max", # 6.9" -> 1320 x 2868 (required slot in 2026)
"iPhone 17 Pro", # 6.3" -> 1206 x 2622 (optional, nicer in store listings)
"iPad Pro 13-inch (M4)", # 13" -> 2064 x 2752 (required if you ship an iPad build)
])
languages([
"en-US",
"de-DE",
"fr-FR",
"es-ES",
"ja",
])
scheme("MyAppUITests") # the UI-testing scheme that runs Snapshot tests
output_directory("./fastlane/screenshots")
clear_previous_screenshots(true)
override_status_bar(true) # 9:41, full battery, full signal
concurrent_simulators(true)
stop_after_first_error(true)
number_of_retries(1)أبرز النقاط:
devices— يجب أن تطابق الأسماء تمامًا ما يطبعه الأمرxcrun simctl list devices. تغير Apple الأسماء كل عام (على سبيل المثال، حل "iPhone 17 Pro Max" محل "iPhone 16 Pro Max"). إذا كان اسم الجهاز خاطئًا، فسيقوم snapshot بتخطيه بصمت.languages— مرر إما رمز المنطقة مثل"en-US"أو رمز لغة مجرد مثل"ja". ينشئ كل إدخال مجلدًا تحتoutput_directory.override_status_bar(true)— يستخدم الأمرsimctl status_bar overrideلإظهار الوقت 9:41، وبطارية كاملة، وإشارة شبكة/Wi-Fi كاملة في كل لقطة شاشة. لا تطلب Apple هذا تقنيًا، ولكن معظم المراجعين يتوقعونه.concurrent_simulators— يقوم بتشغيل محاكيات متعددة بالتوازي. يقلل الوقت الإجمالي تقريبًا بمقدار عدد الأجهزة، ولكن كل محاكي يستهلك حوالي 3 جيجابايت من ذاكرة الوصول العشوائي (RAM)، لذا فهو مكلف للغاية على خوادم CI ذات الموارد المحدودة.clear_previous_screenshots— يمسح المجلدfastlane/screenshots/<locale>/في بداية كل عملية تشغيل. بدون هذا, تتراكم اللقطات القديمة من حالات الاختبار المحذوفة إلى الأبد.
ربط snapshot باختبار XCUITest الخاص بك
يكشف SnapshotHelper عن وظيفتين مستقلتين: setupSnapshot(_:) (تستدعى مرة واحدة لكل اختبار) وsnapshot(_:) (تستدعى أينما تريد تسجيل إطار).
// MyAppUITests/MyAppUITests.swift
import XCTest
final class MyAppUITests: XCTestCase {
override func setUpWithError() throws {
continueAfterFailure = false
let app = XCUIApplication()
setupSnapshot(app) // injects the locale + screenshot bridge
app.launchArguments += [
"-UITests",
"-AppleLanguages", "(\(Snapshot.deviceLanguage))",
"-AppleLocale", Snapshot.currentLocale,
]
app.launch()
}
func testScreenshots() {
let app = XCUIApplication()
snapshot("01-Home") // tap pattern: drive UI, then snapshot
app.tabBars.buttons["Library"].tap()
snapshot("02-Library")
app.cells.element(boundBy: 0).tap()
snapshot("03-Detail")
app.navigationBars.buttons.element(boundBy: 0).tap()
app.tabBars.buttons["Settings"].tap()
snapshot("04-Settings")
}
}بعض الملاحظات العملية:
- مرر
-UITestsكمعامل تشغيل وتحقق منه في تطبيقك لتعطيل التحليلات، أو تبديل بيانات عشوائية محددة مسبقًا، أو تخطي شاشات الترحيب، أو محاكاة مكالمات الشبكة. سيشكرك المراجعون (ونفسك المستقبلية) على ذلك. - يتوقف
snapshotمؤقتًا حتى تكتمل عملية التقاط الشاشة، بحيث يمكنك المضي قدمًا فورًا في التفاعل التالي بعد ذلك. - إذا كانت واجهة المستخدم متحركة، فأضف
UIView.setAnimationsEnabled(false)خلف معامل التشغيل. تؤدي الحركة أثناء الالتقاط إلى إنتاج لقطات ضبابية. - قم بتسمية لقطات الشاشة ببادئة رقمية (
01-،02-) بحيث يتطابق ترتيب أسماء الملفات مع ترتيب العرض في App Store Connect. يقومdeliverبرفعها بالترتيب الأبجدي.
تشغيل snapshot
bundle exec fastlane snapshot
# or, equivalently, in a Fastfile lane:
# capture_ios_screenshotsتهبط المخرجات في fastlane/screenshots/<locale>/iPhone 17 Pro Max-01-Home.png وما شابهها. ينشئ Snapshot أيضًا ملف screenshots.html — افتحه للتنقل بين جميع اللغات والأجهزة في صفحة واحدة.
5. frameit — إضافة إطارات الأجهزة والعناوين
لقطات الشاشة الخام هي مجرد نوافذ عرض الأجهزة العارية. يقوم frameit بلفها في إطارات أجهزة مادية، ويضيف اختياريًا خلفية، وعنوانًا تسويقيًا، وسطراً أصغر لـ "الكلمة المفتاحية" فوقه.
# Download device frames once (cached under ~/.frameit/)
bundle exec fastlane frameit download_frames
# Frame everything in fastlane/screenshots, including subfolders
bundle exec fastlane frameit --use_platform IOSلكل ملف foo.png في شجرة لقطات الشاشة، يكتب frameit ملف foo_framed.png بجانبه. تترك الملفات الأصلية في مكانها؛ ويلتقط deliver النسخة المؤطرة تلقائيًا عندما تكون موجودة (ويعود إلى النسخة الخام إذا لم تكن موجودة).
Framefile.json
تبدو الإطارات الافتراضية مثل اختبار التطوير — إطار أسود، بدون خلفية، وبدون نص. قم بتكوينها باستخدام fastlane/screenshots/Framefile.json:
// fastlane/screenshots/Framefile.json
{
"device_frame_version": "latest",
"default": {
"keyword": {
"font": "./fonts/Inter-SemiBold.ttf",
"color": "#FFFFFF",
"padding": 50
},
"title": {
"font": "./fonts/Inter-Bold.ttf",
"color": "#FFFFFF"
},
"background": "./background.png",
"padding": 80,
"show_complete_frame": false,
"title_below_image": false,
"stack_title": true
},
"data": [
{
"filter": "Home",
"keyword": { "color": "#9F7AEA" },
"frame": "BLACK"
},
{
"filter": "Settings",
"keyword": { "color": "#3B82F6" },
"frame": "WHITE"
}
]
}الحقول الرئيسية:
background— ملف PNG الذي يتم تركيب الجهاز المؤطر عليه. يجب أن يكون أعرض من أكبر لقطة شاشة مخرجة لديك. يقومframeitبتوسيط الجهاز رأسيًا بشكل افتراضي؛ ويتحكمtitle_below_imageوshow_complete_frameفي كيفية تفاعل النص والإطار.data[]— تجاوزات لكل لقطة شاشة يتم تحديدها بواسطة فلتر جزء من النص مقابل اسم ملف لقطة الشاشة. ينطبق الإدخال الثاني أعلاه فقط على الملفات التي تحتوي على "Settings" في الاسم. استخدم هذا لتخصيص الألوان لكل صف.frame— يتجاوز لون إطار الجهاز للملفات المطابقة. القيم التي يتعرف عليهاframeitفيFramefile.jsonهيBLACKوWHITEوGOLDوROSE_GOLD. يتم تنزيل رسومات الإطار نفسها من ملفات Sketch الخاصة بموارد التسويق لـ Apple عبر الأمرfastlane frameit download_frames.
ملفات title.strings و keyword.strings لكل لغة
تأتي النصوص التسويقية من ملفي نصوص متوازيين لكل لغة — title.strings للعنوان الرئيسي وkeyword.strings للتسمية الأصغر فوقه. المفاتيح هي أسماء ملفات لقطات الشاشة بدون الامتداد أو بادئة الجهاز:
/* fastlane/screenshots/en-US/title.strings */
"01-Home" = "Track every match.\nIn one tap.";
"02-Library" = "Your full history,\nalways with you.";
"03-Detail" = "Drill into any session.";
"04-Settings" = "Sync across all devices.";/* fastlane/screenshots/en-US/keyword.strings */
/* keywords are rendered above the title, smaller */
"01-Home" = "FAST";
"02-Library" = "ORGANIZED";
"03-Detail" = "DEEP";
"04-Settings" = "EVERYWHERE";تذهب الترجمات إلى fastlane/screenshots/de-DE/title.strings وfastlane/screenshots/de-DE/keyword.strings، وfastlane/screenshots/ja/title.strings، وما إلى ذلك. استخدم \n لفواصل السطور. تعود اللغات المفقودة إلى لقطة الشاشة غير المترجمة (لا يتم عرض عنوان) — لا يوجد تراجع تلقائي إلى اللغة الإنجليزية.
الخطوط
ضع ملفات TTF أو OTF في fastlane/screenshots/fonts/ وأشر إليها بمسار نسبي في Framefile.json. الخطوط المتغيرة مدعومة. إذا أشرت إلى خط غير موجود، فسيعود frameit بصمت إلى خط sans-serif افتراضي وستظهر النتيجة خاطئة دون أي تحذير.
6. deliver — الرفع إلى App Store Connect
يمر deliver عبر مجلد لقطات الشاشة، ويطابق كل ملف PNG مع عائلة عرض في App Store Connect، ثم يتحدث مع واجهة برمجة تطبيقات App Store Connect لاستبدال مجموعة لقطات الشاشة في الإصدار القابل للتعديل حاليًا. مع استخدام مصادقة مفتاح واجهة برمجة التطبيقات وملف Deliverfile المحدد، تكون خطوة الرفع عبارة عن أمر واحد فقط.
تخطيط المجلد
يتوقع deliver تخطيطًا مسطحًا لكل لغة — بدون مجلدات فرعية للأجهزة. يتم اكتشاف عائلة العرض من دقة الصورة (وعند وجود غموض، من بادئة الجهاز في اسم الملف الذي يضيفه snapshot بالفعل):
fastlane/screenshots/
├── en-US/
│ ├── iPhone 17 Pro Max-01-Home_framed.png # 6.9" iPhone
│ ├── iPhone 17 Pro Max-02-Library_framed.png
│ ├── iPad Pro 13-inch (M4)-01-Home_framed.png # 13" iPad
│ ├── title.strings
│ └── keyword.strings
├── de-DE/
│ └── ...
└── Framefile.jsonDeliverfile
# fastlane/Deliverfile
app_identifier("com.example.myapp")
team_id("ABCDE12345")
# Auth via App Store Connect API key (preferred over username/password).
api_key_path("./fastlane/asc_api_key.json")
# Where to read screenshots / metadata from.
screenshots_path("./fastlane/screenshots")
metadata_path("./fastlane/metadata")
# Only push screenshots — leave the binary, pricing, IAPs, etc. alone.
skip_binary_upload(true)
skip_metadata(false)
skip_screenshots(false)
skip_app_version_update(true)
# Don't ask for confirmation in CI.
force(true)
overwrite_screenshots(true)
run_precheck_before_submit(false)
submit_for_review(false)
# Match a screenshot file against the right App Store display family.
# Useful when fastlane's resolution-based detection is ambiguous.
ignore_language_directory_validation(false)العلامات الأكثر أهمية:
skip_binary_upload— بدون هذا، يبحثdeliverعن ملف IPA ويرفض التشغيل إذا لم يكن موجودًا.skip_metadata— اضبطه علىtrueإذا كنت تريد فقط لقطات الشاشة ولا تملك مجلدfastlane/metadata/. اضبطه علىfalseلشحن العناوين المترجمة والأوصاف والكلمات المفتاحية والنصوص الترويجية في نفس الاستدعاء.force(true)— يتخطى موجه التأكيد "هل أنت متأكد؟" الذي يعرضهdeliverقبل الدفع. مطلوب في CI.overwrite_screenshots(true)— يمسح مجموعة لقطات الشاشة الحالية في كل عائلة عرض قبل رفع اللقطات الجديدة. بدون ذلك، يتم إلحاق عمليات الرفع الجديدة باللقطات القديمة حتى حد 10 لقطات شاشة، ثم تفشل العملية.run_precheck_before_submit(false)— يفحص إجراءprecheckالبيانات الوصفية بحثًا عن مصطلحات محفوفة بالمخاطر ("Beta"، أسماء المنافسين، الكلمات الخاضعة للرقابة). وهو رائع قبل التقديم النهائي ولكنه غير مناسب لعمليات تحديث لقطات الشاشة فقط.
التشغيل التجريبي، ثم الشحن
# verify which display families and locales will be touched, no upload
bundle exec fastlane deliver --verify_only
# inspect the resolved configuration
bundle exec fastlane deliver --print_resolved_options
# real upload
bundle exec fastlane deliverفي أول عملية رفع حقيقية، يطبع deliver معاينة HTML في fastlane/preview.html وينتظر التأكيد ما لم يتم تعيين force(true). افتح المعاينة دائمًا في المرة الأولى لعائلة أجهزة جديدة — فهناك تظهر حالات عدم تطابق عائلة العرض.
7. ملف Fastfile الكامل
ملف واحد بأربعة مسارات تغطي الالتقاط، والتأطير، والالتقاط والتأطير معًا، والشحن الكامل. يستخدم التطوير المحلي مسارًا واحدًا؛ ويستخدم CI مسارًا آخر.
# fastlane/Fastfile
default_platform(:ios)
platform :ios do
desc "Capture screenshots with snapshot"
lane :screenshots do
capture_ios_screenshots # alias for snapshot
end
desc "Frame screenshots with frameit"
lane :frame do
frame_screenshots(
path: "./fastlane/screenshots",
use_platform: "IOS"
)
end
desc "Build framed screenshots end-to-end"
lane :build_marketing do
capture_ios_screenshots
frame_screenshots(path: "./fastlane/screenshots", use_platform: "IOS")
end
desc "Upload screenshots to App Store Connect"
lane :upload_screenshots do
upload_to_app_store( # alias for deliver
skip_binary_upload: true,
skip_metadata: true,
skip_app_version_update: true,
force: true,
overwrite_screenshots: true,
run_precheck_before_submit: false
)
end
desc "Full release pipeline: capture, frame, upload"
lane :ship_screenshots do
capture_ios_screenshots
frame_screenshots(path: "./fastlane/screenshots", use_platform: "IOS")
upload_to_app_store(
skip_binary_upload: true,
skip_metadata: true,
skip_app_version_update: true,
force: true,
overwrite_screenshots: true
)
end
endتعد أسماء capture_ios_screenshots وframe_screenshots وupload_to_app_store هي الأسماء المعتمدة لـ snapshot وframeit وdeliver. الأسماء القصيرة لا تزال تعمل ولكن الأسماء الطويلة أوضح ولا تتداخل مع الكلمات المفتاحية في Ruby.
8. التكامل المستمر (CI) على GitHub Actions
عمليات التقاط الشاشة بطيئة (15 دقيقة هو الوقت المعتاد لـ 5 لغات × 3 أجهزة) ولكنها تتوازى بشكل جيد عبر المحاكيات. عادةً ما تكون بيئة تشغيل macos-26 واحدة مع محاكيات متزامنة كافية.
# .github/workflows/screenshots.yml
name: Screenshots
on:
workflow_dispatch:
push:
paths:
- "fastlane/**"
- "MyAppUITests/**"
jobs:
screenshots:
runs-on: macos-26 # Xcode 26.x default; macos-15 also works with explicit xcode-select
timeout-minutes: 90
env:
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
FASTLANE_SKIP_UPDATE_CHECK: "1"
FASTLANE_HIDE_CHANGELOG: "1"
steps:
- uses: actions/checkout@v4
- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_26.3.app
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.3"
bundler-cache: true
- name: Write App Store Connect API key
run: |
mkdir -p fastlane
echo "$ASC_API_KEY_JSON" > fastlane/asc_api_key.json
env:
ASC_API_KEY_JSON: ${{ secrets.ASC_API_KEY_JSON }}
- name: Capture, frame, upload
run: bundle exec fastlane ios ship_screenshots
- name: Archive framed screenshots
if: always()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: fastlane/screenshotsأشياء تجعل تشغيل CI موثوقًا به:
- تثبيت إصدار Xcode. استخدم الأمر
sudo xcode-select -sبمسار صريح. تشحن بيئات التشغيل المستضافة من GitHub إصدارات متعددة من Xcode والإصدار الافتراضي هو ما قرروه لهذا الشهر. - تثبيت صورة بيئة التشغيل. استخدم صورة ذات إصدار محدد مثل
macos-26أوmacos-15، وليسmacos-latest. تحديث بيئة التشغيل قبل ثلاثة أسابيع من الإصدار هو نوع المفاجآت التي لا تريدها. - تخزين تثبيت Bundler مؤقتًا. يؤدي تعيين
bundler-cache: trueفيsetup-rubyإلى توفير دقيقة أو دقيقتين في كل تشغيل. - كتابة مفتاح API من سر واحد. يعد تخزين
key_idوissuer_idوkeyكأسرار منفصلة أكثر ملاءمة للتناوب ولكنه أكثر عرضة للأخطاء. سر واحد بتنسيق JSON مناسب للفرق الصغيرة. - رفع لقطات الشاشة المؤطرة كأرشيف (artifact) حتى عند فشل خطوة الرفع. ستحتاج دائمًا تقريبًا إلى فحص ما تم إنشاؤه قبل إعادة التشغيل.
9. الأخطاء الشائعة وحلولها
"Could not find a device matching..."
يجب أن تطابق أسماء أجهزة Snapshot الأمر xcrun simctl list devicetypes تمامًا. تشحن Apple جهازًا جديدًا متصدرًا كل عام، ويعني نظام التسمية المستند إلى التقويم السنوي من Apple أن سلسلة الأدوات تتقدم بسرعة. يشحن "iPhone 17 Pro Max" مع Xcode 26 (تم إصداره في خريف 2025)؛ وعلى Xcode 16 كان الجهاز مقاس 6.9 بوصة هو "iPhone 16 Pro Max". بعد كل ترقية لـ Xcode، أعد تشغيل xcrun simctl list devicetypes | grep iPhone وقم بتحديث Snapfile.
"Unable to verify upload" / 401 Unauthorized
انتهت صلاحية رمز JWT الخاص بك في منتصف عملية الرفع، أو تم إلغاء مفتاح API، أو تم خفض دوره. تحقق من أن duration في asc_api_key.json لا يتجاوز 1200، ثم أعد إصدار المفتاح.
"App Store Connect is locked"
الإصدار الذي تقوم بالرفع إليه قيد المراجعة (In Review) أو في انتظار إصدار المطور (Pending Developer Release). لقطات الشاشة تكون للقراءة فقط في هذه الحالات. أنشئ إصدارًا جديدًا (خانة "التحضير لتقديم الطلب") وأعد التشغيل.
تنتهي لقطات الشاشة في عائلة عرض خاطئة
يطابق deliver حسب دقة الصورة. إذا قمت بتصدير لقطة شاشة iPhone مقاس 6.9" بدقة 1320 × 2868 فستهبط في خانة 6.9"؛ وإذا قمت بتغيير حجمها إلى 1284 × 2778 فستهبط في خانة 6.5" القديمة. إذا كانت كلا عائلتي العرض موجودتين في إصدارك، فقدم عائلة الجهاز في اسم الملف — وهو البادئة التي ينتجها snapshot بالفعل. غالبًا ما تتجاهلها عمليات التصدير اليدوية.
"Screenshot has alpha channel" / شفافية غير متوقعة
رفض متجر App Store Connect تاريخيًا ملفات PNG ذات الشفافية، ولا يزال deliver يحذر عندما يرى قناة ألفا (alpha channel). لا تنص صفحة مواصفات لقطات الشاشة الحالية من Apple على هذه القاعدة صراحةً، ولكن تسطيح القناة الشفافة (alpha) هو الخطوة الآمنة. قم بتمرير الملف عبر sips -s format png مع ملف تعريف ألوان PNG، أو أعد التصدير بدون قناة ألفا. تكون المخرجات المركبة من frameit معتمة دائمًا؛ ويمكن أن تتضمن مخرجات simctl io ... screenshot الخام قناة ألفا إذا لم تكن طريقة العرض الجذرية معتمة.
"Locale ru-RU does not exist for this app"
يقوم deliver بالرفع فقط إلى لغات App Store Connect الموجودة بالفعل في الإصدار. أضف اللغة في App Store Connect أولاً (أو اضبط force_create_app(true) + البيانات الوصفية في deliver)، ثم أعد التشغيل.
المحاكيات المتزامنة تلتهم كل ذاكرة الوصول العشوائي (RAM)
يكلف كل محاكي iPhone 17 Pro Max تم تشغيله مع ظهور لوحة المفاتيح حوالي 2.5 جيجابايت. على جهاز MacBook بسعة 16 جيجابايت، سيؤدي تشغيل ثلاثة محاكيات في وقت واحد إلى استخدام الذاكرة الافتراضية والانهيار في منتصف الاختبار. اضبط concurrent_simulators(false) محليًا؛ ودع CI يقوم بتشغيلها بالتوازي على بيئة تشغيل أقوى.
10. عندما تكون أداة fastlane أكثر من اللازم
يكون خط أنابيب لقطات الشاشة لـ fastlane رائعًا عندما:
- يكون لديك بالفعل هدف اختبار واجهة مستعمل ويكون فريقك مرتاحًا في الحفاظ على ثوابت XCUITest.
- تريد أن تكون لقطات الشاشة أرشيفًا للتكامل المستمر (CI artifact)، يتم تجديده في كل إصدار.
- تقوم بالتوطين وتريد تحديد التخطيط مرة واحدة في الكود، وليس إعادة رسمه لكل لغة.
وهو الاختيار الخاطئ عندما:
- تريد توجيهًا فنيًا كاملاً لكل لقطة شاشة — الطباعة، التكوين، العناصر الزخرفية. يعد
frameitمحرك قوالب، وليس أداة تصميم. - تحتاج إلى شحن لقطة شاشة بجودة تسويقية لميزة غير موجودة بعد في التطبيق، أو موجودة فقط خلف ميزة معطلة (feature flag) لا يمكن لهدف الاختبار الوصول إليها.
- تكون مطورًا مستقلاً تفضل التصميم مرة واحدة في تطبيق Mac والنقر على رفع. تعد سلسلة أدوات Ruby بالإضافة إلى XCUITest بالإضافة إلى تثبيت إصدار Xcode عبئًا حقيقيًا في الصيانة.
بالنسبة لسير العمل القائم على التصميم أولاً، تغطي أداة Screenshot Bro نفس المجال — التخطيطات الموطنة، وإطارات الأجهزة، والرفع بنقرة واحدة إلى App Store Connect باستخدام نفس مفتاح واجهة برمجة التطبيقات — دون تعقيدات XCUITest.
ورقة مرجعية سريعة (TL;DR)
# Setup (once)
bundle init && echo 'gem "fastlane"' >> Gemfile && bundle install
bundle exec fastlane init
bundle exec fastlane snapshot init
bundle exec fastlane frameit download_frames
# Per release
bundle exec fastlane ship_screenshotsهذا هو خط الأنابيب بالكامل. أمران عند التثبيت، وأمر واحد لكل إصدار. يكمن التعقيد في ملفات التكوين أعلاه — وبمجرد كتابتها، نادرًا ما تتغير.
هل تتساءل عما إذا كان يجب إعداد هذا على الإطلاق، أو استخدام تطبيق Mac لنفس المهمة؟ انظر Fastlane snapshot مقابل Screenshot Bro للمقارنة جنبًا إلى جنب وسير عمل "استخدام الاثنين معًا".