Build and upload a Flutter iOS app using GitHub Actions
March 28, 2024
What You'll Need
Apple ID
- The ID you use to log into apple developer.
- You should already have this if you've registered an developer account.
App-specific password
- A password for app use.
- Can be created here (under the App-Specific Passwords section):
- Reference:
Bundle ID
- Create a new Identifier here:
- Reference:
Certificate
- Create a new Certificate here:
- Export a .p12 file from the Keychain.
Certificate password
- You'll be asked for this when exporting the .p12 file.
- Specify any password you prefer.
Profile
- Create a new Profile here:
App
- Create a new app here:
Also, I've set environment variables in the GitHub repository's Secrets.
- ENVIRON_JSON
- Set in JSON format:
{
"API_URL":"https://xxxx.com",
}
Steps:
Set up Profile, Identifier, etc., in Xcode.
- Reference:
Create an ExportOptions.plist
- You can find this by archiving once in Xcode and downloading it to your local machine.
- Necessary for creating the ipa file.
Create base64 strings for the Certificate and Profile.
- You can do this by running the following command in the terminal:
base64 -i <file_path>
Set up the GitHub Actions workflow.
- Create a new file in the
.github/workflows
directory. - Set Secrets in the repository settings.
Source
Based on the following:
.github/workflows/ios_cd.yaml
name: CD_iOS
on:
workflow_dispatch:
inputs:
build_number:
description: 'Build Number'
required: true
type: number
jobs:
build_iOS:
name: Build for iOS
runs-on: macos-13
timeout-minutes: 20
steps:
- name: Check out
uses: actions/checkout@v4
- name: Install the Apple certificate and provisioning profile
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.CERTIFICATES_P12 }}
P12_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.PROVISIONING_PROFILE }}
KEYCHAIN_PASSWORD: ${{ secrets.APPLE_APP_PASS }}
run: |
# create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
# import certificate and provisioning profile from secrets
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# import certificate to keychain
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $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
- name: Install Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.19.3'
channel: 'stable'
cache: true
- name: Install Flutter dependencies
run: flutter pub get
- name: Run build_runner build
run: dart run build_runner build --delete-conflicting-outputs
- name: Create environ.json
env:
ENVIRON_JSON: ${{ secrets.ENVIRON_JSON }}
run: echo "$ENVIRON_JSON" > ./environ.json
- name: Building IPA
run: |
flutter build ipa --release \
--dart-define-from-file=environ.json \
--export-options-plist=ios/Runner/ExportOptions.plist \
--build-number ${{ github.event.inputs.build_number }}
- name: Upload to AppStoreConnect
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_PASS: ${{ secrets.APPLE_APP_PASS }}
run: xcrun altool --upload-app -f "./build/ios/ipa/noah_mobile_frontend.ipa" -t ios -u "$APPLE_ID" -p "$APPLE_APP_PASS"