Przejdź do treści
Wszystkie przewodniki
Przewodnik krok po kroku45 min czytania

Jak zautomatyzować zrzuty ekranu App Store w CI/CD

Praktyczny, kompleksowy potok do generowania zrzutów ekranu dla App Store i Google Play z Twojego systemu CI przy użyciu Fastlane, GitHub Actions i Bitrise — z konkretną konfiguracją gotową do wklejenia do Twojego repozytorium.

Eric Isensee
Eric IsenseeFounder · Last updated 5 maja 2026

TL;DR

Przechwyć surowe zrzuty ekranu aplikacji w runnerze testów, wyślij je do API Screenshots.live z identyfikatorem szablonu i listą lokalizacji, a Fastlane zapisze wyrenderowane, oprawione i zlokalizowane wyniki z powrotem do Twojego repozytorium. Fastlane deliver / supply wysyła je do sklepów. Cały potok uruchamia się przy push tagu lub nocnym cronie w czasie poniżej pięciu minut.

Po co w ogóle automatyzować zrzuty ekranu?

Większość zespołów traktuje zrzuty ekranu App Store i Google Play jako jednorazowy, ręcznie tworzony zasób: projektant przygotowuje je w Figmie, marketingowiec wrzuca je do App Store Connect i nikt ich już nie dotyka aż do następnego dużego redesignu. Ten model się sypie w momencie, gdy publikujesz w więcej niż jednej lokalizacji. Z 13 obsługiwanymi lokalizacjami, trzema wymaganymi rozmiarami iPhone, dwoma rozmiarami iPada oraz telefonem i tabletem na Google Play, masz do czynienia z ponad 100 plikami PNG na pojedyncze wydanie. Pomnóż to przez każdą zmianę tekstu, każdą promocję, każdy wariant testu A/B, a matematyka przestaje się zgadzać.

Automatyzacja w CI/CD sprawia, że zrzuty ekranu są artefaktem buildu jak każdy inny plik binarny. Są wersjonowane, odtwarzalne i powiązane z commitem. Gdy marketing zmienia podpis, potok przebudowuje każdą lokalizację i wgrywa je — bez ręcznych sesji w Photoshopie, bez rozjazdu zasobów między językami, bez odrzucenia przy zgłoszeniu, bo ktoś zapomniał o wariancie iPhone 6,7 cala.

Jak właściwie wygląda taki potok?

Kształt czystego potoku zrzutów ekranu w CI to trzy etapy: przechwytywanie, renderowanie i dostarczanie. Przechwytywanie to Twój istniejący zestaw testów UI — XCUITest na iOS, Espresso na Androidzie, Detox lub Maestro dla React Native. Renderowanie to jedno wywołanie API Screenshots.live na lokalizację, które komponuje Twoje surowe klatki na szablonie zaprojektowanym jednorazowo. Dostarczanie to istniejące akcje Fastlane deliver i supply, których już używasz do wysyłania plików binarnych.

API renderowania REST Screenshots.live jest kluczowym elementem. Wysyłasz surowe zrzuty ekranu raz, a otrzymujesz każdy rozmiar urządzenia, każdą lokalizację i każdy wariant — w pełni oprawione, opisane i zlokalizowane. Bez headless browser, bez Puppeteer, bez ręcznego skalowania.

Jak to skonfigurować z Fastlane?

Fastlane to standardowe narzędzie do automatyzacji wydań iOS / Android. Dostarczamy oficjalną wtyczkę Fastlane, która opakowuje API renderowania w jedną akcję. Zainstaluj ją z katalogu głównego projektu:

terminal
bash
fastlane add_plugin screenshotslive

Następnie dodaj lane do swojego pliku Fastfile, który przechwytuje, renderuje i zapisuje wynik:

fastlane/Fastfile
ruby
lane :render_screenshots do
  # 1. Capture raw frames with XCUITest
  capture_screenshots(
    workspace: "MyApp.xcworkspace",
    scheme: "MyAppUITests",
    devices: ["iPhone 16 Pro Max", "iPad Pro 12.9-inch"],
    languages: ["en-US", "de-DE", "es-ES", "fr-FR", "pt-BR"]
  )

  # 2. Send raw frames to Screenshots.live, get framed PNGs back
  screenshotslive_render(
    api_token: ENV["SCREENSHOTSLIVE_API_TOKEN"],
    template_id: "tpl_app_release_v3",
    input_dir: "./fastlane/screenshots",
    output_dir: "./fastlane/rendered",
    locales: ["en", "de", "es", "fr", "pt"],
    devices: ["iphone-6.7", "iphone-6.1", "ipad-12.9"]
  )

  # 3. Push to App Store Connect
  deliver(
    screenshots_path: "./fastlane/rendered",
    skip_binary_upload: true,
    skip_metadata: false
  )
end

Wtyczka obsługuje uploady, polling zakończenia i pobieranie równolegle. Typowy render dla 5 lokalizacji i 3 urządzeń kończy się w 60–90 sekund.

Jak uruchomić to na GitHub Actions?

Wrzuć lane do pliku workflow. Workflow poniżej uruchamia się przy każdym push tagu pasującego do v* oraz w nocnym harmonogramie, więc ręczne git tag v1.2.0 && git push --tags to wszystko, czego potrzebujesz, by wyzwolić świeży render.

.github/workflows/screenshots.yml
yaml
name: Render Store Screenshots

on:
  push:
    tags: ["v*"]
  schedule:
    - cron: "0 4 * * *"  # nightly at 04:00 UTC
  workflow_dispatch:

jobs:
  render:
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v4

      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.2"
          bundler-cache: true

      - name: Render screenshots
        env:
          SCREENSHOTSLIVE_API_TOKEN: ${{ secrets.SCREENSHOTSLIVE_API_TOKEN }}
          FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
          FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }}
        run: bundle exec fastlane render_screenshots

      - name: Upload rendered artifacts
        uses: actions/upload-artifact@v4
        with:
          name: store-screenshots
          path: fastlane/rendered/

Dla zespołów, które wolą GitHub Action bez Fastlane, publikujemy także samodzielną akcję GitHub Action, która komunikuje się z API bezpośrednio:

.github/workflows/screenshots-direct.yml
yaml
- uses: Screenshots-Live/render-screenshots-action@v1
  with:
    api-token: ${{ secrets.SCREENSHOTSLIVE_API_TOKEN }}
    template-id: tpl_app_release_v3
    locales: en,de,es,fr,pt,it,nl
    devices: iphone-6.7,iphone-6.1,ipad-12.9
    output-dir: ./screenshots

A co z Bitrise?

Użytkownicy Bitrise dostają ten sam lane przez istniejący krok Fastlane. Dodaj sekret o nazwie SCREENSHOTSLIVE_API_TOKEN w workspace Bitrise, a następnie wrzuć to do swojego bitrise.yml:

bitrise.yml
yaml
workflows:
  render-screenshots:
    steps:
    - activate-ssh-key@4: {}
    - git-clone@8: {}
    - script@1:
        title: Install Fastlane plugins
        inputs:
        - content: |-
            #!/usr/bin/env bash
            bundle install
            bundle exec fastlane add_plugin screenshotslive
    - fastlane@3:
        inputs:
        - lane: render_screenshots
        - work_dir: "$BITRISE_SOURCE_DIR"
    - deploy-to-bitrise-io@2:
        inputs:
        - deploy_path: ./fastlane/rendered

Jak utrzymać krótki czas budowania?

Wolnym etapem każdego potoku zrzutów ekranu w CI jest renderowanie, a nie upload. Sztuczka polega na cache'owaniu renderów według hasha treści, aby niezmienione lokalizacje używały ponownie poprzedniego wyniku. Screenshots.live zwraca stabilny renderHash w odpowiedzi — użyj go jako klucza cache:

  • Wersja szablonu + lokalizacja + hash wariantu → klucz cache
  • Bajty wyrenderowanego PNG → wartość cache
  • TTL cache: 30 dni lub do ponownej publikacji szablonu

Na GitHub Actions użyj actions/cache@v4 z kluczem opartym na ID szablonu i liście lokalizacji. Większość buildów wydania pomija renderowanie całkowicie i przebudowuje tylko lokalizację, która faktycznie się zmieniła.

Jak walidować wynik przed wysyłką?

Apple i Google odrzucają zrzuty ekranu po cichu, gdy nie przejdą kontroli formatu: PNG z kanałem alpha, JPEG zakodowany jako CMYK lub zrzut ekranu iPhone 6,7 cala krótszy o jeden piksel. Zbuduj 20-liniowy krok lintera, który łapie te problemy przed wysyłką:

fastlane/lint_screenshots.rb
ruby
require "chunky_png"

Dir.glob("./fastlane/rendered/**/*.png").each do |path|
  png = ChunkyPNG::Image.from_file(path)
  raise "alpha channel: #{path}" if png.metadata["ColorType"] == 6
  raise "too large: #{path}"     if File.size(path) > 30 * 1024 * 1024
  raise "wrong size: #{path}"    if png.width < 1242
end

Czy uruchamiać to według harmonogramu?

Tak. Nocny cron oznacza, że zrzuty ekranu regenerują się za każdym razem, gdy zmienia się Twój szablon — niezależnie od tego, czy zmiana pochodzi od projektanta w edytorze, aktualizacji tekstu z marketingu, czy dodania nowej lokalizacji. Bez harmonogramu listingi w sklepie rozjeżdżają się z Twoim rzeczywistym produktem i ktoś musi pamiętać, by przed każdą wysyłką wypchnąć świeży build.

Połącz harmonogram z alertem Slack lub e-mail przy awarii. Zepsuty render o 04:00 UTC to powiadomienie o 07:00 czasu lokalnego — a nie blokada wydania odkryta o 17:00 w dniu, w którym chciałeś wypuścić aplikację.

A co z Androidem, React Native i Flutterem?

Potok jest niezależny od platformy po stronie renderowania — zmienia się tylko etap przechwytywania. Dla Androida zamień capture_screenshots na Fastlane screengrab i swój zestaw Espresso. Dla React Native użyj snapshotów Detox. Dla Fluttera użyj zrzutów ekranu integration_test. Wszystkie produkują surowe pliki PNG, które Screenshots.live traktuje identycznie.

Przeczytaj więcej w naszym przewodniku po obsłudze wielu platform, aby skonfigurować przechwytywanie na każdym stosie technologicznym.

Co dalej?

Gdy Twój potok CI niezawodnie dostarcza zrzuty ekranu, następne pytanie brzmi: które zrzuty wygrywają. Połącz ten potok z przewodnikiem po testach A/B, aby publikować warianty według harmonogramu i mierzyć konwersję. Sparuj go z przewodnikiem po lokalizacji, aby dodać 30+ lokalizacji bez 30+ potoków.

Autorytatywne źródła do dalszej lektury: dokumentacja zrzutów ekranu iOS Fastlane, akcja Fastlane deliver oraz oficjalne specyfikacje zrzutów ekranu App Store Connect.

Wypróbuj na prawdziwym repozytorium

Generuj wszystkie te rozmiary automatycznie

Przestań ręcznie zmieniać rozmiar zrzutów ekranu. Zaprojektuj jeden szablon i wyrenderuj każdy rozmiar, urządzenie i język jednym wywołaniem API.

Zacznij za darmo — wypróbuj Screenshots.live