30 अप्रैल 2026 · 15 मिनट में पढ़ें

Fastlane: Make and Ship App Store Screenshots (2026 Guide)

fastlane आईओएस (iOS) और एंड्रॉइड (Android) ऐप्स भेजने के उबाऊ हिस्सों को स्वचालित करने के लिए एक रूबी (Ruby) टूलचेन है। इसके स्क्रीनशॉट से जुड़े तीन कार्य — snapshot, frameit, और deliver — पूरे पाइपलाइन को कवर करते हैं: कच्ची छवियों को कैप्चर करने के लिए अपने ऐप को XCUITest के अंदर चलाएं, उन्हें मार्केटिंग कॉपी के साथ डिवाइस फ़्रेम में लपेटें, और परिणाम को ऐप स्टोर कनेक्ट (App Store Connect) पर पुश करें। यह मार्गदर्शिका आपके लिए आवश्यक कॉन्फ़िगरेशन फ़ाइलों, लेन (lane) परिभाषाओं और CI वर्कफ़्लो के साथ पाइपलाइन को शुरू से अंत तक समझाती है।

अंत तक आपके पास होगा: एक रूबी बंडलर-पिन सेटअप, एक ऐप स्टोर कनेक्ट एपीआई कुंजी, एक काम करने वाला Snapfile + SnapshotHelper.swift, स्क्रीनशॉट के अनुसार कीवर्ड और शीर्षकों के साथ एक Framefile.json, केवल स्क्रीनशॉट अपलोड के लिए ट्यून की गई एक Deliverfile, चार-लेन वाली Fastfile, और एक गिटहब एक्शन्स (GitHub Actions) वर्कफ़्लो जो macos-26 रनर पर पूरा सेटअप चलाता है।

1. पूर्वापेक्षाएँ और मानसिक मॉडल

रूबी इंस्टॉल करने से पहले, वैचारिक तस्वीर को पूरी तरह स्पष्ट कर लें। स्क्रीनशॉट पाइपलाइन के तीन स्वतंत्र चरण होते हैं, जिनमें से प्रत्येक एक अलग fastlane कार्य के स्वामित्व में होता है:

  1. कैप्चर — एक XCUITest सिम्युलेटर के खिलाफ चलता है और उन क्षणों पर snapshot("01-Home") को कॉल करता है जिन्हें आप रिकॉर्ड करना चाहते हैं। फ़ास्टलेन का snapshot कार्य सही सिम्युलेटर लॉन्च करता है, प्रत्येक को सही लोकेल में स्विच करता है, परीक्षण चलाता है, और परिणामी PNG फ़ाइलों को fastlane/screenshots/<locale>/ में खींचता है।
  2. फ़्रेमframeit प्रत्येक PNG को पढ़ता है, छवि रिज़ॉल्यूशन के आधार पर एक डिवाइस फ़्रेम चुनता है, वैकल्पिक रूप से एक बैकग्राउंड और मार्केटिंग शीर्षक जोड़ता है, और मूल फ़ाइल के साथ एक _framed.png लिखता है।
  3. अपलोडdeliver स्क्रीनशॉट फ़ोल्डर में जाता है, प्रत्येक छवि को एक ऐप स्टोर कनेक्ट डिस्प्ले फ़ैमिली (जैसे iPhone 6.9", iPad 13"), और ऐप स्टोर कनेक्ट एपीआई के माध्यम से संपादन योग्य ऐप स्टोर संस्करण पर स्क्रीनशॉट सेट को बदल देता है।

प्रत्येक चरण को स्वतंत्र रूप से चलाया जा सकता है। आप स्क्रीनशॉट को बिना अपलोड किए ले सकते हैं, किसी अन्य टूल द्वारा निर्मित स्क्रीनशॉट को फ़्रेम कर सकते हैं, या सिम्युलेटर को बिना कॉल किए प्री-बिल्ट स्क्रीनशॉट अपलोड कर सकते हैं।

2. समझदारी से फ़ास्टलेन स्थापित करें: बंडलर (Bundler)

brew install fastlane का उपयोग न करें। प्रोजेक्ट के अनुसार फ़ास्टलेन को बंडलर के साथ पिन करें ताकि आपके प्रोजेक्ट को बनाने वाली हर मशीन — आपका लैपटॉप, सहकर्मी का लैपटॉप, या 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>

Gemfile और Gemfile.lock को कमिट करें। अपनी .gitignore फ़ाइल में vendor/bundle जोड़ें। अब प्रोजेक्ट में फ़ास्टलेन को इनिशियलाइज़ करें:

bundle exec fastlane init
# choose option 4: "Manual setup"

यह Fastfile और Appfile के साथ एक fastlane/ निर्देशिका बनाता है। Appfile में अपना बंडल पहचानकर्ता (bundle identifier) और टीम आईडी भरें:

# 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 ID

3. ऐप स्टोर कनेक्ट एपीआई कुंजी (API Key)

उपयोगकर्ता नाम/पासवर्ड प्रमाणीकरण नए खातों के लिए अप्रचलित है और अन्य सभी के लिए टू-फैक्टर सुरक्षित है, जिससे यह CI पर अनुपयोगी हो जाता है। इसके बजाय ऐप स्टोर कनेक्ट एपीआई कुंजी का उपयोग करें। ऐप स्टोर कनेक्ट में → Users and AccessIntegrationsApp Store Connect API:

  1. Generate API Key पर क्लिक करें (आप इसे टीम के लिए केवल एक बार करते हैं; खोई हुई .p8 फ़ाइलों को दोबारा डाउनलोड नहीं किया जा सकता)।
  2. इसे App Manager की भूमिका दें। स्क्रीनशॉट अपलोड करने के लिए Developer की भूमिका पर्याप्त नहीं है; Admin की भूमिका आपकी आवश्यकता से अधिक है।
  3. Key ID (10 वर्ण, उदा. ABCD1234EF) और टीम का Issuer ID (उसी पृष्ठ के शीर्ष पर स्थित UUID) नोट करें।
  4. descargar el archivo AuthKey_ABCD1234EF.p8.

फ़ास्टलेन एक JSON फ़ाइल से कुंजी पढ़ता है। इसे fastlane/asc_api_key.json के रूप में सहेजें (और उस पथ को .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) के रूप में स्टोर करें और रन टाइम पर फ़ाइल लिखें — नीचे गिटहब एक्शन्स सेक्शन में एक उदाहरण है।

4. snapshot — XCUITest में स्क्रीनशॉट कैप्चर करना

snapshot आपके UI परीक्षण लक्ष्य में एक छोटा स्विफ्ट हेल्पर इंजेक्ट करके काम करता है। हेल्पर परीक्षण रनटाइम में हुक करता है ताकि आपके परीक्षण से snapshot("name") का प्रत्येक कॉल सिम्युलेटर स्क्रीन का एक स्क्रीनशॉट लेता है, उसका नाम रखता है, और उसे डिस्क स्थान पर लिखता है जिसे फ़ास्टलेन पहले से ही खोजना जानता है।

हेल्पर जेनरेट करें

bundle exec fastlane snapshot init

यह fastlane/Snapfile और fastlane/SnapshotHelper.swift बनाता है। Xcode में अपने UI testing target में SnapshotHelper.swift जोड़ें (इसे खींचें, सुनिश्चित करें कि "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" को बदल दिया)। यदि कोई डिवाइस नाम गलत है, तो स्नैपशॉट चुपचाप इसे छोड़ देगा।
  • languages — या तो एक क्षेत्र कोड जैसे "en-US" या एक सामान्य भाषा कोड जैसे "ja" पास करें। प्रत्येक प्रविष्टि output_directory के अंतर्गत एक फ़ोल्डर बनाती है।
  • override_status_bar(true) — प्रत्येक स्क्रीनशॉट में 9:41, पूर्ण बैटरी, और पूर्ण वाई-फाई/सेल सिग्नल दिखाने के लिए simctl status_bar override का उपयोग करता है। Apple तकनीकी रूप से इसकी आवश्यकता नहीं रखता है, लेकिन अधिकांश समीक्षक इसकी अपेक्षा करते हैं।
  • concurrent_simulators — समानांतर में कई सिम्युलेटर चलाता है। डिवाइसों की संख्या के आधार पर समय को कम करता है, लेकिन प्रत्येक सिम्युलेटर की लागत ~3 जीबी रैम होती है, इसलिए यह कम संसाधन वाले सीआई रनर्स के लिए बहुत भारी हो सकता है।
  • clear_previous_screenshots — प्रत्येक रन की शुरुआत में fastlane/screenshots/<locale>/ को हटा देता है। इसके बिना, हटाए गए परीक्षण मामलों के पुराने शॉट्स हमेशा के लिए जमा होते रहते हैं।

अपने 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-) के साथ स्क्रीनशॉट नाम दें ताकि फ़ाइल नाम का क्रम ऐप स्टोर कनेक्ट डिस्प्ले ऑर्डर से मेल खाए। deliver उन्हें लैक्सिकल क्रम में अपलोड करता है।

स्नैपशॉट चलाएं

bundle exec fastlane snapshot

# or, equivalently, in a Fastfile lane:
#   capture_ios_screenshots

आउटपुट fastlane/screenshots/<locale>/iPhone 17 Pro Max-01-Home.png और अन्य में आता है। स्नैपशॉट भी 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 — एक पीएनजी जिस पर फ़्रेमयुक्त डिवाइस को कंपोजिट किया गया है। आपके सबसे बड़े स्क्रीनशॉट आउटपुट से चौड़ा होना चाहिए। frameit डिफ़ॉल्ट रूप से डिवाइस को लंबवत रूप से केंद्रित करता है; title_below_image और show_complete_frame नियंत्रण करते हैं कि टेक्स्ट और फ़्रेम कैसे इंटरैक्ट करते हैं।
  • data[] — स्क्रीनशॉट फ़ाइल नाम के विरुद्ध एक सबस्ट्रिंग फ़िल्टर द्वारा की गई प्रति-स्क्रीनशॉट ओवरराइड्स। ऊपर दी गई दूसरी प्रविष्टि केवल उन फ़ाइलों पर लागू होती है जिनके नाम में "Settings" है। प्रति पंक्ति रंग थीमिंग के लिए इसका उपयोग करें।
  • frame — मिलान करने वाली फ़ाइलों के लिए डिवाइस फ़्रेम रंग को ओवरराइड करता है। frameit जो मान Framefile.json में पहचानता है वे हैं BLACK, WHITE, GOLD, और ROSE_GOLD। फ्रेम आर्टवर्क स्वयं एप्पल के मार्केटिंग-संसाधन स्केच फ़ाइलों से 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 चुपचाप एक डिफ़ॉल्ट सेन्स-सेरिफ़ पर वापस आ जाता है और परिणाम बिना किसी चेतावनी के गलत दिखाई देगा।

6. deliver — ऐप स्टोर कनेक्ट पर अपलोड करना

deliver स्क्रीनशॉट फ़ोल्डर में जाता है, प्रत्येक PNG को एक ऐप स्टोर कनेक्ट डिस्प्ले फ़ैमिली से मिलाता है, फिर वर्तमान में संपादन योग्य संस्करण पर स्क्रीनशॉट सेट को बदलने के लिए ऐप स्टोर कनेक्ट एपीआई से बात करता है। एपीआई कुंजी प्रमाणीकरण और एक केंद्रित 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.json

Deliverfile

# 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 — यदि आप केवल स्क्रीनशॉट चाहते हैं और आपके पास कोई fastlane/metadata/ निर्देशिका नहीं है, तो इसे true पर सेट करें। एक ही कॉल में स्थानीयकृत शीर्षक, विवरण, कीवर्ड और प्रचार टेक्स्ट भेजने के लिए 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 fastlane/preview.html पर एक 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 के कैनोनिकल नाम हैं। संक्षिप्त नाम अभी भी काम करते हैं लेकिन लंबे नाम स्पष्ट हैं और रूबी कीवर्ड्स को प्रभावित नहीं करते हैं।

8. गिटहब एक्शन्स (GitHub Actions) पर CI

स्क्रीनशॉट धीमे होते हैं (5 लोकेल × 3 डिवाइस के लिए 15 मिनट सामान्य है) लेकिन वे सिमुलेटरों में अच्छी तरह से समानांतर चलते हैं। सिम्युलेटरों के समानांतर चलाने वाला एक सिंगल 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

चीजें जो सीआई रन को विश्वसनीय बनाती हैं:

  • Xcode को पिन करें। एक स्पष्ट पथ के साथ sudo xcode-select -s चलाएं। गिटहब-होस्टेड रनर कई Xcode संस्करण शिप करते हैं और डिफ़ॉल्ट वह होता है जो उन्होंने इस महीने तय किया था।
  • रनर इमेज को पिन करें। macos-latest के बजाय macos-26 या macos-15 जैसी संस्करणित इमेज का उपयोग करें। रिलीज से तीन सप्ताह पहले रनर अपग्रेड उस तरह का सरप्राइज है जो आप नहीं चाहते।
  • बंडलर इंस्टॉल को कैश करें। setup-ruby में bundler-cache: true हर रन से एक या दो मिनट बचा लेता है।
  • एक ही सीक्रेट से एपीआई की लिखें। key_id, issuer_id, और key को अलग-अलग सीक्रेट के रूप में स्टोर करना अधिक रोटेशन-अनुकूल है लेकिन अधिक त्रुटि-प्रवण है। छोटे समूहों के लिए एक JSON-आकार का सीक्रेट ठीक है।
  • फ़्रेमयुक्त स्क्रीनशॉट को एक आर्टिफ़ैक्ट के रूप में अपलोड करें भले ही अपलोड चरण विफल हो जाए। दोबारा चलाने से पहले आप हमेशा यह जांचना चाहते हैं कि क्या उत्पन्न हुआ था।

9. सामान्य त्रुटियां और सुधार

"Could not find a device matching..."

स्नैपशॉट के डिवाइस नाम ठीक 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 कुंजी निरस्त कर दी गई है, या इसकी भूमिका डाउनग्रेड कर दी गई थी। जांचें कि asc_api_key.json में duration अधिकतम 1200 है, फिर कुंजी को फिर से जारी करें।

"App Store Connect is locked"

जिस संस्करण को आप अपलोड कर रहे हैं वह In Review या Pending Developer Release में है। उन राज्यों में स्क्रीनशॉट केवल-पढ़ने के लिए होते हैं। एक नया संस्करण बनाएं ("सबमिशन के लिए तैयार करें" स्लॉट) और फिर से चलाएं।

स्क्रीनशॉट गलत डिस्प्ले फ़ैमिली में समाप्त हो जाते हैं

deliver छवि रिज़ॉल्यूशन द्वारा मेल खाता है। यदि आपने 1320 × 2868 पर 6.9" iPhone स्क्रीनशॉट निर्यात किया है तो यह 6.9" स्लॉट में उतरेगा; यदि आपने इसे 1284 × 2778 पर स्केल किया है तो यह लीगेसी 6.5" स्लॉट में उतरेगा। यदि दोनों डिस्प्ले परिवार आपके संस्करण पर मौजूद हैं, तो फ़ाइल नाम में डिवाइस परिवार की आपूर्ति करें — यह वही उपसर्ग है जो snapshot पहले से ही उत्पन्न करता है। मैनुअल निर्यात अक्सर इसे छोड़ देते हैं।

"Screenshot has alpha channel" / अप्रत्याशित पारदर्शिता

ऐप स्टोर कनेक्ट ने ऐतिहासिक रूप से पारदर्शिता वाले पीएनजी को अस्वीकार कर दिया है, और deliver अभी भी चेतावनी देता है जब वह अल्फा चैनल देखता है। Apple का वर्तमान स्क्रीनशॉट विनिर्देश पृष्ठ अब स्पष्ट रूप से नियम नहीं बताता है, लेकिन अल्फा को समतल करना सुरक्षित कदम है। पीएनजी रंग प्रोफ़ाइल के साथ sips -s format png के माध्यम से फ़ाइल चलाएं, या अल्फा चैनल के बिना फिर से निर्यात करें। frameit का संयुक्त आउटपुट हमेशा अपारदर्शी होता है; कच्चा simctl io ... screenshot आउटपुट अल्फा शामिल कर सकता है यदि आपका रूट व्यू अपारदर्शी नहीं है।

"Locale ru-RU does not exist for this app"

deliver केवल ऐप स्टोर कनेक्ट लोकेल्स पर अपलोड करता है जो पहले से ही संस्करण पर मौजूद हैं। पहले ऐप स्टोर कनेक्ट में लोकेल जोड़ें (या deliver में force_create_app(true) + मेटाडेटा सेट करें), फिर से चलाएं।

समानांतर सिमुलेटर सभी रैम खा जाते हैं

कीबोर्ड अप के साथ बूट किया गया प्रत्येक iPhone 17 Pro Max सिम्युलेटर ~2.5 जीबी खर्च करता है। 16 जीबी मैकबुक पर एक साथ तीन चलाने से स्वैप होगा और टेस्ट के बीच में क्रैश हो जाएगा। स्थानीय रूप से concurrent_simulators(false) सेट करें; सीआई को उन्हें अधिक शक्तिशाली रनर पर समानांतर में चलाने दें।

10. जब फ़ास्टलेन अत्यधिक हो

फ़ास्टलेन की स्क्रीनशॉट पाइपलाइन तब बहुत अच्छी होती है जब:

  • आपके पास पहले से ही एक यूआई परीक्षण लक्ष्य है और आपकी टीम XCUITest फिक्स्चर को बनाए रखने में सहज है।
  • आप चाहते हैं कि स्क्रीनशॉट एक सीआई आर्टिफ़ैक्ट हो, जो हर रिलीज़ पर फिर से उत्पन्न हो।
  • आप स्थानीयकृत करते हैं और चाहते हैं कि लेआउट कोड में एक बार परिभाषित किया जाए, प्रति भाषा फिर से नहीं खींचा जाए।

यह तब गलत विकल्प है जब:

  • आप प्रत्येक स्क्रीनशॉट पर पूर्ण कला निर्देश चाहते हैं — टाइपोग्राफी, रचना, सजावटी तत्व। frameit एक टेम्प्लेटिंग इंजन है, डिज़ाइन टूल नहीं।
  • आपको किसी ऐसी सुविधा के मार्केटिंग-गुणवत्ता वाले स्क्रीनशॉट को भेजने की आवश्यकता है जो अभी तक ऐप में मौजूद नहीं है, या जो केवल एक फीचर ध्वज के पीछे मौजूद है जिसे आपका परीक्षण लक्ष्य नहीं छू सकता है।
  • आप एक एकल डेवलपर हैं जो मैक ऐप में एक बार डिज़ाइन करना और Upload पर क्लिक करना पसंद करेंगे। रूबी टूलचेन प्लस XCUITest प्लस एक्सकोड-वर्जन पिनिंग वास्तविक रखरखाव ओवरहेड है।

डिज़ाइन-प्रथम वर्कफ़्लो के लिए, Screenshot Bro समान आधार को कवर करता है — स्थानीयकृत लेआउट, डिवाइस फ़्रेम, उसी एपीआई कुंजी के साथ वन-क्लिक ऐप स्टोर कनेक्ट अपलोड — बिना 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

वही पूरी पाइपलाइन है। स्थापना के समय दो कमांड, प्रति रिलीज एक कमांड। जटिलता ऊपर दी गई कॉन्फ़िगरेशन फ़ाइलों में छिपती है — और एक बार लिखे जाने के बाद, वे मुश्किल से बदलते हैं।

सोच रहे हैं कि क्या इसे बिल्कुल सेटअप किया जाए, या उसी काम के लिए मैक ऐप का उपयोग किया जाए? साथ-साथ तुलना और "दोनों का उपयोग करें" वर्कफ़्लो के लिए फ़ास्टलेन स्नैपशॉट बनाम स्क्रीनशॉट ब्रो देखें।

क्या आप XCUITest पाइपलाइन के बिना समान अपलोड फ्लो चाहते हैं? एक ही Mac और iPad ऐप से App Store screenshots डिज़ाइन करें और भेजें।

App Store पर पाएं

काम करते देखें