Բոլոր դասերը
Մոբայլ հավելվածներՄիջին12 րոպե ընթերցումԻմ ստեկում

Release App Store և Google Play

Տարբերակավորում, signing, թեստային track-եր, review-ի կանոններ և rollout-ներ՝ առանց անակնկալների։

  • Signing
  • TestFlight
  • Play Console
  • Versioning
  • Review rules
Քայլ 1/6
CI-ը հավաքում է signed release՝ 2.4.0 տարբերակ, build number-ով, որը միայն աճում է։

30 վայրկյանում

Մոբայլ հավելված հրապարակելը նշանակում է ստեղծել ստորագրված build, վերբեռնել այն App Store Connect կամ Play Console, թեստավորել իրական օգտատերերի հետ, անցնել review և rollout անել։ Յուրաքանչյուր store ունի իր կանոնները տարբերակների, signing key-երի, գաղտնիության տեղեկատվության և կոնտենտի համար։ Գրավոր, կրկնելի release process-ը սթրեսային օրը վերածում է առօրյայի։

Ինչու եմ օգտագործում

  • Ինձ համար կարևոր է release engineering-ը, որովհետև հիանալի հավելվածը, որը չի կարող հուսալի release լինել, հաճախորդի համար ոչինչ չարժե։
  • Store review-ի մերժումները և signing-ի խնդիրները օրեր են արժենում․ ես կնախընտրեի կանխել դրանք checklist-ով, քան ուղղել deadline-ի ճնշման տակ։
  • Staged rollout-ները և force-update անջատիչը թույլ են տալիս ինձ վստահ release անել և կանգնեցնել վատ build-ը, մինչ այն կհասնի բոլորին։
  • Յուրաքանչյուր հավելվածի release process-ը փաստաթղթավորում եմ, որպեսզի թիմի ցանկացած ինժեներ կարողանա release անել, ոչ միայն այն մարդը, ով այն կարգավորել է։

Ինչպես է աշխատում

01Տարբերակավորում՝ version name ընդդեմ build number-ի

Յուրաքանչյուր build ունի երկու թիվ։ Օգտատիրոջը տեսանելի տարբերակը (iOS CFBundleShortVersionString, Android versionName) հետևում է semantic versioning-ին՝ 2.4.0 ֆունկցիոնալ release-ի համար, 2.4.1՝ ուղղման համար։ Build number-ը (iOS CFBundleVersion, Android versionCode) store-երի համար է և պետք է միշտ աճի․ Google Play-ը մերժում է versionCode, որն արդեն տեսել է, իսկ App Store Connect-ին պետք է եզակի build number յուրաքանչյուր տարբերակի համար։ Պահեք ճշմարտության մեկ աղբյուր և թողեք, որ CI-ը մեծացնի build number-ը, օրինակ՝ pipeline-ի run number-ից։ Git commit-ը tag արեք տարբերակով, որպեսզի միշտ կարողանաք վերակառուցել store-ում եղածը։ Հիշեք նաև, որ OTA թարմացումները (եթե օգտագործում եք դրանք) պետք է կապված լինեն runtime version-ի հետ, որպեսզի JavaScript թարմացումը երբեք չընկնի այլ native կոդով binary-ի վրա։

groovy
// android/app/build.gradle
android {
  defaultConfig {
    versionCode 2041      // integer, must always increase
    versionName "2.4.1"   // shown to users
    targetSdk 36          // Play: API 36 for new apps and updates from Aug 31, 2026
  }
}
// iOS (Xcode build settings):
// MARKETING_VERSION = 2.4.1       -> CFBundleShortVersionString
// CURRENT_PROJECT_VERSION = 2041  -> CFBundleVersion

02Signing՝ iOS certificate-ներ և profile-ներ, Android keystore-ներ

Signing-ը ապացուցում է, որ build-ը ձեզնից է և չի փոփոխվել։ iOS-ում ձեզ պետք է Apple Developer Program-ի անդամակցություն, App ID (bundle identifier), distribution certificate (private key-ը ձեր keychain-ում կամ CI-ում) և provisioning profile, որը կապում է certificate-ը, App ID-ն և capability-ները (push, Sign in with Apple, associated domains)։ Xcode-ի automatic signing-ն աշխատում է փոքր թիմերի համար․ CI-ը սովորաբար օգտագործում է fastlane match կամ EAS credentials՝ դրանք անվտանգ կիսելու համար։ Android-ում ստորագրում եք upload key-ով keystore ֆայլից․ Play App Signing-ով (պարտադիր նոր հավելվածների համար, որոնք release են արվում որպես AAB bundle-ներ) Google-ը պահում է իրական app signing key-ը և վերաստորագրում։ Եթե կորցնեք upload key-ը, կարող եք reset խնդրել․ առանց Play App Signing-ի կորցրած key-ը նշանակում էր, որ երբեք չէիք կարող թարմացնել հավելվածը։ Երբեք keystore-ներ կամ գաղտնաբառեր commit մի արեք git-ում․ պահեք դրանք secrets manager-ում կամ CI secrets-ում։

bash
# Create an Android upload key (keep the file and passwords in a secret store)
keytool -genkeypair -v -keystore upload-key.jks -alias upload \
  -keyalg RSA -keysize 2048 -validity 10000
 
# Build a signed Android App Bundle
cd android && ./gradlew bundleRelease
 
# iOS: archive and upload with fastlane (certificates via match)
bundle exec fastlane ios beta

03Թեստային track-եր՝ TestFlight և Play testing

Երբեք production մի ուղարկեք build, որը իրական մարդիկ չեն տեղադրել store-ի սեփական pipeline-ից։ iOS-ում վերբեռնեք App Store Connect և օգտագործեք TestFlight․ ներքին թեստավորողները (ձեր թիմի անդամները) build-երը ստանում են գրեթե անմիջապես, արտաքին թեստավորողների համար (մինչև 10,000՝ email-ով կամ հանրային հղումով) տարբերակի առաջին build-ը կարճ beta review է պահանջում։ Build-երի ժամկետը լրանում է 90 օր հետո։ Android-ում Play Console-ն ունի internal testing (արագ, մինչև 100 թեստավորող), closed testing (ցուցակներ կամ խմբեր) և open testing։ Նշում՝ նոր անհատական developer հաշիվները պետք է անցկացնեն closed test առնվազն 12 թեստավորողով, որոնք opt-in են արել 14 անընդմեջ օր, նախքան production հասանելիության դիմելը․ կազմակերպությունների հաշիվները ազատված են։ Թեստավորեք release build-ը, ոչ միայն debug-ը․ minification-ը, signing-ը, push-ը և deep link-երը հաճախ այլ կերպ են վարվում։

  • Թեստավորեք upgrade-ը store-ի նախորդ տարբերակից, ոչ միայն մաքուր տեղադրումները
  • Release build-ում ստուգեք push-ը, deep link-երը, վճարումները և sign-in-ը
  • Պատրաստ պահեք review/demo հաշիվ՝ օրինակ տվյալներով

04Store listing-ներ և գաղտնիության տեղեկատվություն

Listing-ը վաճառում է հավելվածը և նաև ներկայացնում փաստեր, որոնք store-երը ստուգում են։ Ձեզ պետք են անուն, subtitle/կարճ նկարագրություն, նկարագրություն, keyword-եր (iOS), screenshot-ներ պահանջվող սարքերի չափսերի համար, icon, կատեգորիա, տարիքային վարկանիշի հարցաթերթիկ, support URL և privacy policy URL։ Երկու store-երն էլ պահանջում են գաղտնիության բացահայտումներ․ Apple-ի App Privacy «nutrition labels»-ը և Google Play-ի Data safety ձևը պետք է համապատասխանեն նրան, ինչ իրականում հավաքում են հավելվածը և դրա SDK-ները (analytics, crash reporting, վճարումներ)։ iOS-ում հավելվածները և third-party SDK-ները պետք է ներառեն privacy manifest-ներ, որոնք հայտարարում են տվյալների օգտագործումը և «required reason» API-ները։ Եթե հավելվածը հետևում է օգտատերերին այլ ընկերությունների հավելվածներում կամ կայքերում, պետք է օգտագործեք App Tracking Transparency։ Listing-ները տեղայնացրեք յուրաքանչյուր թիրախային շուկայի համար։ Screenshot-ները պատրաստեք իրական build-ից՝ իրատեսական, ոչ անձնական demo տվյալներով։

05Review-ի կանոններ և տարածված մերժումներ

Apple-ը յուրաքանչյուր տարբերակ review է անում App Review Guidelines-ի համաձայն, Google-ը համակցում է ավտոմատ ստուգումները policy review-ի հետ։ Մերժումների մեծ մասը կանխատեսելի է։ Crash-եր կամ կոտրված flow-եր review-ի ժամանակ։ Բացակայող demo հաշիվ կամ login պատ, որը review-ողները չեն կարող անցնել։ Հաշվի ստեղծում առանց հավելվածի ներսում հաշվի ջնջման (Apple-ը դա պահանջում է)։ Third-party social login առանց համարժեք, գաղտնիության վրա կենտրոնացած տարբերակի, ինչպիսին է Sign in with Apple-ը։ Թվային ապրանքների կամ բաժանորդագրությունների վաճառք in-app purchase-ից դուրս, որտեղ այն պահանջվում է (ֆիզիկական ապրանքները և իրական աշխարհի ծառայությունները, օրինակ՝ սննդի պատվերները, օգտագործում են սովորական վճարային պրովայդերներ)։ Թույլտվության prompt-եր՝ առանց հստակ purpose string-ների։ Placeholder կոնտենտ, կոտրված հղումներ կամ հավելված, որը պարզապես կայքի wrapper է։ Գաղտնիության label-ներ, որոնք չեն համապատասխանում SDK-ներին։ Resolution Center-ում պատասխանեք հանգիստ և կոնկրետ․ շատ մերժումներ լուծվում են բացատրությամբ կամ կարճ տեսանյութով։

  • Տրամադրեք աշխատող demo հաշիվ և նշումներ review-ողի համար
  • Առաջարկեք հաշվի ջնջում հավելվածի ներսում, երբ օգտատերերը կարող են հաշիվներ ստեղծել
  • Գրեք կոնկրետ թույլտվությունների purpose string-ներ (տեսախցիկ, location, լուսանկարներ)
  • Store build-երը պահեք ընթացիկ SDK-ների վրա․ Apple-ը և Google-ը ամեն տարի բարձրացնում են նվազագույնները

06Staged rollout-ներ, hotfix-եր և OTA թարմացումներ

Մի release արեք միանգամից 100%-ի համար։ iOS-ում Phased Release-ը ավտոմատ թարմացումները բաշխում է յոթ օրվա ընթացքում (1%, 2%, 5%, 10%, 20%, 50%, 100%) և կարող է դադարեցվել․ օգտատերերը դեռ կարող են թարմացնել ձեռքով։ Google Play-ում staged rollout-ը թույլ է տալիս ընտրել տոկոս և մեծացնել այն, կամ կանգնեցնել այն, եթե Crashlytics-ը թռիչք է ցույց տալիս։ Յուրաքանչյուր քայլից առաջ հետևեք crash-free users-ին, հիմնական funnel-ներին և support-ի հաղորդագրություններին։ Արտակարգ իրավիճակների համար ունեցեք hotfix branch և expedited review-ի հարցումներ (օգտագործեք խնայողաբար), ինչպես նաև ձեր Remote Config kill-switch-երը։ OTA թարմացումները (EAS Update կամ self-hosted համարժեքներ․ Microsoft-ի CodePush ծառայությունը դադարեցվեց App Center-ի հետ 2025-ին) փոխարինում են JavaScript bundle-ը և asset-ները՝ առանց store review-ի։ Դրանք հիանալի են փոքր ուղղումների համար, բայց չեն կարող փոխել native կոդը, պետք է համապատասխանեն binary-ի runtime version-ին և չպետք է փոխեն հավելվածի հիմնական նպատակը․ store-ի կանոնները դեռ գործում են։ OTA թարմացումներն էլ rollout արեք աստիճանաբար։

Օգտագործեք, երբ

  • Յուրաքանչյուր հանրային iOS կամ Android release՝ հենց առաջինից սկսած։
  • Ներքին և enterprise հավելվածներ, որոնք տարածվում են TestFlight-ի կամ private Play track-երի միջոցով։
  • Hotfix իրավիճակներ, որտեղ պատրաստված process-ը և rollout-ի վերահսկողությունն ամենաշատն են կարևոր։

Մի՛ օգտագործեք, երբ

  • Մաքուր վեբ հավելվածներ կամ PWA-ներ, որոնց store-ում ներկայություն պետք չէ։
  • Շատ վաղ prototype-ներ․ կիսեք development build-եր կամ ներքին track-եր՝ նախքան ամբողջական listing-ներում ներդրում անելը։

Այլընտրանքներ

  • EAS Build & Submit

    Expo-ի cloud ծառայություն, որը build, sign և submit է անում iOS և Android հավելվածներ։

  • fastlane

    Open-source ավտոմատացում signing-ի, build-ի, screenshot-ների և վերբեռնումների համար՝ տեղում կամ CI-ում։

  • Firebase App Distribution

    Pre-release build-երի տարածում թեստավորողներին store-երից դուրս։

  • Enterprise / MDM distribution

    Apple Business Manager custom app-եր կամ managed Google Play՝ ընկերության ներքին հավելվածների համար։

Լավագույն պրակտիկաներ

  1. Յուրաքանչյուր հավելվածի համար պահեք գրավոր release checklist՝ կոդի հետ տարբերակավորված։
  2. Թողեք, որ CI-ը մեծացնի build number-ները և tag անի git commit-ը յուրաքանչյուր store build-ի համար։
  3. Signing key-երը և գաղտնաբառերը պահեք secrets manager-ում․ միացրեք Play App Signing-ը։
  4. Թեստավորեք ստորագրված release build-ը TestFlight-ի կամ ներքին track-ի միջոցով, ներառյալ upgrade-ը նախորդ տարբերակից։
  5. Privacy label-ները, Data safety-ն և privacy manifest-ները համաձայնեցված պահեք ձեր ավելացրած յուրաքանչյուր SDK-ի հետ։
  6. Օգտագործեք phased/staged rollout-ներ և հետևեք crash-free users-ին՝ նախքան մեծացնելը։
  7. Առաջին release-ից ներառեք force-update անջատիչ և աջակցեք հաշվի ջնջմանը հավելվածի ներսում։

Տարածված սխալներ

  • Build number-ը կրկին օգտագործել կամ նվազեցնել, այնպես որ վերբեռնումը մերժվում է վերջին պահին։
  • Կորցնել signing key-ը կամ պահել այն մեկ laptop-ի վրա՝ առանց backup-ի։
  • Submit անել առանց demo հաշվի կամ login-ով, որը review-ողները չեն կարող անցնել։
  • Ավելացնել analytics կամ ads SDK՝ առանց privacy label-ները և Data safety ձևը թարմացնելու։
  • Push անել OTA թարմացում, որը կախված է տեղադրված binary-ում բացակայող native կոդից։

Խորը վերլուծություն

07CI pipeline, որը build է անում, ստորագրում և upload անում

Ձեռքով release-ները ձախողվում են ձանձրալի ձևերով՝ սխալ scheme, մեկ laptop-ի վրա ժամկետանց certificate, մոռացված build number։ Ամբողջ ճանապարհը տեղափոխեք CI։ iOS-ի համար fastlane match-ը certificate-ներն ու profile-ները պահում է encrypt արված private storage repository-ում, և CI-ն դրանք քաշում է read-only․ App Store Connect API key-ը փոխարինում է անձնական Apple ID login-ներին և two-factor prompt-երին։ Android-ի համար CI-ն upload keystore-ը decode է անում secret-ից, build է անում AAB-ը և upload անում internal track։ Build number-ները գալիս են pipeline-ի run number-ից, commit-ը tag է արվում, և job-ը պահում է build artifact-ները և symbol ֆայլերը։ Մարդիկ պահում են այն որոշումները, որոնք դատողություն են պահանջում՝ production-ի promote և rollout-ի տոկոսների ավելացում, իսկ մեքենաներն անում են յուրաքանչյուր կրկնվող քայլ։

ruby
# fastlane/Fastfile
platform :ios do
  lane :beta do
    setup_ci
    app_store_connect_api_key(key_id: ENV["ASC_KEY_ID"], issuer_id: ENV["ASC_ISSUER_ID"],
                              key_content: ENV["ASC_KEY_P8"])
    match(type: "appstore", readonly: true)
    increment_build_number(build_number: ENV["CI_RUN_NUMBER"])
    build_app(scheme: "MyApp", export_method: "app-store")
    upload_to_testflight(skip_waiting_for_build_processing: true)
  end
end
 
platform :android do
  lane :internal do
    gradle(task: "bundle", build_type: "Release", project_dir: "android")
    upload_to_play_store(track: "internal", aab: lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH])
  end
end

08Պլատֆորմի պահանջները փոխվում են ամեն տարի

Երկու store-երն էլ իրենց նվազագույն պահանջները բարձրացնում են տարեկան ռիթմով, և release-ը կարող է արգելափակվել մի կանոնով, որը ոչ մի կապ չունի ձեր feature-ի հետ։ Apple․ 2026 թվականի ապրիլի 28-ից App Store Connect-ն ընդունում է միայն Xcode 26-ով և iOS 26 SDK-ով կամ ավելի նորով արված build-եր․ privacy manifest-ները և զգայուն API-ների համար հայտարարված պատճառները ստուգվում են upload-ի ժամանակ։ Google․ 2026 թվականի օգոստոսի 31-ից նոր հավելվածները և update-ները պետք է target անեն API 36, իսկ Android 15 կամ ավելի բարձր target անող հավելվածները պետք է աջակցեն 16 KB memory page size-երին, ինչը նշանակում է, որ native գրադարանները, ներառյալ React Native dependency-ների ներսում եղածները, պետք է build արված լինեն դրա համար։ Ամեն տարի պլանավորեք upgrade-ի ֆիքսված պատուհան (Xcode, Android Gradle Plugin, React Native, SDK-ներ), այլ ոչ թե կանոնը բացահայտեք հրատապ hotfix-ի ժամանակ, և բաժանորդագրվեք Apple-ի և Google-ի developer news-ին։

09Ինչ են անում store-երը ձեր binary-ի հետ

Android-ում դուք upload եք անում App Bundle, ոչ թե APK։ Google Play-ը այն բաժանում է APK-ների՝ ըստ սարքի կոնֆիգուրացիայի՝ CPU architecture, screen density, լեզու, և ստորագրում app signing key-ով, որպեսզի օգտատերերը ներբեռնեն միայն այն, ինչ պետք է իրենց սարքին։ bundletool-ը թույլ է տալիս լոկալ վերարտադրել այդ APK-ները՝ ճշգրիտ թեստավորելու համար, թե ինչ կտեղադրեր սարքը։ iOS-ում App Store Connect-ը մշակում է upload-ը, և App Thinning-ը ստեղծում է սարքին հատուկ variant-ներ․ App Store Connect-ի size report-ը ցույց է տալիս իրական download և install չափերը՝ ըստ սարքի։ Չափը կարևոր է conversion-ի համար, հատկապես mobile data-ի դեպքում․ հետևեք native գրադարաններին, bundle արված font-երին և նկարներին, չօգտագործվող locale-ներին։ Երկու store-երն էլ upload-ի ժամանակ կատարում են ավտոմատ ստուգումներ՝ private API-ի օգտագործում, բացակայող purpose string-եր, privacy manifest-ի խնդիրներ, target SDK, և այդ հաղորդագրությունները գալիս են email-ով, ուստի ինչ-որ մեկը պետք է կարդա դրանք։

bash
# Reproduce what Play would install on the connected device
bundletool build-apks --bundle=app-release.aab --output=app.apks --connected-device
bundletool install-apks --apks=app.apks
 
# Check which certificate signed an APK
apksigner verify --print-certs app.apk
 
# Fingerprints of your upload key (needed for API consoles and resets)
keytool -list -v -keystore upload-key.jks -alias upload

10OTA update-ներ․ runtime version-ներ, channel-ներ և rollback

OTA update-ը անվտանգ է միայն այն դեպքում, եթե հասնում է ճիշտ այն native կոդով binary-ին, որը սպասում է։ EAS Update-ը դրան հետևում է runtime version-ով։ appVersion policy-ն այն կապում է հավելվածի version-ին, ինչը պարզ է, բայց կոտրվում է, եթե ինչ-որ մեկը փոխում է native կոդը՝ առանց version-ը բարձրացնելու․ fingerprint policy-ն hash է անում native project-ը, ուստի ցանկացած native փոփոխություն ավտոմատ ստեղծում է նոր runtime՝ ավելի հաճախակի store build-երի գնով։ Build-երը լսում են channel (production, staging), որը map է արված update-ների branch-ին, ուստի կարող եք update-ը թեստավորել staging build-երի վրա՝ նախքան production-ը դրան ուղղելը։ Rollout արեք աստիճանաբար՝ տոկոսով, հետևեք crash-free users-ին և պահեք rollback-ի ճանապարհ․ նորից publish արեք նախորդ update-ը կամ rollback արեք binary-ի մեջ embed արված update-ին։ Ստորագրեք update-ները (code signing), որպեսզի կոտրված սերվերը չկարողանա push անել կամայական կոդ։

bash
# app.json (expo): runtime changes whenever native code changes
# "runtimeVersion": { "policy": "fingerprint" }
 
# Publish to 10% of production users first
eas update --branch production --message "Fix checkout crash" --rollout-percentage 10
 
# Increase, finish or revert the rollout
eas update:edit
eas update:revert-update-rollout
 
# Emergency: send clients back to the code embedded in the binary
eas update:roll-back-to-embedded

Իրական իրավիճակներ

01

Մերժում՝ account-ի ջնջման բացակայության պատճառով

Խնդիր

Նոր version-ը մերժվում է App Review Guideline 5.1.1(v)-ի համաձայն․ օգտատերերը կարող են հավելվածում account ստեղծել, բայց չեն կարող ջնջել այն։ Release-ը client-ին խոստացված է երկուշաբթի։

Ինչպես լուծել

Ավելացրեք հստակ ճանապարհ, սովորաբար Settings → Account → Delete account, որը ջնջումը սկսում է հավելվածի ներսում․ միայն email հասցեի հղումը բավարար չէ։ Հաստատեք օգտատիրոջ հետ, բացատրեք, թե ինչ կլինի նրա տվյալների և պատվերների հետ, և եթե օրենսդրությունը պահանջում է որոշ record-ներ պահել (հաշիվ-ապրանքագրեր), նշեք դա։ Backend-ը պետք է իրականում ջնջի կամ անանունացնի անձնական տվյալները, revoke անի token-ները և հեռացնի push token-ները, հնարավոր է՝ կարճ grace period-ից հետո։ Եթե հավելվածն օգտագործում է Sign in with Apple, revoke արեք token-ը Apple-ի REST API-ով։ Ship արեք fix-ը reviewer notes-ով, որոնք նկարագրում են, թե որտեղ է տարբերակը, կցեք կարճ screen recording և պատասխանեք Resolution Center-ում՝ build number-ով։

02

Crash-երի spike 10% rollout-ի ժամանակ

Խնդիր

3.2.0 version-ը գնաց Android օգտատերերի 10%-ին և iOS phased release-ով 1%-ին։ Մի քանի ժամվա ընթացքում Crashlytics-ը ցույց է տալիս, որ crash-free users-ը նոր վճարման էկրանի վրա 99.8%-ից իջել է 97%-ի։

Ինչպես լուծել

Անմիջապես կանգնեցրեք Play staged rollout-ը և դադարեցրեք iOS phased release-ը․ արդեն update արած օգտատերերը պահում են build-ը, ուստի նրանց պաշտպանելու համար անջատեք նոր վճարման էկրանը իր Remote Config flag-ով։ Կարդացեք Crashlytics-ի խմբավորված issue-ն, վերարտադրեք ազդված սարքի մոդելի վրա, շտկեք release tag-ից ստեղծված hotfix branch-ում և բարձրացրեք 3.2.1՝ ավելի բարձր build number-ով։ Թեստավորեք fix-ը TestFlight-ով և internal track-ով, ներառյալ upgrade-ը 3.2.0-ից։ iOS-ում խնդրեք expedited review, եթե ազդեցությունը դա արդարացնում է։ Վերսկսեք rollout-ը ցածր տոկոսից և գրեք կարճ incident note․ ինչը ձախողվեց, ինչու թեստերը բաց թողեցին, որ ստուգումն ավելացնել։

03

Android upload key-ը կորել է

Խնդիր

Միակ developer-ը, որն ուներ upload keystore-ը, լքել է ընկերությունը, և ոչ ոք չգիտի password-ը։ CI-ն չի կարող ստորագրել նոր build-եր, իսկ անվտանգության fix-ը պետք է դուրս գա այս շաբաթ։

Ինչպես լուծել

Play App Signing-ի դեպքում հավելվածը կորած չէ․ Google-ը պահում է app signing key-ը։ Գեներացրեք նոր upload key keytool-ով, export արեք նրա certificate-ը keytool -export -rfc -keystore new-upload.jks -alias upload -file upload_certificate.pem-ով, և թող account-ի owner-ը Play Console-ում՝ App integrity բաժնում, խնդրի upload key reset՝ կցելով այդ PEM ֆայլը։ Google-ի հաստատումից հետո (դա կարող է տևել մի քանի օր) build-երը ստորագրեք նոր key-ով։ Թարմացրեք բոլոր API console-ները, որոնք օգտագործում են upload key-ի fingerprint-ը, օրինակ՝ Google sign-in կամ Maps-ի սահմանափակումներ։ Ապա շտկեք արմատական պատճառը․ keystore-ը և password-ները՝ թիմային secret store-ում, առնվազն երկու մարդու access-ով։

Արագ ուղեցույց

Սահմանել iOS build number-ը CLI-ից
agvtool new-version -all 2042
Build անել ստորագրված Android App Bundle
cd android && ./gradlew bundleRelease
Export անել upload certificate-ը key reset-ի համար
keytool -export -rfc -keystore upload-key.jks -alias upload -file upload_certificate.pem
Ստուգել APK-ի signing certificate-ը
apksigner verify --print-certs app.apk
Upload անել TestFlight fastlane-ով
bundle exec fastlane pilot upload
Upload անել AAB-ը Play internal track
bundle exec fastlane supply --aab app-release.aab --track internal
Build և submit EAS-ով
eas build --platform all --profile production && eas submit --platform ios
Publish անել աստիճանական OTA update
eas update --branch production --message "Fix" --rollout-percentage 10
iOS-ի աստիճանական release
App Store Connect → version → Phased Release for Automatic Updates
Android-ի աստիճանական release
Play Console → Production → Create release → staged rollout %

Իմ իրական նախագծերում

  • Իմ release արած մոբայլ հավելվածների համար սպասարկում եմ փաստաթղթավորված release process, ներառյալ Remote Config force-update և maintenance անջատիչ։
  • Իմ հավելվածներն աջակցում են deep link-երին Universal Links-ի և Android App Links-ի միջոցով, իսկ association ֆայլերը մատուցվում են համապատասխան կայքերից։
  • Ներդրումային հավելվածը, որի վրա աշխատել եմ, ներառում է հաշվի ջնջում հավելվածի ներսում, համաձայնության նախընտրություններ, offline banner և force update։

Հարցեր և պատասխաններ

Q1Ո՞րն է տարբերությունը version-ի և build number-ի միջև։

Version-ը, օրինակ՝ 2.4.1-ը, այն է, ինչ տեսնում են օգտատերերը, և հետևում է semantic versioning-ին։ Build number-ը նույնականացնում է մեկ վերբեռնված binary և պետք է անընդհատ աճի․ Android-ում versionCode-ը ամբողջ թիվ է, որը երբեք չի կարող կրկնվել, իսկ iOS-ում տարբերակի յուրաքանչյուր build-ին պետք է եզակի CFBundleVersion։ Bug-եր ուղղելիս կարող եք TestFlight վերբեռնել 2.4.1-ի մի քանի build, յուրաքանչյուրը՝ ավելի բարձր build number-ով, և release անել միայն մեկը։ Ես թողնում եմ, որ CI-ը ավտոմատ սահմանի build number-ը և tag անի commit-ը, որպեսզի յուրաքանչյուր store build հնարավոր լինի հետևել և վերակառուցել։

Q2Ի՞նչ է լինում, եթե կորցնեք ձեր Android signing key-ը։

Կախված է նրանից, թե արդյոք հավելվածն օգտագործում է Play App Signing։ Դրանով՝ պարտադիր App Bundle-ներով release արվող նոր հավելվածների համար, Google-ը պահում է app signing key-ը, իսկ դուք պահում եք միայն upload key։ Եթե կորցնեք upload key-ը, հաշվի սեփականատերը կարող է Play Console-ում upload key-ի reset խնդրել և շարունակել հրապարակել։ Առանց Play App Signing-ի կորցրած key-ը նշանակում էր, որ երբեք չէիք կարող թարմացնել հավելվածը և ստիպված էիք նոր listing հրապարակել։ Ահա թե ինչու ես միշտ միացնում եմ այն և upload keystore-ն ու գաղտնաբառերը պահում secrets manager-ում՝ backup-ով։

Q3Որո՞նք են App Store-ի մերժման ամենատարածված պատճառները և ինչպե՞ս եք խուսափում դրանցից։

Crash-եր և կոտրված ֆունկցիաներ review-ի ժամանակ, բացակայող demo credential-ներ, թերի metadata կամ placeholder կոնտենտ, հավելվածի ներսում հաշվի ջնջման բացակայություն, privacy label-ներ, որոնք չեն համապատասխանում SDK-ներին, անհասկանալի թույլտվությունների purpose string-ներ և արտաքին վճարումների օգտագործում թվային կոնտենտի համար, որը պահանջում է in-app purchase։ Ես խուսափում եմ դրանցից pre-submission checklist-ով՝ թեստավորում եմ release build-ը TestFlight-ի միջոցով, տրամադրում demo հաշիվ և նշումներ review-ողի համար, ստուգում յուրաքանչյուր թույլտվության string, թարմ պահում գաղտնիության տեղեկատվությունը և համոզվում, որ sign-in տարբերակները հետևում են login guideline-ին։ Մերժվելու դեպքում պատասխանում եմ Resolution Center-ում ճշգրիտ փաստերով։

Q4Ինչպե՞ս եք անվտանգ rollout անում release-ը։

Նախ build-ը անցնում է ներքին թեստավորողների միջով TestFlight-ում և Play internal track-ում, ներառյալ upgrade-ը նախորդ տարբերակից։ Հետո օգտագործում եմ Apple-ի Phased Release-ը և Play staged rollout՝ սկսելով փոքր տոկոսից։ Յուրաքանչյուր մեծացումից առաջ ստուգում եմ Crashlytics-ի crash-free users-ը, error log-երը և բիզնես metric-ները, օրինակ՝ ավարտված պատվերները։ Եթե ինչ-որ բան սխալ է, դադարեցնում եմ rollout-ը և Remote Config-ի միջոցով անջատում ազդված ֆունկցիան, մինչ ուղղումն անցնում է review։ Force-update անջատիչը վերջին միջոցն է, եթե հին տարբերակը պետք է դադարի աշխատել։

Q5Թույլատրվա՞ծ են OTA թարմացումները, և ե՞րբ կօգտագործեիք դրանք։

Այո, սահմաններում։ Apple-ը և Google-ը թույլ են տալիս թարմացնել interpreted կոդը, օրինակ՝ JavaScript bundle-ը, քանի դեռ թարմացումը չի փոխում հավելվածի հիմնական նպատակը, չի ավելացնում review-ը շրջանցող ֆունկցիաներ և չի խախտում store-ի կանոնները։ Տեխնիկապես OTA թարմացումը կարող է փոխել միայն JavaScript-ը և asset-ները, երբեք native կոդը, ուստի այն պետք է թիրախավորի տեղադրված binary-ի համապատասխան runtime version-ը։ OTA-ն օգտագործում եմ հրատապ bug fix-երի և տեքստի փոփոխությունների համար՝ աստիճանաբար rollout արված և rollback պլանով։ Նոր ֆունկցիաները և այն ամենը, ինչ դիպչում է native մոդուլներին, դեռ անցնում են store-երով։

Սենիոր մակարդակի հարցեր

S1Ինչպե՞ս եք պահպանում backend-ի և հավելվածի հին version-ների համատեղելիությունը։

Ենթադրեք, որ ձեր երբևէ ship արած յուրաքանչյուր version դեռ ինչ-որ տեղ օգտագործվում է։ API-ի փոփոխությունները հետևում են expand-and-contract-ին․ նախ ավելացրեք նոր դաշտեր և endpoint-ներ, պահեք հինները աշխատող, ship արեք հավելվածներ, որոնք օգտագործում են նոր ձևը, և հեռացրեք հին ձևը միայն այն ժամանակ, երբ analytics-ը ցույց տա, որ մնացած հին version-ները աննշան են։ Client-ները header-ներում ուղարկում են իրենց app version-ը և պլատֆորմը, որպեսզի backend-ը կարողանա չափել օգտագործումը, կիրառել version-ին հատուկ վարքագիծ և վերադարձնել հստակ «update required» սխալ։ Remote Config-ը պահում է force update-ի համար նվազագույն աջակցվող version-ը, ինչը վերջին միջոցն է, ոչ թե պլանը, քանի որ ընդհատում է օգտատերերին։ Trade-off-ը ամիսներով համատեղելիության կոդ կրելն է․ ես յուրաքանչյուր ժամանակավոր shim գրանցում եմ հեռացման պայմանով, որպեսզի այն իրոք ջնջվի։

S2Նախագծեք release-ի գործընթաց մեկ template-ից կառուցված հինգ հավելվածի համար։

Ստանդարտացրեք pipeline-ը, ոչ թե հավելվածները։ Յուրաքանչյուր հավելված ունի template-ից գեներացված նույն fastlane կամ EAS կոնֆիգուրացիան, իսկ հավելվածին հատուկ արժեքները՝ bundle ID-ներ, signing identity-ներ, store metadata, environment URL-ներ, փոքր config ֆայլում, secret-ները՝ CI-ում։ Մեկ ընդհանուր checklist ներառում է versioning, privacy label-ներ, demo account-ներ, release build-ի smoke test-եր և rollout-ի քայլեր․ յուրաքանչյուր հավելված ավելացնում է միայն իր հատուկ դեպքերը։ Template-ի փոփոխությունները, օրինակ՝ SDK upgrade-ը, նախ անցնում են մեկ pilot հավելվածի internal track-երով, ապա տարածվում մյուսներին։ Յուրաքանչյուր release tag է արվում և ունի կարճ changelog։ Trade-off․ միատեսակությունը երբեմն ուշացնում է մեկ հավելվածը, որը սպասում է ընդհանուր փոփոխությանը, բայց դա նշանակում է, որ ցանկացած engineer կարող է ship անել ցանկացած հավելված, իսկ store-ի կանոնի փոփոխությունը շտկվում է մեկ անգամ, ոչ թե հինգ։

S3Ե՞րբ է OTA update-ը սխալ գործիք, նույնիսկ եթե տեխնիկապես հնարավոր է։

Երբ այն էականորեն փոխում է այն, ինչ հաստատել է reviewer-ը՝ նոր վճարման հոսք, նոր feature-ի տարածք, հավելվածի նպատակի փոփոխություն, քանի որ store-ի կանոնները interpreted-code update-ները թույլ են տալիս միայն սահմաններում, և դրանք խախտելը վտանգում է հավելվածի հեռացմամբ։ Երբ fix-ը կախված է native կոդից, նոր permission-ից կամ փոխված SDK-ից, քանի որ runtime version-ը չի համընկնում։ Երբ update-ը մեծ է, կամ փոփոխությունն այնքան ռիսկային է, որ crash monitoring-ով staged store rollout-ն ավելի անվտանգ է։ Եվ երբ ձեր թիմին պակասում է rollback-ի կարգապահությունը․ OTA-ի սխալը րոպեների ընթացքում հասնում է օգտատերերին։ Ես OTA-ն օգտագործում եմ bug fix-երի, տեքստի և փոքր layout ուղղումների համար՝ միշտ աստիճանական rollout-ով, ստորագրված update-ներով և թեստավորված rollback-ի ճանապարհով, և պահում եմ գրավոր կանոն այն մասին, թե ինչն է համապատասխանում։

Բառարան

Bundle identifier
iOS հավելվածի եզակի reverse-domain ID, օրինակ՝ com.example.shop․ release-ից հետո այն հնարավոր չէ փոխել։
Provisioning profile
Apple-ի ֆայլ, որը կապում է certificate-ը, App ID-ն և capability-ները՝ թույլ տալով build-ին տեղադրվել կամ ship լինել։
Distribution certificate
Apple-ի certificate-ը և private key-ը, որոնցով ստորագրվում են build-երը TestFlight-ի և App Store-ի համար։
Upload key
Key, որով ստորագրում եք Android bundle-ները upload-ից առաջ․ Google-ը ստուգում է այն, ապա վերաստորագրում app signing key-ով։
Play App Signing
Google-ը պահում է ձեր app signing key-ը և ստորագրում իր առաքած APK-ները, ուստի կորած upload key-ը վերականգնելի է։
AAB
Android App Bundle․ upload-ի ֆորմատ, որից Google Play-ը յուրաքանչյուր սարքի համար գեներացնում է օպտիմալացված APK-ներ։
Phased release
Ավտոմատ update-ների Apple-ի յոթնօրյա աստիճանական rollout, որը կարելի է դադարեցնել ընդհանուր առմամբ մինչև 30 օր։
Runtime version
Binary-ի native կոդը նկարագրող պիտակ, որը երաշխավորում է, որ OTA update-ները հասնեն միայն համատեղելի build-երին։
Privacy manifest
PrivacyInfo.xcprivacy ֆայլ, որը հայտարարում է, թե ինչ տվյալներ է հավաքում հավելվածը կամ SDK-ն և ինչու է օգտագործում որոշ API-ներ։
Expedited review
Apple-ին ուղղված խնդրանք build-ն ավելի արագ review անելու, նախատեսված կրիտիկական fix-երի և ժամկետային event-ների համար։

Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։

Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։