Automate Flutter App Deployment on iOS to TestFlight using Fastlane and Semaphore

Prerequisites

  • An existing Flutter project; you can use our starter app or create one by running flutter create my_app
  • An Apple Developer Account (a.k.a. developer account) for the developer certificates and provisioning profiles–costs $99/year

Preparing a Flutter (iOS) App

TestFlight

Previously, we discussed how to release your Flutter (iOS) apps to Firebase using Adhoc releases. Now, we will create an iOS deployment using App Store releases that allow us to upload and the process builds to TestFlight.

Photo by TestFlight.

Creating a new bundle identifier

Bundle identifiers or bundle IDs allow you to uniquely identify your apps.

Creating a new bundle identifier

Bundle identifiers or bundle IDs allow you to uniquely identify your apps.

Creating a new app

After creating the bundle identifier for your app, it’s now time to create the app on App Store Connect, where you will also manage app store listings and releases.

Assigning the bundle identifier

Now, navigate to the ios directory and open Runner.xcworkspace.

Setting up fastlane

fastlane is an open-source tool that simplifies the complex process of creating releases for mobile. For iOS, it comes in handy with the tool called fastlane match, which does all the heavy lifting of managing iOS certificates and provisioning profiles.

Installation

Before continuing, make sure you have fastlane installed on your development machine. If you don’t, follow the steps outlined here to install it.

Setting up fastlane for iOS

To initialize fastlane, run the following:

cd ios && fastlane init && cd
  1. Select manual setup:
What would you like to use fastlane for?
1. 📸 Automate screenshots
2. 👩‍✈️ Automate beta distribution to TestFlight
3. 🚀 Automate App Store distribution
4. 🛠 Manual setup - manually setup your project to automate your tasks
>>> 4

Setting up fastlane match

To initialize fastlane match, run the following:

fastlane match init
fastlane match supports multiple storage modes, please select the one you want to use:
1. git
2. google_cloud
3. s3
type("appstore")

Generating fastlane match credentials

Now that you have set up a fastlane match, it’s time to generate the provisioning profiles and certificates for your app.

app_identifier(["com.yourapp.example"])
fastlane match appstore
fastlane match appstore -a com.yourapp.example

Setting environment variables

Below you can see an example of how Fastlane loads environment variables.

example_value = ENV["EXAMPLE_VALUE"]
sem create secret semaphore-flutter2-env \
-e MATCH_GIT_URL="<YOUR_MATCH_GIT_URL>" \
-e MATCH_PASSWORD="<YOUR_MATCH_PASSWORD>" \
-e MATCH_GIT_AUTHORIZATION="<YOUR_GIT_AUTHORIZTION_TOKEN>" \
-e FASTLANE_USER="<YOUR_APPLE_ID_EMAIL>" \
-e FASTLANE_PASSWORD="<YOUR_APPLE_ID_PASSWORD>"\
-e FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD="<YOUR_APPLE_APP_SPECIFIC_PASSWORD"
git_authorization = ENV["MATCH_GIT_AUTHORIZATION"]
team_id = ENV["TEAM_ID"]
app_id = ENV["APP_ID"]
app_identifier = ENV["APP_IDENTIFIER"]
provisioning_profile_specifier = ENV["PROVISIONING_PROFILES_SPECIFIER"]
temp_keychain_user = "temp"
temp_keychain_password = "temp"

Creating fastlane deploy lane

Let’s go ahead and set up workflows on fastlane.

# This is where the environment variables are located(truncated)# Add the followingdef delete_temp_keychain(name)
delete_keychain(
name: name
) if File.exist? File.expand_path("~/Library/Keychains/#{name}-db")
end
def create_temp_keychain(name, password)
create_keychain(
name: name,
password: password,
unlock: false,
timeout: 0
)
end
def ensure_temp_keychain(name, password)
delete_temp_keychain(name)
create_temp_keychain(name, password)
end
platform :ios do
lane :deploy do
# Step 1 - Create keychains
keychain_name = temp_keychain_user
keychain_password = temp_keychain_password
ensure_temp_keychain(keychain_name, keychain_password)
# Step 2 - Download provisioning profiles and certificates
match(
type: 'appstore',
app_identifier: app_identifier,
git_basic_authorization: Base64.strict_encode64(git_authorization),
readonly: true,
keychain_name: keychain_name,
keychain_password: keychain_password
)
# Step 3 - Build the project
gym(
configuration: "Release",
workspace: "Runner.xcworkspace",
scheme: "Runner",
export_method: "app-store",
export_options: {
provisioningProfiles: {
app_id => provisioning_profile_specifier,
}
}
)
# Step 4 - Upload the project
pilot(
apple_id: "#{app_id}",
app_identifier: "#{app_identifier}",
skip_waiting_for_build_processing: true,
skip_submission: true,
distribute_external: false,
notify_external_testers: false,
ipa: "./Runner.ipa"
)
# Step 5 - Delete temporary keychains
delete_temp_keychain(keychain_name)
end
end

Deploying to Semaphore

Semaphore

We’re now going to automate our deployments using Semaphore. Semaphore supports a wide-range of platforms and programming languages and is reliable and fast for your mobile development needs. Semaphore is fast and works well for mobile app distribution with TestFlight.

Creating your Semaphore project

In the navigation bar, click “Create New +”.

Setting up continuous integration pipeline

Your continuous integration pipeline will check if a change is stable before merging it into the main branch by running a series of build checks, lints and tests.

checkout
cache restore flutter-packages-$SEMAPHORE_GIT_BRANCH-$(checksum pubspec.yaml),flutter-packages-$(checksum pubspec.yaml),flutter-packages
flutter pub get
cache store flutter-packages-$SEMAPHORE_GIT_BRANCH-$(checksum pubspec.yaml),flutter-packages-$(checksum pubspec.yaml),flutter-packages /root/.pub-cache
flutter format --set-exit-if-changed .
flutter analyze .
checkout
cache restore flutter-packages-$SEMAPHORE_GIT_BRANCH-$(checksum pubspec.yaml),flutter-packages-$(checksum pubspec.yaml),flutter-packages
flutter pub get
flutter test test
checkout
cache restore flutter-packages-$SEMAPHORE_GIT_BRANCH-$(checksum pubspec.yaml),flutter-packages-$(checksum pubspec.yaml),flutter-packages
flutter pub get

Setting up continuous deployment pipeline

Your continuous deployment pipeline will handle the promotions, automatic or manual depending on your needs, and will execute the fastlane deploy lane to upload the build of your apps to TestFlight.

checkout
cache restore flutter-packages-$SEMAPHORE_GIT_BRANCH-$(checksum pubspec.yaml),flutter-packages-$(checksum pubspec.yaml),flutter-packages
flutter pub get
cache store flutter-packages-$SEMAPHORE_GIT_BRANCH-$(checksum pubspec.yaml),flutter-packages-$(checksum pubspec.yaml),flutter-packages /root/.pub-cache
checkout
cache restore flutter-packages-$SEMAPHORE_GIT_BRANCH-$(checksum pubspec.yaml),flutter-packages-$(checksum pubspec.yaml),flutter-packages
flutter build ios --no-codesign
cd ios
bundle install
cache store
bundle exec fastlane deploy
git fetch --all
git merge origin/set-up-semaphore
git push origin master

Conclusion

Congratulations! You have successfully deployed your app to TestFlight. This should allow you to iterate on the features of your app faster without worrying too much about the manual aspect of doing releases, which is complex and time consuming–saving you lots of development time!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Semaphore

Semaphore

Supporting developers with insights and tutorials on delivering good software. · https://semaphoreci.com