The easiest way to build a Flutter iOS app using GitHub Actions, plus a key takeaway for developers
The last few months I have been working on a Flutter mobile app. Whenever you are starting a new project, at one point, you’ll need to setup your CI/CD pipelines. For mobile apps, building and publishing (test) releases manually can be tedious. Releases need to be signed to prove authenticity. The certificates and provisioning profiles to do this are best kept secure, so you don’t want to share these with every team member. The solution is to setup your build pipeline to build, sign and distribute your app package. This way any team member can (automatically) trigger a build while keeping your certificates secure.
A few months ago I set on this mission to set up a GitHub workflow to build our Flutter iOS app and deploy it to Firebase App Distribution.
This was the first time I used GitHub workflows to build a mobile application. Of course I started with Googling something like “GitHub workflow flutter iOS” and went on from there. However, I made a key mistake here: I followed the articles and tutorials I found on Google instead of consulting the official documentation of GitHub and Flutter. Every article used a different way to install the iOS certificate and provisioning profile, a different way to build the iOS application (most used external tools like Codemagic or Fastlane) and a different way to sign the application. However, I couldn’t get the signing to work. After too many test runs on GitHub (a time consuming and frustrating process) I decided to call it a day. A good night’s sleep later, I knew where to start instead: the official GitHub and Flutter documentation. That’s where I found the easiest solution to the problem I was trying to solve, got it working in a few minutes and decided to write this article with the key takeaway below.
The key takeaway
Be inspired by online articles and tutorials, but then read the official documentation of the tools and platforms you are using before you start with the implementation.
Tech articles and tutorials can become quickly out of date. Also, they can be opinionated (by the use of certain framework or tools) and can set you on the wrong track. Of course, there is also tremendous value in them, it helps to see the steps someone else took and the frameworks and tools used might save you a lot of time. This is true for CI/CD pipelines, but also for coding problems.
The easiest way to build a signed Flutter iOS app using GitHub Actions
While I suggest you read the official documentation, I will still show you the easiest way (in my opinion) to build your Flutter iOS app. I will also show you the steps I took and documentation I used. This way you can be inspired by this article and double check the sources when you start implementing your own GitHub workflow.
Firstly, I assume you:
- Are familiar with the different iOS build types (development, ad-hoc, app store, and enterprise)
- Know how to create an iOS build certificate and provisioning profile
- Know how to export the certificate as
.p12
file (password protected).
If not, read the official Apple documentation here: https://help.apple.com/xcode/mac/current/#/dev154b28f09
Secondly, I assume you know how to use this certificate and profile to build and sign your flutter application locally. If not, see: https://docs.flutter.dev/deployment/ios#create-a-build-archive-with-xcode
I started with this page: https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development
The documentation of GitHub tells you to include your iOS distribution certificate and provisioning profile as base64 string to you GitHub secrets. I entered the following entries in our “GitHub Actions secrets”:
APPSTORE_CERT_BASE64
: Base64 string of the iOS certificate p12 file.APPSTORE_CERT_PASSWORD
: Certificate password.MOBILEPROVISION_BASE64
: The iOS provisioning profile you want to use.
We will use these entries to install the certificate and provisioning profile in your workflow runner.
Next, create a new workflow file and use the information from GitHub and your secrets to get something like this:
This script will install the certificate and provisioning profile on the workflow runner so they can be used by Flutter to sign your application.
Important! Do not forget to add the cleanup step with the if: ${{ always() }}
condition. This removes the certificates from the workflow runner regardless if the earlier steps fail. That way your certificate and profile will not be at risk.
Now we need a way to build and sign our Flutter iOS application. We know how to this locally from the official documentation: https://docs.flutter.dev/deployment/ios#create-a-build-archive-with-xcode
What is not (yet) mentioned in the documentation, you can build and sign an ipa file with just one command:
flutter build ipa — export-options-plist=absolute/path/to/ExportOptions.plist
The ExportOptions.plist file is generated the first time you build and sign your application locally via Flutter CLI and Xcode in the same folder as where your ipa is created. This useful response from John on StackOverflow https://stackoverflow.com/a/69919068 pointed me in that direction.
Further investigation shows that if you’ve set up your Xcode project correctly even the export-options-plist
is obsolete. See the message of this merged pull request in the flutter repo: https://github.com/flutter/flutter/pull/97243
The second takeaway of this article: when using open source frameworks, even the official documentation is sometimes out-of-date. Searching through GitHub issue discussions and pull request can get you a little further. The PR above is merged the 2nd of February 2022, so I expect the documentation to be updated soon.
Using export-options-plist
gives us the option to configure the build type, certificate and provisioning profile. Therefore, I will use the method with the ExportOptions.plist
file.
Joseph Muller explains this further in his medium article: https://jtmuller5-98869.medium.com/flutter-build-an-ipa-90520e813a96 let him inspire you!
Assuming your ExportOptions.plist
is located at:
{flutter_root}\ios\Runner\ExportOptions.plist
We can now build and sign our iOS app with this command:
flutter build ipa — release — export-options-plist=ios/Runner/ExportOptions.plist
We’ll add a step to upload the .ipa file as GitHub artifact and a “release” job to upload the file to FireBase App Distribution to come to the final workflow yaml file below:
This is the only YAML necessary to build your Flutter applications automatically! To publish to Firebase I use the wzieba/Firebase-Distribution-Github-Action, this is not in the scope of this article, however, the linked documentation will help you to set it up correctly.
Have fun coding and building your Flutter applications, but remember: Read the f***ing manual!
If you have any questions or need help with setting up your workflow, let me know in the comments.