TestFlight a Google Play Internal Testing pre .NET MAUI: beta distribúcia od buildu po testerov (2026)

Ako postaviť beta pipeline .NET MAUI cez TestFlight, Google Play Internal Testing a Firebase App Distribution v roku 2026, vrátane GitHub Actions workflow a splnenia pravidla 12 testerov, 14 dní.

TestFlight & Play Beta: .NET MAUI 2026

Aktualizované: 15. septembra 2026

Beta distribúcia .NET MAUI aplikácie v roku 2026 znamená prevádzkovať dve nezávislé linky súčasne: TestFlight pre iOS a Google Play Internal Testing (prípadne Closed Testing) pre Android. Po zániku Visual Studio App Center 31. marca 2025 už neexistuje jednotný nástroj, ktorý by pokrýval obe platformy. Tímy musia beta pipeline poskladať z natívnych služieb dvoch obchodov plus voliteľného Firebase App Distribution ako univerzálnej paralelnej linky. Úprimne, väčšina tímov na túto realitu neprejde, kým im niekto neprestane dostávať buildy, a potom už rieši požiar (hovorím z vlastnej skúsenosti pri jednom klientskom projekte hneď v apríli 2025).

  • TestFlight zostáva jediný oficiálny beta kanál pre iOS a je natívne prepojený s App Store Connect; pre externých testerov vyžaduje krátku beta review Apple.
  • Google Play má tri testovacie tracky (Internal, Closed, Open); pre osobné účty vytvorené po 13. novembri 2023 platí povinné Closed Testing s minimálne 12 testermi po dobu 14 súvislých dní pred prístupom k produkcii.
  • App Center bol vypnutý 31. marca 2025 – náhradou pre distribúciu je Firebase App Distribution alebo Play track, pre crash reporting Sentry/Firebase Crashlytics.
  • Automatizovaný upload používa App Store Connect API s .p8 kľúčom a Google Play Publishing API v3 so service account JSON – oboje sa dá spustiť z GitHub Actions bez interaktívneho prihlásenia.
  • Google Play od 31. augusta 2026 vyžaduje targetSdkVersion 36 pre nové aj aktualizované aplikácie a povinný formát je Android App Bundle (AAB) s Play App Signing.
  • Pri Xcode 26 vyžaduje altool parameter --provider-public-id, ak je účet spojený s viacerými providermi – bez neho upload zlyhá alebo pošle IPA do zlej organizácie.

Prečo je beta distribúcia v roku 2026 iná než pred zánikom App Center

Väčšina tímov, s ktorými som pracoval pred rokom 2025, mala rovnaký beta setup: jeden App Center projekt na Android, druhý na iOS, distribučné skupiny, e-mailové notifikácie a jeden webhook do Slacku. Keď Microsoft 31. marca 2025 App Center vypol, tá vrstva zmizla a s ňou aj ilúzia „jednotného beta portálu". V roku 2026 je realita taká, že Apple a Google majú vlastné, veľmi rozdielne pravidlá pre beta distribúciu a snažiť sa ich zabaliť do jedného workflowu zvyčajne skončí tak, že jedna strana pipeline ticho zaostáva.

Praktický dopad zániku App Center je trojaký. Po prvé, distribúcia sa presunula do natívnych obchodov (TestFlight, Play Console) alebo do Firebase App Distribution pre interné a ad-hoc buildy. Po druhé, crash reporting bolo treba prekopať na Sentry alebo Firebase Crashlytics – detaily nájdeš v článku crash reporting v .NET MAUI po App Center. Po tretie, samotné podpisovanie a upload sa presunuli do CI/CD, čo vyžaduje predpripravený pipeline; ak tento kúsok chýba, pozri CI/CD pre .NET MAUI v GitHub Actions, kde sme si podpisovanie prešli krok po kroku.

V tomto článku predpokladám, že máš čerstvý MAUI 10 projekt so signed IPA a AAB výstupom z buildu. To, čo tu pridávame, je posledná míľa – dostať tie súbory k testerom bez zbytočného trenia.

TestFlight pre .NET MAUI: od IPA po pozvánku testerovi

TestFlight je Apple oficiálny beta kanál a v roku 2026 je stále bez alternatívy pre iOS. Žiadny „TestFlight pre Android" v pravom zmysle slova neexistuje na Apple strane, a ani opačne. Pipeline pre .NET MAUI je priamočiary, ale má niekoľko miest, kde tímy pravidelne narážajú (a hovorím to ako niekto, kto tri projekty zlyhal na tom istom mieste, kým mu to došlo).

Predpoklady na Apple strane

  1. Aktívne členstvo v Apple Developer Program (99 USD/rok).
  2. App Store Connect záznam pre bundle identifier (napríklad sk.tvojafirma.mauiapp) – bez toho altool upload zlyhá s chybou „No suitable application records were found".
  3. Distribučný certifikát a provisioning profil typu App Store, uložené v CI ako base64 secrets.
  4. App Store Connect API kľúč (.p8) s rolou App Manager alebo vyššou – rola Developer síce upload dovolí, ale zabráni ti nastavovať build metadata a testerov.

Build a upload

Z MAUI riešenia vygeneruj IPA cez dotnet publish a nahraj ho pomocou xcrun altool alebo fastlane pilot:

dotnet publish -f net10.0-ios \
  -c Release \
  /p:ArchiveOnBuild=true \
  /p:CodesignKey="Apple Distribution: Tvoja Firma s.r.o. (ABC123)" \
  /p:CodesignProvision="MauiApp AppStore" \
  /p:RuntimeIdentifier=ios-arm64

xcrun altool --upload-app \
  --type ios \
  --file ./bin/Release/net10.0-ios/ios-arm64/publish/MauiApp.ipa \
  --apiKey "$ASC_KEY_ID" \
  --apiIssuer "$ASC_ISSUER_ID" \
  --provider-public-id "$ASC_PROVIDER_ID"

Pozor na dva momenty. Po prvé, --provider-public-id je od Xcode 26 povinný, ak je Apple ID naviazané na viacero organizácií (napríklad interná firemná apka + klientská apka). Bez neho altool vyberie prvého providera abecedne a IPA skončí v zlom účte. Po druhé, altool má v roku 2026 reportované bugy, kde reportuje úspech aj napriek reálnej chybe alebo naopak. V CI teda po uploade vždy pollnuť App Store Connect API a overiť, že build sa objavil s processingState = VALID.

Interní vs externí testeri

TestFlight rozlišuje dva typy skupín. Internal testers sú maximálne 100 ľudí s prístupom k App Store Connect a build sa im sprístupní hneď po skončení processingu (typicky 5–15 minút). External testers sú až 10 000 e-mailov alebo verejný link, ale prvý build v každej „verzii" musí prejsť Apple beta review – zvyčajne pár hodín, občas cez noc. Pre CI a interné iterácie choď cez internal group; pre skutočnú beta cez external. Nikdy neposielaj neschválený build externým – dostanú notifikáciu, že build je „Waiting for Beta App Review" a ozvú sa ti do e-mailu.

Google Play Internal Testing vs Closed Testing: kedy použiť ktorý

Google Play má tri testovacie tracky a v .NET MAUI kontexte sa najviac používajú prvé dva. Rozdiel medzi nimi je väčší, než sa zdá. Výber nesprávneho tracku pre publikáciu je bežná chyba, ktorá stojí týždne, a osobne som to už videl aj u seniorných tímov.

VlastnosťInternal TestingClosed TestingOpen Testing
Max. testerov100Neobmedzené (zoznamy alebo Google skupiny)Neobmedzené (verejné)
Google review pred sprístupnenímNieÁno (prvá verzia každej appky)Áno
Čas od uploadu po testera5–10 minútHodiny až 1–2 dni pri prvej verziiHodiny až 2 dni
Počíta sa do pravidla „12 testerov, 14 dní"NieÁnoNie (nahrádza ju)
Vhodné preCI smoke testy, interné iterácieBeta pre pozvaných používateľov, splnenie požiadaviek PlayVerejná beta pred produkciou
FormátAAB (App Bundle)AABAAB

Praktické pravidlo: Internal Testing používaj z CI po každom merge do main, aby QA a produktový vlastník mali čerstvý build na dosah. Closed Testing spusti raz za týždeň alebo za sprint pre skutočnú beta skupinu a zároveň – ak si osobný účet – aby ti bežali dni na pravidle „12 testerov, 14 dní". Open Testing púšťaj len tesne pred produkciou, pretože ho vidí každý s Play Store linkom.

Od 31. augusta 2026 platí targetSdkVersion = 36 pre všetky nové aj aktualizované aplikácie a Microsoft dokumentácia pre publikovanie MAUI Android to explicitne zdôrazňuje. AAB (Android App Bundle) je povinný formát a Play App Signing je defacto štandard – Google drží upload key a distribučný podpis generuje sám z každej varianty pre danú konfiguráciu zariadenia.

Ako splniť pravidlo „12 testerov, 14 dní" pri .NET MAUI aplikácii

Ak máš osobný Google Play účet registrovaný po 13. novembri 2023, Google od teba pred udelením prístupu k produkcii vyžaduje splnenie takzvaného pravidla 12 testerov, 14 dní. V decembri 2024 sa hranica znížila z pôvodných 20 na 12 testerov, ale 14 súvislých dní zostáva. Organizačné účty verifikované cez D-U-N-S číslo sú od tejto povinnosti oslobodené, no verifikácia trvá 2–4 týždne, takže mnohým sólo vývojárom sa oplatí splniť požiadavku na osobnom účte.

Praktický plán pre MAUI tím

  1. Recruituj 14–15 testerov, nie presne 12. Ak počet klesne pod 12 (človek odinštaluje appku alebo si vymaže účet), časovač sa nuluje. Pár rezerv je najdôležitejšie taktické rozhodnutie celej fázy.
  2. Pošli opt-in link cez Play Console. V Play Console > Testing > Closed Testing vytvor testovací zoznam (nie Google skupinu, tá má vlastnú dynamiku), pridaj e-maily a použi „copy link" na opt-in URL. Nikdy neposielaj priamu Play Store URL – bez opt-in kroku sa nezapočíta.
  3. Nahraj prvý AAB. Prvá closed release každej appky prejde Google review – rátaj s 1–2 dňami. Až po jej schválení sa link stane funkčným.
  4. Overuj skutočné použitie. Google počíta „opted-in" testerov, ale zároveň hodnotí, či appku reálne používali. Do buildu si zahrň jednoduchý event ping (napríklad cez Firebase Analytics) a preposielaj testerom tichú výzvu, aby appku aspoň raz denne spustili.
  5. Sleduj timer v Play Console. Po dosiahnutí 12 testerov sa v sekcii „Publishing overview" objaví countdown „X days remaining". Ak spadne pod 12, hodnota sa vráti na 14 a musíš začať znova.

Rátaj s tým, že 12 opted-in testerov je vstupenka na žiadosť o produkčný prístup, nie garancia. Google môže odmietnuť s odôvodnením „insufficient tester engagement" a vtedy zvyčajne pomôže rozšíriť skupinu na 20+ a získať viac reálnych session z odlišných zariadení a lokalít.

Automatizácia cez App Store Connect API a Play Publishing API

Manuálny upload cez Transporter.app a drag-and-drop v Play Console je fajn na prvé dva releasy, ale pri viac ako dvoch buildoch týždenne sa oplatí prejsť na API. V .NET MAUI 2026 pipeline používam dva samostatné bloky: App Store Connect API pre iOS a Google Play Developer Publishing API v3 pre Android.

App Store Connect API (iOS)

V App Store Connect si pod „Users and Access > Keys" vytvor kľúč. Dostaneš trojicu: Key ID, Issuer ID a stiahnutý AuthKey_XXXX.p8 súbor. Ten .p8 ulož do CI ako base64-enkódovaný secret. Kľúč sa dá použiť dvoma spôsobmi: priamo cez xcrun altool/notarytool, alebo cez fastlane, ktoré vygeneruje krátkodobý JWT token a autentikuje sa cez REST.

# .github/workflows/testflight.yml (výrez)
- name: Decode ASC API key
  run: |
    echo "$ASC_API_KEY_B64" | base64 -d > ./AuthKey.p8
  env:
    ASC_API_KEY_B64: ${{ secrets.ASC_API_KEY_B64 }}

- name: Upload to TestFlight
  run: |
    xcrun altool --upload-app \
      --type ios \
      --file "$IPA_PATH" \
      --apiKey "$ASC_KEY_ID" \
      --apiIssuer "$ASC_ISSUER_ID" \
      --provider-public-id "$ASC_PROVIDER_ID"
  env:
    IPA_PATH: ./artifacts/MauiApp.ipa
    ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
    ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
    ASC_PROVIDER_ID: ${{ secrets.ASC_PROVIDER_ID }}
    API_PRIVATE_KEYS_DIR: ${{ github.workspace }}

Kľúčové: altool hľadá .p8 v ~/.appstoreconnect/private_keys, ~/.private_keys, alebo v adresári označenom API_PRIVATE_KEYS_DIR. Ak ho tam nenájde, upload padne s „Could not find the API key file". Nastavenie API_PRIVATE_KEYS_DIR na workspace býva najspoľahlivejší postup v runneroch.

Google Play Publishing API v3 (Android)

V Google Cloud konzole zapni Google Play Android Developer API, vytvor service account, stiahni JSON kľúč, a v Play Console pod „API access" mu daj rolu Release manager. Prvý AAB ale musíš nahrať ručne cez konzolu, inak API vráti chybu „Package name not found" – Google vyžaduje aspoň jeden zaznamenaný release pred prijatím API uploadov.

V GitHub Actions používam osvedčenú akciu r0adkll/upload-google-play, ktorá zaobalí komplikovaný edit/commit workflow do jedného stepu:

- name: Upload AAB to Play Internal
  uses: r0adkll/upload-google-play@v1
  with:
    serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
    packageName: sk.tvojafirma.mauiapp
    releaseFiles: ./artifacts/MauiApp.aab
    track: internal
    status: completed
    inAppUpdatePriority: 2
    whatsNewDirectory: ./distribution/whatsnew

Adresár whatsnew obsahuje textové súbory formátu whatsnew-sk-SK.txt, whatsnew-en-US.txt atď. – Play Console ich mapuje na jazyky. Neposielaj tam viac ako 500 znakov, dlhší text sa oreže a v niektorých prípadoch celý release zlyhá validáciou.

Firebase App Distribution ako paralelná linka pre interné buildy

Firebase App Distribution je najbližšie k tomu, čo App Center kedysi robilo pre distribúciu: skupiny, e-mailové notifikácie, jeden portál pre iOS aj Android, žiadny obchod v ceste. Neplatí zaň žiadne dodatočné review a build sa dá poslať doslova sekundy po podpise. Pre .NET MAUI má zmysel v dvoch scenároch: nightly buildy pre interný tím, kde nechceš plniť TestFlight quotu, a ad-hoc reprodukcie zákazníckych bugov, kde potrebuješ pridať jedného externého testera bez rituálov Play Consolu.

Setup pre iOS aj Android z jedného workflow

Firebase CLI má jednu utilitu, ktorá berie oba formáty. Kľúčové je, že pre iOS ad-hoc distribúciu potrebuješ iný provisioning profil (Ad Hoc, nie App Store) a UDID zariadení testerov musí byť v profilu zahrnuté – v roku 2026 stále platí, že iOS je „closed by design" a Firebase to nevie obísť.

firebase appdistribution:distribute ./MauiApp.ipa \
  --app "$FIREBASE_IOS_APP_ID" \
  --groups "qa-team,internal-alpha" \
  --release-notes-file ./release-notes.md

firebase appdistribution:distribute ./MauiApp.aab \
  --app "$FIREBASE_ANDROID_APP_ID" \
  --groups "qa-team,internal-alpha" \
  --release-notes-file ./release-notes.md

Autentikuje sa cez GOOGLE_APPLICATION_CREDENTIALS a rovnaký service account, ako pre Play upload – rozhrania majú prekrývajúce sa scopes. Distribúcia je okamžitá, testeri dostanú e-mail s linkom a inštalácia na Android beží cez Firebase App Tester utilitku, na iOS cez konfiguračný profil zariadenia.

Kompletný GitHub Actions workflow pre paralelnú beta distribúciu

Nasledujúci workflow spája všetky tri linky (TestFlight, Play Internal a Firebase App Distribution) do jedného pipeline. Beží na push do vetvy release/* a paralelne buildne obe platformy. Podpisovanie preskočíme, keďže je detailne pokryté v inom článku; sústredíme sa na distribučnú vrstvu. Ak potrebuješ hlbší úvod do MAUI architektúry pred beta fázou, oplatí sa pozrieť aj MVVM architektúru v .NET MAUI, pretože testovateľná aplikácia je poloviční problém beta pipeline.

name: Beta Distribution

on:
  push:
    branches: [ "release/*" ]

jobs:
  ios-testflight:
    runs-on: macos-15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with: { dotnet-version: '10.0.x' }

      - name: Import certs and profiles
        run: ./scripts/import-ios-signing.sh
        env:
          P12_BASE64: ${{ secrets.IOS_P12_BASE64 }}
          P12_PASSWORD: ${{ secrets.IOS_P12_PASSWORD }}
          PROVISION_BASE64: ${{ secrets.IOS_PROVISION_BASE64 }}

      - name: Build IPA
        run: |
          dotnet publish -f net10.0-ios \
            -c Release \
            /p:ArchiveOnBuild=true \
            /p:RuntimeIdentifier=ios-arm64

      - name: Upload to TestFlight
        run: ./scripts/testflight-upload.sh
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_PROVIDER_ID: ${{ secrets.ASC_PROVIDER_ID }}
          ASC_API_KEY_B64: ${{ secrets.ASC_API_KEY_B64 }}

      - name: Firebase App Distribution (iOS ad-hoc)
        if: github.ref == 'refs/heads/release/nightly'
        run: |
          firebase appdistribution:distribute ./bin/adhoc.ipa \
            --app $FIREBASE_IOS_APP_ID \
            --groups internal-alpha

  android-play:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with: { dotnet-version: '10.0.x' }
      - uses: actions/setup-java@v4
        with: { distribution: temurin, java-version: '21' }

      - name: Build AAB
        run: |
          dotnet publish -f net10.0-android \
            -c Release \
            /p:AndroidPackageFormat=aab \
            /p:AndroidKeyStore=true \
            /p:AndroidSigningKeyStore=./keystore.jks \
            /p:AndroidSigningStorePass="$KEYSTORE_PASSWORD" \
            /p:AndroidSigningKeyAlias=upload \
            /p:AndroidSigningKeyPass="$KEY_PASSWORD"
        env:
          KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
          KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}

      - name: Upload to Play Internal
        uses: r0adkll/upload-google-play@v1
        with:
          serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
          packageName: sk.tvojafirma.mauiapp
          releaseFiles: ./bin/Release/net10.0-android/*-Signed.aab
          track: internal
          status: completed
          whatsNewDirectory: ./distribution/whatsnew

Časté chyby pri beta distribúcii .NET MAUI a ako ich obísť

Za posledný rok som videl rovnaké chyby opakovane, na rôznych projektoch. Nasledujúci checklist pokrýva zhruba 80 % incidentov, ktoré tímy hlásia pri prvom nasadzovaní distribučného pipeline. Bez okolkov, poďme na to.

iOS strana

  • „No suitable application records were found": App Store Connect ešte nemá záznam pre bundle ID. Vytvor ho ručne cez portál pred prvým uploadom, nie cez CI.
  • Build sa objaví ako „Missing Compliance": chýba ITSAppUsesNonExemptEncryption v Info.plist. Pridaj <false/>, ak neexportuješ kryptografiu mimo bežného HTTPS – ušetríš si týždeň dopĺňania ECCN dokumentov.
  • Externí testeri vidia „Waiting for Review": prvý build v každom „train" (major/minor) verzii musí prejsť beta review Apple. Naplánuj release aspoň 24 hodín pred termínom.
  • Xcode 26 upload zlyhá s „unknown provider": chýba --provider-public-id. Získaš ho príkazom xcrun altool --list-providers --apiKey KEY --apiIssuer ISSUER.

Android strana

  • „The Android App Bundle was not signed": MAUI v Release móde predvolene nepodpisuje AAB, ak nezapneš /p:AndroidKeyStore=true a všetky štyri sprievodné property.
  • „APKs are not allowed": od 2021 (a stále v 2026) je AAB povinný. Nezabudni nastaviť <AndroidPackageFormat>aab</AndroidPackageFormat> v .csproj.
  • Closed Testing timer sa nulu je aj po zaplnení skupiny: testeri z rovnakej domény ako developer účet sa niekedy nepočítajú. Miešaj externých adresátov.
  • „Version code must be greater": Play API porovnáva versionCode, nie versionName. V CI ho zvyšuj automaticky napríklad z github.run_number.

Prierezové problémy

  • Rozdielne verzie iOS a Android: pravidelne treba synchronizovať ApplicationDisplayVersion a ApplicationVersion v csproj s tým, čo očakávajú obchody – v MAUI 10 slúžia oba na jednotný build metadata systém.
  • Chýbajúci release notes: TestFlight aj Play Console zobrazujú release notes testerom. Ak sú prázdne, dostávaš mnohonásobne viac otázok „čo som mal vlastne testovať".
  • Nezhodné build ID cez platformy: ak posielaš rovnaký sprint na obidve platformy s odlišnou build číslicou, sťažujú si testeri, ktorí porovnávajú iOS vs Android verziu. Nastav si politiku „daná verzia = daná dátumová značka" a drž sa jej.

Často kladené otázky

Existuje TestFlight pre Android?

Nie, TestFlight je exkluzívne pre Apple platformy. Najbližší ekvivalent na Android je Google Play Internal Testing (rýchle, bez review) alebo Firebase App Distribution (bez obchodu, s vlastnou tester utilitkou). Pre .NET MAUI tím to znamená prevádzkovať obidve linky paralelne.

Ako splním pravidlo 12 testerov a 14 dní na Google Play?

Vytvor Closed Testing track, pošli opt-in link aspoň 14 unikátnym Google účtom (rezerva nad 12), a udržuj počet po 14 súvislých dní. Emulátory a duplicity sa nepočítajú a časovač sa nuluje pri poklese pod 12. Organizačné účty overené D-U-N-S sú z pravidla vyňaté.

Čím nahradiť App Center pre beta distribúciu .NET MAUI?

Distribúcia sa najlepšie rozdelí na tri linky: TestFlight pre iOS beta, Google Play Internal/Closed Testing pre Android beta a Firebase App Distribution pre interné a ad-hoc buildy oboch platforiem. Crash reporting a analytiku preneste na Sentry alebo Firebase Crashlytics.

Ako nahrám IPA do TestFlight z CI bez interaktívneho prihlásenia?

Použi App Store Connect API kľúč (.p8) s rolou App Manager, ulož ho v CI ako base64 secret a autentikuj sa cez xcrun altool parametrami --apiKey, --apiIssuer a od Xcode 26 aj --provider-public-id. Alternatíva je fastlane upload_to_testflight, ktorý používa rovnaký kľúč a rieši JWT interne.

Aký je rozdiel medzi Internal Testing a Closed Testing na Google Play?

Internal Testing je pre max. 100 testerov, bez Google review, s dostupnosťou za 5–10 minút – ideálne pre CI smoke testy. Closed Testing je pre neobmedzený počet pozvaných testerov, prvá verzia každej appky prechádza Google review, a jedine tento track sa počíta do pravidla „12 testerov, 14 dní" pre nové osobné účty.

Sofia Rodriguez
O Autorovi Sofia Rodriguez

Mobile DevOps engineer focused on the unglamorous stuff: build pipelines, signing, store releases, and the tooling that keeps teams shipping.