In today’s fast-paced development environment, continuous integration and continuous deployment (CI/CD) are essential practices for maintaining efficiency and ensuring high-quality software delivery. For Flutter developers, deploying iOS apps can be a cumbersome process due to the complexities involved in setting up the build environment and managing Apple’s provisioning profiles and certificates. This is where GitHub Actions come into play, offering a powerful and flexible automation platform directly integrated with your GitHub repositories.
Recently, I made a significant pivot from native iOS development to using Flutter for my mobile app projects. During this transition, I discovered new strategies for optimizing the CI/CD process, making the deployment of iOS apps much more efficient. I want to share these findings with you to help streamline your own workflow.
In this tutorial, we will walk you through setting up a GitHub Actions workflow to automatically build and deploy test builds of your Flutter iOS app. By the end of this guide, you will have a seamless CI/CD pipeline that automatically handles the build and deployment process every time you push changes to your repository. This not only saves time but also ensures consistency and reliability in your deployment process.
Let’s dive in and see how we can achieve this step-by-step.
Step 1: Prerequisites
Before you start, ensure you have the following:
1. Flutter project set up and working locally.
2. Apple Developer Account with necessary certificates and provisioning profiles.
3. GitHub repository with your Flutter project.
4. GitHub Secrets configured with necessary values.
Step 2: Create GitHub Secrets
All secrets are base64 encoded, command to use:
base64 -i input_file -o output_file
• DISTRIBUTION_P12 – Your distribution certificate in base64 encoded format.
• P12_PASS – Password for your .p12 file.
• PROD_PROVISION – Your provisioning profile in base64 encoded format.
• AUTH_KEY – Apple authentication key in base64 encoded format.
• APPLE_KEY_ID – Your Apple key ID.
• APPLE_ISSUER_ID – Your Apple issuer ID.
Step 3: Create the GitHub Actions Workflow
GitHub Actions workflows are defined by jobs, each of which performs a series of steps in a specific environment. By creating a well-defined set of jobs, we can ensure that each stage of our CI/CD pipeline—from code checkout to build and deployment—is handled efficiently and reliably. By the end of this guide, you will have a seamless CI/CD pipeline that automatically handles the build and deployment process every time you push changes to your repository. This not only saves time but also ensures consistency and reliability in your deployment process.
Create a file named deploy-ios.yml inside .github/workflows/ directory in your repository and add first job:
name: Deploy iOS
on:
push:
branches:
- main
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
• name: Deploy iOS: This defines the name of the workflow. It will appear in the GitHub Actions tab in your repository, helping you identify this workflow among others.
• on:: This specifies the events that trigger the workflow. In this case, the workflow is triggered by a push event.
• push:: This indicates that the workflow should run whenever code is pushed to the repository.
• branches:: This specifies the branches that the workflow should monitor. Here, it’s set to trigger only when code is pushed to the main branch.
In order to deploy ipa file through terminal, we need to create an AppManager key through Apple developer account. Save that key as secret as well.
- name: Setup Flutter SDK
uses: flutter-actions/setup-flutter@v3.6
with:
flutter-version: "stable"
- name: Install dependencies
run: flutter pub get
- name: Install the Apple certificate and provisioning profile
env:
DISTRIBUTION_P12: ${{ secrets.DISTRIBUTION_P12 }}
P12_PASS: ${{ secrets.P12_PASS }}
PROD_PROVISION: ${{ secrets.PROD_PROVISION }}
AUTH_KEY: ${{ secrets.AUTH_KEY }}
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
run: |
# create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PP_PATH=$RUNNER_TEMP/prov.mobileprovision
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
echo -n "$DISTRIBUTION_P12" | base64 --decode -o $CERTIFICATE_PATH
echo -n "$PROD_PROVISION" | base64 --decode -o $PP_PATH
# import certificate and provisioning profile from secrets
security create-keychain -p "$P12_PASS" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$P12_PASS" $KEYCHAIN_PATH
# import certificate to keychain
security import $CERTIFICATE_PATH -P "$P12_PASS" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security set-key-partition-list -S apple-tool:,apple: -k "$P12_PASS" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
# apply provisioning profile
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
ls -la ~/Library/MobileDevice/Provisioning\ Profiles
mkdir -p ~/.private_keys
echo "$AUTH_KEY" | base64 --decode > ~/.private_keys/AuthKey_$APPLE_KEY_ID.p8
ls ~/.private_keys
- name: Build iOS
run: flutter build ipa --export-options-plist=ExportOptions.plist
- name: Deploy ipa
env:
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
AUTH_KEY_PATH: ~/.private_keys/AuthKey_***.p8
run: xcrun altool --upload-app --type ios -f build/ios/ipa/*.ipa --apiKey $APPLE_KEY_ID --apiIssuer $APPLE_ISSUER_ID --apiKeyFile $AUTH_KEY_PATH
After adding a basic intro job, we need to setup flutter, extract certificates and deploy ipa.
Step 4: Explanation of the Workflow
1. Trigger the workflow: The workflow triggers on a push to the main branch.
2. Set up the build environment: Use the latest macOS environment.
3. Check out the code: Use the actions/checkout@v4 action to check out the repository.
4. Set up Flutter: Use the flutter-actions/setup-flutter@v3.6 action to set up Flutter.
5. Install dependencies: Run flutter pub get to install Dart dependencies.
6. Install certificates and provisioning profiles:
• Decode and import the distribution certificate.
• Set up the provisioning profile.
• Create a keychain and import the certificate.
7. Build the iOS app: Use flutter build ipa to build the iOS app.
8. Deploy the ipa file: Use xcrun altool to upload the ipa file to App Store Connect.

Conclusion
This guide provides a straightforward method to automate the deployment of Flutter iOS apps using GitHub Actions. By following these steps, you can ensure a seamless and continuous integration and delivery process for your Flutter projects. In the next blog I will focus on deploying Android builds. Happy coding!
