Automating Unity Builds to iOS, macOS, and visionOS
When deploying a Unity game to Apple platforms, you can easily get bogged down with provisioning profiles, signing certificates, and Xcode configuration. It's a time complex consuming process, and manually repeating it for every build is going to eat up your time and slow your team down.
In this guide, we'll walk through how to use GitHub Actions and Buildalon to automate your builds for iOS, macOS, and visionOS, covering everything from setting up your Apple Developer account to creating a workflow that deploys straight to your testers.
Table of Contents
- Prerequisites
- Setup Overview
- Setting up App Store Connect
- Generating a Build for Sideloading
- Deploying to TestFlight
Prerequisites
Before we dive into the automation, there are a few things you'll need:
- Apple Developer Account: You need an active membership to deploy to Apple devices or the App Store. Enroll in the Apple Developer Program.
- Working Unity Build: You should already have a basic automated build pipeline. If not, follow our Automating Unity Builds with GitHub Actions guide first.
Setup Overview
Automating Xcode builds involves connecting several services with your GitHub workflow. At a high level we need:
- Registration: We need to register the App ID and create an app record in App Store Connect so Apple knows what we are building.
- Authentication: We will create an App Store Connect API Key to allow GitHub Actions to act on our behalf without 2FA issues.
- Update the Build Pipeline:
- Unity Build: Unity runs in batch mode to export an Xcode project. It's important to set the
bundleVersionandbuildNumberhere to track our releases. - Xcode Build: We use
xcodebuild(wrapped in a GitHub Action) to compile, sign, and archive the app into an.ipa(or.app/.pkgfor macOS) file.
- Unity Build: Unity runs in batch mode to export an Xcode project. It's important to set the
- Distribution: Finally, we either upload the build to TestFlight for beta testing or keep it as an artifact for manual sideloading.
The key to making this easy is the buildalon/unity-xcode-builder action, which handles the complex Xcode build and upload steps for us.
Creating the App on Apple's Portal
Before automation can take over, we need to do a little manual setup to tell Apple about our app. This is a one-time process for each new project.
- Log in to App Store Connect.
- Go to My Apps.
- Click the blue (+) button > New App.
- Select your platform (iOS, macOS, or visionOS) and give it a Name.
- In the Bundle ID section, you can choose an existing identifier or create a new one. Ensure this matches the Bundle Identifier in your Unity project (e.g.,
com.mycompany.mygame). - Enter a SKU (a unique tracking ID for your own use) and click Create.
Setting up App Store Connect Authentication
For GitHub Actions to upload builds on your behalf, it needs a secure way to authenticate with Apple. The best way to do this is with an App Store Connect API Key, which avoids the headaches of 2FA in CI environments.
1. Generate an API Key
- Head over to App Store Connect.
- Go to Users and Access > Integrations > Team Keys.
- Click the + button to generate a new key.
- Give it a name like "GitHub Actions" and assign the App Manager role.
- Download the
.p8file. Keep this safe! You can only download it once. - Take note of the Key ID and your Issuer ID.
2. Base64 Encode the Key
GitHub Secrets can sometimes struggle with the newlines in the .p8 file. A robust way to handle this is to Base64 encode the file content.
Run this command in your terminal (macOS/Linux):
openssl base64 -in AuthKey_XXXXXXXXXX.p8 -out AuthKey_Base64.txt
On Windows (PowerShell):
[Convert]::ToBase64String([IO.File]::ReadAllBytes("AuthKey_XXXXXXXXXX.p8")) | Set-Content AuthKey_Base64.txt
Copy the contents of the generated text file.
3. Add Secrets to GitHub
Go to your repository on GitHub, navigate to Settings > Secrets and variables > Actions, and add the following secrets:
-
APP_STORE_CONNECT_KEY: The Base64 encoded string from the previous step. -
APP_STORE_CONNECT_KEY_ID: The Key ID (e.g.,D383SF739). -
APP_STORE_CONNECT_ISSUER_ID: The Issuer UUID (e.g.,6053b7fe...). -
APPLE_TEAM_ID: Your Team ID (found in Apple Developer Portal under Membership).
If you notice your build failing with authentication errors, double-check that you copied the stored secret correctly and that it doesn't contain any extra whitespace.
Generating a Build for Sideloading
Before messing with TestFlight, it's often easier to just get an installable build (an .ipa for iOS/visionOS, or .app/.pkg for macOS) that you can install directly. This is great for rapid iteration or testing on a few specific devices.
1. The Workflow
Now let's update your build workflow to add steps for building and signing your app. If you don't have a workflow yet, see our Automating Unity Builds article to get started.
Choosing a Runner:
To build for Apple platforms, you must use a macOS runner. You have two main options:
-
runs-on: macos-latest: GitHub's hosted runners. They are convenient for one-off builds, but can be slow and expensive if you're building frequently. -
runs-on: buildalon-macos: Buildalon's runners. These will cache your Unity builds, often completing much faster.
In this example, we'll use buildalon-macos for best performance.
A Note on Build Numbers:
Unity projects use the build number set in the editor (ProjectSettings.asset) by default. In a CI/CD pipeline, it's best practice to override this so every build has a unique identifier. In the example below, we'll use ${{ github.run_number }} as the build number, while keeping the version string (e.g. 1.0) defined in your project settings.
jobs:
build-iOS:
name: Build iOS
runs-on: buildalon-macos
steps:
- uses: actions/checkout@v6
with:
lfs: true
# Setup Unity, License, and Build the Project
# See our "Automating Unity Builds" article for details on these steps
- uses: buildalon/unity-setup@v2
with:
# Use 'iOS', 'Standalone', or 'VisionOS'
build-targets: iOS
- uses: buildalon/activate-unity-license@v2
with:
license: 'Personal'
username: '${{ secrets.UNITY_EMAIL }}'
password: '${{ secrets.UNITY_PASSWORD }}'
- uses: buildalon/unity-action@v3
name: Build Unity Project
with:
# Use 'iOS', 'StandaloneOSX', or 'VisionOS'
build-target: iOS
# Use run_number as the build number (e.g. 1.0.42)
args: -quit -batchmode -executeMethod Buildalon.Editor.BuildPipeline.UnityPlayerBuildTools.StartCommandLineBuild -buildNumber ${{ github.run_number }}
log-name: iOS-Build
# Build the XCode project and sign it
- uses: buildalon/unity-xcode-builder@v1
id: xcode-build
with:
project-path: ${{ github.workspace }}/Builds/iOS/**/*.xcodeproj
# 'development' creates a build signed for specific devices
export-option: development
team-id: ${{ secrets.APPLE_TEAM_ID }}
app-store-connect-key: ${{ secrets.APP_STORE_CONNECT_KEY }}
app-store-connect-key-id: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}
app-store-connect-issuer-id: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
- uses: actions/upload-artifact@v6
with:
name: iOS-Build
path: ${{ steps.xcode-build.outputs.executable }}
2. Grab the Artifact
Once the workflow finishes successfully, you can find your build file waiting for you in the Summary page of the workflow run.
The Catch: To install a development build, the destination device's UDID must be registered in the Apple Developer Portal before the build runs. If you get a new device, you'll need to add it to the portal and trigger a fresh build to update the provisioning profile.
3. Installing the Build
Since this is a raw development build, you can't just tap a link to install it. You'll need to use one of the following methods:
- iOS/visionOS:
- Finder (macOS): Connect your device via USB. In Finder, select your device from the sidebar and drag the
.ipaonto the general information window.
- Finder (macOS): Connect your device via USB. In Finder, select your device from the sidebar and drag the
- macOS:
- Simply download and unzip the artifact, then run the
.appor installer. Note that you may need to right-click and select "Open" to bypass security warnings if it's not notarized yet.
- Simply download and unzip the artifact, then run the
Deploying to TestFlight
Sideloading is great for quick tests, but for beta testing with your team (or the world), TestFlight is the way to go. This works for iOS, tvOS, visionOS, and macOS apps.
1. Create a Testing Group
First, organize your testers in App Store Connect.
- Navigate to App Store Connect > My Apps > Select your App > TestFlight.
- Under Internal Testing, click the (+) button to create a group (e.g., "Internal Testers").
- Add your team members to this group.
2. Update the Workflow
Now we just need to tweak our workflow to tell it to upload to App Store Connect instead of just signing for development. Change the export-option to app-store-connect and specify your test group:
- uses: buildalon/unity-xcode-builder@v1
with:
project-path: ${{ github.workspace }}/Builds/iOS/**/*.xcodeproj
export-option: app-store-connect
# ... credentials ...
# Automatically invite this group when the build processes
test-groups: "Internal Testers"
Once the build finishes, Buildalon will upload the archive. Apple usually takes a few minutes to process it. Once that's done, your "Internal Testers" will get an email, and they can start testing immediately.
3. Download & Play
Your testers just need to grab the TestFlight app from the App Store. Once they accept the invite, your game will appear there, ready to install.
Tip: Want to tell your testers what's new? Add the
release-notesparameter to thebuildalon/unity-xcode-builderstep. You can even pass${{ github.event.head_commit.message }}to automatically use your commit message!
About Buildalon
Buildalon provides verified GitHub Actions and dedicated build infrastructure to streamline Unity development. Register for Buildalon to get support with your Unity build automation needs.
Top comments (0)