Have a look at our new "Complete Guide to Optimizing Slow Tests"! Discover →

10 Mar 2022 · Greatest Hits

Deploy Flutter (iOS) Apps to Testers using Firebase App Distribution with Semaphore

firebase ios

Deploying iOS apps to early testers helps you get feedback for improvements or detect issues that came out from early on during the development. Firebase App Distribution, or App Distribution, is a service that helps you deploy your Flutter apps to testers.

This article focuses on automating deployments for Flutter (iOS) apps using ad-hoc releases in App Distribution. If you’re interested in implementing this for Android, you can read this.

Prerequisites

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

Firebase App Distribution

Deploying iOS apps is more complex than in Android. Unlike in Android, most of the time, you can create an android package (APK), and users can directly install and use them on their devices. But for iOS, it’s a different story. You can only install apps from the Apple Apps Store or signed ad-hoc releases from tools like App Distribution.

App Distribution and Apple Apps Store

App Distribution is an alternative service to deploy iOS apps to a limited number of testers to get early feedback or detect issues, and it’s not a complete replacement for the Apple Apps Store if you are planning to release your apps to the public.

Creating a Firebase Project

In the console, create a new project by tapping Add project.

Assign a project name and unique ID (optional).

Semaphore Firebase

You can also enable Google Analytics (Optional). For now, we’ll disable this setting.

Google analytics firebase

Authentication with Firebase CLI

Firebase CLI contains tools that allow you to manage, view, and deploy to Firebase. We will use Firebase CLI to authenticate CI later when deploying builds to Firebase projects.

Setting up Firebase CLI

There are at least two ways to set up Firebase CLI on your machine. Either by downloading the standalone binary or via the npm package manager.

If you already have npm, you can go ahead and install Firebase CLI using the package manager, as shown below:

npm install -g firebase-tools

Testing and logging in with Firebase CLI

Authenticate Firebase CLI using the email associated with your Firebase account:

firebase login

✔  Success! Logged in as joshua*****@gmail.com
Firebase CLI

For more information, see Firebase CLI reference.

Setting up App Distribution

App Distribution in Firebase is where deployed builds show up and where you can manage your app testers.

Setting up an iOS project

If you have an existing Flutter project that uses Firebase, please skip this step. Otherwise, do the following if you are using the starter app or have created a new project using flutter create.

In the Firebase console’s sidebar, navigate to and open App Distribution.

App distribution

Tap the iOS logo to create a new iOS project.

ios firebase choose a product

Then, enter the details of your iOS project. Optionally, you can download the GoogleService-Info.plist file and put them to your root iOS project directory.

add firebase to your apple app

If you use the starter app, replace com.example.semaphoreciFlutterDemo with your desired bundle identifier.

Setting up group testers

App Distribution allows you to configure multiple test groups depending on the build you deploy, for example, for internal or private beta builds.

Navigate to the App Distribution page and select the Testers & Groups tab.

ios firebase testers & groups

Add a group named “testers”, then click Save.

Last, to deploy your build to the users, add their emails to the group.

ios firebase groups

fastlane

fastlane is an open-source platform with a wide range of tools and APIs for simplifying deployment for Android and iOS projects. We will use fastlane to automate our workflow, communicate with Firebase, and ultimately deploy the project.

Setting up Ruby

Ruby is a must-have to use fastlane in your project. Here’s an exhaustive list of ways to install Ruby on your machine.

Setting up Bundler

Bundler and Gemfile are for defining dependencies in fastlane. Run the following command to install bundler:

gem install bundler

Setting up fastlane

If you’re on macOS and have Homebrew installed, run the following:

brew install fastlane

If you have gems installed on macOS/Windows/Linux, run the following:

sudo gem install fastlane

Setting up fastlane with your Flutter iOS project

Set up fastlane in the ios directory of your Flutter project.

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

Then, wait for all the packages and dependencies to be installed properly.

ios firebase fastlane

You should now have a fastlane directory containing the Appfile and Fastfile.

Adding the Firebase App Distribution Plugin to Fastlane

We will need the plugin to upload the build artifacts to Firebase.

Install the fastlane plugin with the following command:

fastlane add_plugin firebase_app_distribution

Then, when asked to modify the Gemfile for the current path (ios), type y.

Depending on your Ruby setup, you might be asked to enter your password and provide temporary root privileges when installing the dependency:

sudo fastlane add_plugin firebase_app_distribution

Setting up Fastfile with Firebase

Fastfile

Fastfile stores the configuration for automation using fastlane.

First, navigate to your Fastfile directory and replace the default content with the following:

default_platform(:ios)

# Credentials (for later)
git_authorization = "<GIT_AUTHORIZATION>"
firebase_app_id = "<YOUR_FIREBASE_APP_ID>"
firebase_cli_token = "<YOUR_CLI_TOKEN>"

# Team
team_id = "<YOUR_TEAM_ID>"

platform :ios do
 desc "Deploy iOS to Firebase"
 lane :deploy do
 # add actions here: https://docs.fastlane.tools/actions
 end
end

deploy here is the name of the fastlane workflow. To run the command in your terminal, run fastlane deploy.

Next, below team_id variable, add the following:

# Keychains
keychain_user = "temp"
keychain_pass = "temp"

def 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

These are for creating temporary keychains and downloading developer certificates, and provisioning profiles on the CI.

Then, inside the deploy lane, add the following:

platform :ios do
  lane :deploy do
    ensure_temp_keychain(keychain_user, keychain_pass)

    gym(
      scheme: "Runner",
      archive_path: "./build/Runner.xcarchive",
      export_method: "ad-hoc",
      output_directory: "./build/Runner",
    )

    firebase_app_distribution(
      app: firebase_app_id,
      firebase_cli_token: firebase_cli_token,
      release_notes: "Bug fixes and performance improvements",
      groups: "internal",
    )

    delete_temp_keychain(keychain_user)
  end
end

App ID

To get your App ID, click Project Overview, then Project Settings.

Next, select the current app’s Project Settings and copy your App ID.

firebase ios setup

Replace firebase_app_id in your Fastfile.

Generating Firebase CLI token

Earlier you authenticated Firebase CLI with your Firebase account; mainly for testing tooling installation. Now, we will authenticate and generate a token for CI.

To generate a token for CI run:

firebase login:ci

This should give a hashed string like this:

✔  Success! Use this token to login on a CI server:

1//0etIq08H_oIB5CgYIARAAGA4SNwF-L9IrgmGb2iadCGcAmqhzK_NH8F8zQBkbPrMS2qtWhMqUmpbVbYK4YzC_83-eHb8NgylEMUg

Replace firebase_cli_token in your Fastfile.

Setting up fastlane match

fastlane match, or match, simplifies the complex code signing for your iOS apps, and Semaphore has an in-depth documentation on how to deal with iOS code signing using fastlane match. I will explain the setup here succinctly.

Configuring match

Initialize match by running

fastlane match init

Next, select git to store the developer certificates and provisioning profiles:

fastlane match supports multiple storage modes, please select the one you want to use:
1. git
2. google_cloud
3. s3
>>> 1

Then, enter the URL of your git repository, for example: [https://github.com/joshuadeguzman/ios-certificates](https://github.com/joshuadeguzman/ios-certificates)

Finally, replace development to adhoc in your Matchfile

type("adhoc")

Generating certificates and profiles

Create ad-hoc certificates and profiles by running. When prompted, enter your Apple Developer Credentials.

fastlane match adhoc -a <YOUR_BUNDLE_IDENTIFIER>

...

All required keys, certificates and provisioning profiles are installed 🙌

Use the match provisioning profile in your project settings using Xcode:

firebase ios Xcode

Downloading certificates and profiles in fastlane

In most cases, you should be able to clone your match’s git private repository on your local machine. But in CI, we have to provide appropriate authorization to allow access.

Create your Github personal access token by following this.

Use the personal access token generate and replace the git_authorizationin yourFastfile:

git_authorization = "joshuadeguzman:<YOUR_ACCESS_TOKEN>"

Last, add the match command

platform :ios do
  lane :deploy do
    ensure_temp_keychain(keychain_user, keychain_pass)

    # Add this
    match(
      type: "adhoc",
      git_basic_authorization: git_authorization,
    )

    ... (truncated)
  end
end

Deploying to Firebase using fastlane locally

That’s it for Firebase and fastlane for now. Let’s test the Firebase deployment by running fastlane locally.

Ensure the project is in clean state:

flutter packages & clean

Then, build the Flutter iOS app:

flutter build ios —-no-codesign

To deploy your app, run the following:

cd ios && fastlane deploy && cd -

This should give you a success message for deploying your app to Firebase.

firebase ios deploy

Great! Your latest iOS build should be displayed on App Distribution’s releases dashboard.

Deploying to Firebase using Semaphore

Before proceeding, I recommend reading core concepts that make Semaphore intuitive and powerful. I also explained the Visual Builder for iOS continuous integration in this article.

Setting up a Semaphore project for your app

Log in with your Semaphore account, then create a new project.

firebase ios create

Choose the repository for your project, then click Continue to workflow set up.

Then for the workflow set up, click Customize.

firebase ios customize

Setting up blocks to integrate and build your apps

Configuring the workflow pipeline

We’ll be using a Mac Based Virtual Machine environment with a macos-xcode13 image.

firebase ios semaphore

Setting up your blocks for Continuous Integration (CI)

Create a block named Install Dependencies, then add a job Install and cache Flutter:

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

alt_text)

Then, add a new block named Lint, then add jobs Format and Analyze:

Format

flutter format --set-exit-if-changed .

Analyze

flutter analyze . 

Prologue

checkout
cache restore flutter-packages-$SEMAPHORE_GIT_BRANCH-$(checksum pubspec.yaml),flutter-packages-$(checksum pubspec.yaml),flutter-packages
flutter pub get
firebase ios semaphore flutter

Finally, add a block to run your Flutter tests.

Run unit and widget tests

flutter test test

Prologue

checkout
cache restore flutter-packages-$SEMAPHORE_GIT_BRANCH-$(checksum pubspec.yaml),flutter-packages-$(checksum pubspec.yaml),flutter-packages
flutter pub get
firebase ios semaphore

Optionally, you can test your workflow by clicking Run the workflow.

Configuring promotions to deploy your apps

Setting up a new promotion

Promotions help us create conditions around our workflows and whether or not we have to run another workflow or set of workflows. Also, they can be triggered manually or automatically.

After configuring the visual builder, we can merge the set-up-semaphore branch to the default branch for continuous deployment to Firebase using automatic promotions. In our case, the Deploy to Firebase promotion gets triggered once the condition "branch = 'master' AND result = 'passed' is satisfied by the workflow run.
alt_text

Configuring the deployment pipeline

Similar to the Main pipeline, we will use a Mac Based Virtual Machine environment with a macos-xcode13 image.

firebase ios semaphore

Next, in this pipeline, you will also need a Install Dependencies block set up with a job Install and cache Flutter:

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
firebase ios semaphore

Last, create a block named Deploy to Firebase with a job Run Fastlane:

checkout
cache restore flutter-packages-$SEMAPHORE_GIT_BRANCH-$(checksum pubspec.yaml),flutter-packages-$(checksum pubspec.yaml),flutter-packages
sh bump_version.sh
flutter build ios --no-codesign
cd ios
bundle install
cache store
bundle exec fastlane deploy

This block is responsible for executing your deploy lane in fastlane.

firebase ios semaphore

Click Run the workflow and commit the changes.

Awesome, your workflow should be ready for automatic deployments.

Testing continuous deployment

To check automatic promotions and deployments, we should merge it to the working branch to master:

git fetch --all
git merge origin/set-up-semaphore
git push origin master

Now, push the changes to trigger the build.

firebase ios semaphore

Conclusion

Setting up a continuous deployment pipeline for iOS takes more time than Android, but it’s worth the investment. Creating new releases every time a developer merges a pull request using this approach saves you half the time if you’re doing all the steps mentioned above manually, especially dealing code-signing for your teams.

Have a comment? Join the discussion on the forum

mm
Writen by:
I'm a product-minded engineer based in the Philippines. Google Developer Expert for Flutter and software engineer at Better Financial, a FinTech startup based in New York, US.