Build, test, and deploy Xcode apps
Azure Pipelines | Azure DevOps Server 2019 | TFS 2018 | TFS 2017
This guidance explains how to automatically build Xcode projects.
For a working example of how to build an app with Xcode, import (into Azure Repos or TFS) or fork (into GitHub) this repo:
The sample code includes an
azure-pipelines.yml file at the root of the repository. You can use this file to build the app.
Follow all the instructions in Create your first pipeline to create a build pipeline for the sample app.
You can use Azure Pipelines to build your apps with Xcode without needing to set up any infrastructure of your own. Xcode is preinstalled on Microsoft-hosted macOS agents in Azure Pipelines. You can use the macOS agents to run your builds.
For the exact versions of Xcode that are preinstalled, refer to Microsoft-hosted agents.
Create a file named azure-pipelines.yml in the root of your repository. Then, add the following snippet to your
azure-pipelines.yml file to select the appropriate agent pool:
# https://docs.microsoft.com/azure/devops/pipelines/languages/xcode pool: vmImage: 'macOS-10.13'
Build an app with Xcode
To build an app with Xcode, add the following snippet to your
azure-pipelines.yml file. This is a minimal snippet for building an iOS project using its default scheme, for the Simulator, and without packaging. Change values to match your project configuration. See the Xcode task for more about these options.
variables: scheme: '' sdk: 'iphoneos' configuration: 'Release' steps: - task: Xcode@5 inputs: sdk: '$(sdk)' scheme: '$(scheme)' configuration: '$(configuration)' xcodeVersion: 'default' # Options: default, 10, 9, 8, specifyPath exportPath: '$(agent.buildDirectory)/output/$(sdk)/$(configuration)' packageApp: false
Signing and provisioning
An Xcode app must be signed and provisioned to run on a device or be published to the App Store. The signing and provisioning process needs access to your P12 signing certificate and one or more provisioning profiles. The Install Apple Certificate and Install Apple Provisioning Profile tasks make these available to Xcode during a build.
The following snippet installs an Apple P12 certificate and provisioning profile in the build agent's Keychain. Then, it builds, signs, and provisions the app with Xcode. Finally, the certificate and provisioning profile are automatically removed from the Keychain at the end of the build, regardless of whether the build succeeded or failed. For more details, see Sign your mobile app during CI.
# The `certSecureFile` and `provProfileSecureFile` files are uploaded to the Azure Pipelines secure files library where they are encrypted. # The `P12Password` variable is set in the Azure Pipelines pipeline editor and marked 'secret' to be encrypted. steps: - task: InstallAppleCertificate@2 inputs: certSecureFile: 'chrisid_iOSDev_Nov2018.p12' certPwd: $(P12Password) - task: InstallAppleProvisioningProfile@1 inputs: provProfileSecureFile: '6ffac825-ed27-47d0-8134-95fcf37a666c.mobileprovision' - task: Xcode@5 inputs: actions: 'build' scheme: '' sdk: 'iphoneos' configuration: 'Release' xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace' xcodeVersion: 'default' # Options: 8, 9, 10, default, specifyPath signingOption: 'default' # Options: nosign, default, manual, auto useXcpretty: 'false' # Makes it easier to diagnose build failures
If your project uses CocoaPods, you can run CocoaPods commands in your pipeline using a script, or with the CocoaPods task. The task optionally runs
pod repo update, then runs
pod install, and allows you to set a custom project directory. Following are common examples of using both.
- script: /usr/local/bin/pod install displayName: 'pod install using a script' - task: CocoaPods@0 displayName: 'pod install using the CocoaPods task with defaults' - task: CocoaPods@0 inputs: forceRepoUpdate: true projectDirectory: '$(system.defaultWorkingDirectory)' displayName: 'pod install using the CocoaPods task with a forced repo update and a custom project directory'
If your project uses Carthage with a private Carthage repository,
you can set up authentication by setting an environment variable named
GITHUB_ACCESS_TOKEN with a value of a token that has access to the repository.
Carthage will automatically detect and use this environment variable.
Do not add the secret token directly to your pipeline YAML. Instead, create a new pipeline variable with its lock enabled on the Variables pane to encrypt this value. See secret variables.
Here is an example that uses a secret variable named
myGitHubAccessToken for the value of the
GITHUB_ACCESS_TOKEN environment variable.
- script: carthage update --platform iOS env: GITHUB_ACCESS_TOKEN: $(myGitHubAccessToken)
Testing on Azure-hosted devices
# App Center test # Test app packages with Visual Studio App Center - task: AppCenterTest@1 inputs: appFile: #artifactsDirectory: '$(Build.ArtifactStagingDirectory)/AppCenterTest' #prepareTests: true # Optional #frameworkOption: 'appium' # Required when prepareTests == True# Options: appium, espresso, calabash, uitest, xcuitest #appiumBuildDirectory: # Required when prepareTests == True && Framework == Appium #espressoBuildDirectory: # Optional #espressoTestApkFile: # Optional #calabashProjectDirectory: # Required when prepareTests == True && Framework == Calabash #calabashConfigFile: # Optional #calabashProfile: # Optional #calabashSkipConfigCheck: # Optional #uiTestBuildDirectory: # Required when prepareTests == True && Framework == Uitest #uitestStorePath: # Optional #uiTestStorePassword: # Optional #uitestKeyAlias: # Optional #uiTestKeyPassword: # Optional #uiTestToolsDirectory: # Optional #signInfo: # Optional #xcUITestBuildDirectory: # Optional #xcUITestIpaFile: # Optional #prepareOptions: # Optional #runTests: true # Optional #credentialsOption: 'serviceEndpoint' # Required when runTests == True# Options: serviceEndpoint, inputs #serverEndpoint: # Required when runTests == True && CredsType == ServiceEndpoint #username: # Required when runTests == True && CredsType == Inputs #password: # Required when runTests == True && CredsType == Inputs #appSlug: # Required when runTests == True #devices: # Required when runTests == True #series: 'master' # Optional #dsymDirectory: # Optional #localeOption: 'en_US' # Required when runTests == True# Options: da_DK, nl_NL, en_GB, en_US, fr_FR, de_DE, ja_JP, ru_RU, es_MX, es_ES, user #userDefinedLocale: # Optional #loginOptions: # Optional #runOptions: # Optional #skipWaitingForResults: # Optional #cliFile: # Optional #showDebugOutput: # Optional
Retain artifacts with the build record
- task: CopyFiles@2 inputs: contents: '**/*.ipa' targetFolder: '$(build.artifactStagingDirectory)' - task: PublishBuildArtifacts@1
Add the App Center Distribute task to distribute an app to a group of testers or beta users, or promote the app to Intune or the Apple App Store. A free App Center account is required (no payment is necessary).
# App Center distribute # Distribute app builds to testers and users via Visual Studio App Center - task: AppCenterDistribute@1 inputs: serverEndpoint: appSlug: appFile: #symbolsOption: 'Apple' # Optional. Options: apple #symbolsPath: # Optional #symbolsPdbFiles: '**/*.pdb' # Optional #symbolsDsymFiles: # Optional #symbolsMappingTxtFile: # Optional #symbolsIncludeParentDirectory: # Optional #releaseNotesOption: 'input' # Options: input, file #releaseNotesInput: # Required when releaseNotesOption == Input #releaseNotesFile: # Required when releaseNotesOption == File #isMandatory: false # Optional #distributionGroupId: # Optional
Apple App Store
Install the Apple App Store extension and use the following tasks to automate interaction with the App Store. By default, these tasks authenticate to Apple using a service connection that you configure.
Add the App Store Release task to automate the release of updates to existing iOS TestFlight beta apps or production apps in the App Store.
See limitations of using this task with Apple two-factor authentication, since Apple authentication is region specific and fastlane session tokens expire quickly and must be recreated and reconfigured.
- task: AppStoreRelease@1 displayName: 'Publish to the App Store TestFlight track' inputs: serviceEndpoint: 'My Apple App Store service connection' # This service connection must be added by you appIdentifier: com.yourorganization.testapplication.etc ipaPath: '$(build.artifactstagingdirectory)/**/*.ipa' shouldSkipWaitingForProcessing: true shouldSkipSubmission: true
Add the App Store Promote task to automate the promotion of a previously submitted app from iTunes Connect to the App Store.
- task: AppStorePromote@1 displayName: 'Submit to the App Store for review' inputs: serviceEndpoint: 'My Apple App Store service connection' # This service connection must be added by you appIdentifier: com.yourorganization.testapplication.etc shouldAutoRelease: false