Setting Up AppCenter for Mobile App CI/CD
Microsoft AppCenter is not just a test build distribution platform. It's a full CI/CD for mobile apps: build from Git trigger, testing on real devices via Device Farm, publishing to TestFlight and Google Play, crash analytics. Everything in one service without infrastructure setup.
Project Structure in AppCenter
For each app (iOS and Android), a separate "app" is created in AppCenter. The organization is called "org" — all apps are grouped under it. Typical structure:
MyCompany (Organization)
├── MyApp-iOS (App)
│ ├── Build (CI)
│ ├── Test (Device Farm)
│ ├── Distribute
│ └── Diagnostics (Crashes)
└── MyApp-Android (App)
├── Build (CI)
├── Distribute
└── Diagnostics
Setting Up Build via YAML
AppCenter supports appcenter-post-clone.sh, appcenter-pre-build.sh, appcenter-post-build.sh — scripts executed at corresponding stages. A more flexible approach — appcenter.yml (Preview):
trigger:
branches:
include:
- main
- release/*
pool:
vmImage: macos-latest
steps:
- task: InstallAppleCertificate@2
inputs:
certSecureFile: distribution.p12
certPwd: $(P12_PASSWORD)
- task: InstallAppleProvisioningProfile@1
inputs:
provisioningProfileLocation: secureFiles
provProfileSecureFile: MyApp_AppStore.mobileprovision
- script: |
cd ios && pod install --repo-update
displayName: 'Install CocoaPods'
- task: Xcode@5
inputs:
actions: 'build'
scheme: 'MyApp'
xcWorkspacePath: 'ios/MyApp.xcworkspace'
exportPath: '$(Build.ArtifactStagingDirectory)'
exportOptions: 'plist'
exportOptionsPlist: 'ios/ExportOptions.plist'
signingOption: 'manual'
signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)'
provisioningProfileUuid: '$(APPLE_PROV_PROFILE_UUID)'
Post-build Scripts
If YAML configuration is unavailable, appcenter-post-build.sh is called after successful build:
#!/usr/bin/env bash
# Send to App Distribution
appcenter distribute release \
--app "$APPCENTER_APP_ID" \
--file "$APPCENTER_OUTPUT_DIRECTORY/MyApp.ipa" \
--group "QA Team" \
--release-notes "Build $APPCENTER_BUILD_ID"
# Slack notification
curl -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-type: application/json' \
--data "{\"text\":\"New build $APPCENTER_BUILD_ID is available\"}"
APPCENTER_OUTPUT_DIRECTORY, APPCENTER_BUILD_ID — AppCenter built-in environment variables.
Environment Variables and Secrets
Build Configuration → Environment Variables. Sensitive values marked as "Secret" — encrypted and hidden in logs. Standard set:
-
P12_PASSWORD— certificate password -
KEYSTORE_PASSWORD— Android keystore -
FIREBASE_APP_ID— for Crashlytics/App Distribution -
SLACK_WEBHOOK_URL— notifications
Testing on Real Devices
AppCenter Device Farm allows running UI tests (XCUITest, Espresso) on real devices:
# Upload and run XCUITest
appcenter test run xcuitest \
--app "MyOrg/MyApp-iOS" \
--devices "MyOrg/top-ios-devices" \
--test-series "main" \
--locale "ru_RU" \
--build-dir DerivedData/Build/Products/Debug-iphoneos
Device sets are created in AppCenter UI — you can select specific models and iOS/Android versions.
AppCenter CI Limitations
AppCenter Build doesn't support complex matrix builds, custom Docker images for Android, Gradle caching at organization level. For advanced scenarios, AppCenter CI is combined with external CI (GitHub Actions for main build, AppCenter — only for distribution and testing).
Process
Create apps in AppCenter → connect repository → configure Build → add signing (for iOS) → write post-build scripts → configure Distribution → test run → write documentation.
Timeline: 2–3 days. Cost is calculated individually.







