- GitHub
- GitHub Actions
- Flutter
- iOS
While developing your Flutter project hosted on GitHub you can easily set up a GitHub Actions to regularly build and distribute your application to your users.
To achieve your goal you have to follow these steps:
.xarchive.ipa from the .xarchiveTo be able to complete this tutorial you need to:
.p12) with the associated passwordWith that ready, let’s get started!
The idea is to automate this process using GitHub Actions, this process is called continuous integration.
The interest of using GitHub Actions is that:
We will create something called a GitHub Action that will allow you to generate your ipa with just single click.
Let’s create our first workflow!
Inside your project, you need to create a workflows folder inside the .github folder and then create a new file called: ios-release.yml for instance. This file will contain our first job called build_ios:
name: Flutter_iOS
on:
push:
branches: [main]
jobs:
build_ios:
runs-on: macos-latest
steps:
- name: Checkout the code
uses: actions/checkout@v2
This job will be triggered when you push new changes on the main branch. The first step that we will do is to check out the code of our branch.
You need to use something called secrets. This allows you to store these files securely in order to access your .p12 certificate and your provisioning profile for your application. If you haven’t used secrets before, checkout this previous article.
Let’s define one for the .p12 certificate called P12_BASE64 and one for the associated password called P12_PASSWORD.
Next step is to install your Apple Certificate, to do so we will use an action from the community called apple-actions/import-codesign-certs@v1 and use our previously defined secrets:
- name: Install Apple Certificate
uses: apple-actions/import-codesign-certs@v1
with:
p12-file-base64: ${{ secrets.P12_BASE64 }}
p12-password: ${{ secrets.P12_PASSWORD }}
To sign the application we also need to have our provisioning profile installed:
- name: Install the provisioning profile
env:
PROVISIONING_CERTIFICATE_BASE64: ${{ secrets.PROVISIONING_PROFILE_BASE64 }}
run: |
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
echo -n "$PROVISIONING_CERTIFICATE_BASE64" | base64 --decode --output $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
As you can see above, this script has 3 operations:
secretsTo be able to use Flutter in our workflow we need to install it. In order to reach this we will use another action from the community:
- name: Install and set Flutter version
uses: subosito/flutter-action@v1.4.0
with:
flutter-version: '2.0.1'
We need to add this action and specify the version of Flutter we want to use. It’s recommanded to fix this version instead of using stable as value to avoid potential breaking changes when a new version is published.
Now we are able to restore the packages for our application:
- name: Restore packages
run: flutter pub get
Once they have been retrieved, we can build the application in release mode without signing it:
- name: Build Flutter
run: flutter build ios --release --no-codesign
In fact we will apply our certificates previously installed in the next step!
First thing, open your iOS project using XCode and select the target and inside Signing & Capabilities make sure Automatically manage signing is unchecked so we can sign it using the certificates we want without editing the XCode project.
Next, when you sign an iOS application you don’t sign the pods associated to it so you need to specify it inside your Podfile like this:
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_REQUIRED'] = "NO"
config.build_settings['CODE_SIGNING_ALLOWED'] = "NO"
end
end
end
Before we can generate our xarchive, we need to resolve the Swift dependencies of our project. This is useful specially when you have some Flutter iOS plugins written in Swift.
- name: Build resolve Swift dependencies
run: xcodebuild -resolvePackageDependencies -workspace ios/Runner.xcworkspace -scheme Runner -configuration Release
Now to create an xarchive you need to find some information that can be found in your Provisioning Profile or in your Apple Certificate:
With all of that done, we can use it like this:
- name: Build xArchive
run: |
xcodebuild -workspace ios/Runner.xcworkspace -scheme Runner -configuration Release DEVELOPMENT_TEAM=YOUR_TEAM_ID -sdk 'iphoneos' -destination 'generic/platform=iOS' -archivePath build-output/app.xcarchive PROVISIONING_PROFILE=YOUR_UUID clean archive CODE_SIGN_IDENTITY="Apple Distribution: Damien Aicheh"
With the xarchive generated we are able to export it as an ipa. To achieve that we need to add a new file called ExportOptions.plist to our project to specify the export options.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string> <!-- app-store, ad-hoc, enterprise, development -->
<key>teamID</key>
<string>YOUR_TEAM_ID</string>
<key>signingStyle</key>
<string>manual</string>
<key>provisioningProfiles</key>
<dict>
<key>YOUR_BUNDLE_ID</key>
<string>YOUR_UUID</string>
</dict>
</dict>
</plist>
Depending on your project configurations, you may have to add some more options to this file. You can have one ExportOptions.plist file for each environment of your project if you need that.
Then just run this command line and your ipa will be generated:
- name: Export ipa
run: xcodebuild -exportArchive -archivePath build-output/app.xcarchive -exportPath build-output/ios -exportOptionsPlist ios/ExportOptions.plist
To get access to the ipa generated previously from the GitHub interface let’s add this final action:
- name: Publish iOS Artefacts
uses: actions/upload-artifact@v1
with:
name: release-ios
path: build-output/ios
This will publish the ios folder that contain our package. Then you can install it on your device.

Now you are ready to share your application with your users depending on the context of your project! You will find an example code in this Github repository.