Besure you was installed Fastlane before. If you not, please follow guide from Fastlane Doc: https://docs.fastlane.tools/

In ReactNative, you need init Fastlane for each android folder and ios folder. This Post write about ReactNative, with native project may have some difference.

1# — Android Fastlane — Config Android

1.1# — Init Fastlane

With Android, just go to /android folder and init Fastlane:

cd android
fastlane init

Some information you need to input in this step:

  • Package name: you can find it in app/build.gradle
  • Json file: it’s an access way for fastlane connect Google Play and get your app metadata. You can follow Fastlane guide to create this file https://docs.fastlane.tools/actions/supply/ (This is very important step to decide you can automate upload Google Play or not, so just follow guide of Fastlane carefully)

After Fastlane init finish, you can see folder fastlane in your folder android . In fastlane folder will have 2 file Appfile and Fastfile and a folder metadata

Appfile — is contains your link to JSON file, and package name

Fastfile — You need to take care about this file, you will define your lane in this file for run build App and upload to Google Play Store.

metadata — contains title, description, images about your App, get from Google Play

1.2# — Add plugin in Fastlane of Android folder

With Android, you need to add increment_version_code to automate increase version number in next step

cd android
fastlane add_plugin increment_version_code

Now in folder fastlane will see like this

fastlane init — Android

Pluginfile — contains name of plugins you add before.

2# — Android Fastlane — Code for Fastfile

  • With Fastlane, I just need upload source to Internal Testing, if you need upload to other, you can use this code below
upload_to_play_store(
track: 'internal',
# internal: usefor Internal Testing
# alpha: usefor Alpha Testing
# beta: usefor Beta Testing
# production: usefor Production
)

So in my case, I will do some task:

  • Get current version code from App on Google Play Store
  • Increment version code for App on Local
  • Clean
  • Build
  • Upload to Play Store

This is my code:

lane :upload_internal do
previous_build_number = google_play_track_version_codes(
package_name: "com.myapprn",
track: "internal",
)[0]

current_build_number = previous_build_number + 1

increment_version_code(version_code: current_build_number) # Increment the build number of the application

gradle(task: "clean") # run the task clean
gradle(
task: "bundle",
build_type: 'release') # compile a release bundle application app.aab

upload_to_play_store(
track: 'internal',
skip_upload_metadata: true,
skip_upload_images: true,
skip_upload_screenshots: true,
)
end

if your Appfile correct, You can run build App by yourself and check on Google Play Store

# in android folder
fastlane upload_internal
# upload_internal is name of lane in Fastlane android
fastlane upload_internal

3# — Fastlane — Config iOs

With iOs, like Android, you can go to ios folder and init Fastlane:

cd iOs
init Fastlane

Fastlane will show 4 choices:

  • 1 — Automate screenshots
  • 2 — Automate beta distribution to TestFlight
  • 3 — Automate App Store distribution
  • 4 — Manual setup — manually setup your project to automate your task

With my task is “Automate upload to TestFlight”, I chose number 2.

Just follow step in number 2, you will finished setup. Fastlane will require you input account development Appstore, this is very important for upload app after.

After init finished, in folder ios you can see 2 file like android :

Appfile — now contains app_identifier , apple_id , itc_team_id , team_id

Fastfile — define lane for build App

4# — Fastlane — Sign & Certificate iOs

With iOs, you have Fastlane action call “Match” to Code Signing in Apple Store, Why “Match”?

When deploying an app to the App Store, beta testing service or even installing it on a device, most development teams have separate code signing identities for every member. This results in dozens of profiles including a lot of duplicates.

You have to manually renew and download the latest set of provisioning profiles every time you add a new device or a certificate expires. Additionally this requires spending a lot of time when setting up a new machine that will build your app.

With “Match”, this can help you save Code Signing at a Place like git, and everytime build App, it will sign from this place.

In ios folder, start using match

fastlane init match

You’ll be asked if you want to store your code signing identities inside a Git repo, Google Cloud or Amazon S3. I chose Git repo, and I create a Gitlab Repo for Save Code Signing (be sure this repo is private).

After init finished, let generate new certificates and profiles:

fastlane match appstore

When finished, your Git Repo will be like this:

fastlane match appstore

5# — iOs Fastlane — Code for Fastfile

  • Now with Fastlane, my task will build App and upload to TestFlight. So I will using this code in Fastlane:
upload_to_testflight

My Fastlane iOs will have list Task:

  • Load App Store Connect API token to use for Fastlane tools and actions below
app_store_connect_api_key
  • Run pod install because my App using cocoapods
cocoapods
  • Use match to signing (if you don’t init match you can’t run this step)
match(
app_identifier: ["com.myapprn"], # [target 1, target 2] if you have multiple target need to Sign
type: 'appstore', # because init match appstore so this line must be appstore
readonly: true, # only fetch existing certificates and profiles from GIT, don't generate new ones
git_basic_authorization: '<project access token from Git Repo contains Cer and Profiles', # example aWlvb2xsOmdscAJ0LOVEYOUFOREVERALLMYLIFEyc0FT
)

git_basic_authorization : you can get this by go to Gitlab >> choose Git Repo contains Cert >> Settings >> Access Tokens . Be sure check to api

  • Configures Xcode’s Codesigning options of all targets in the project
update_code_signing_settings(
use_automatic_signing: true,
)
  • Increase build number of App
increment_build_number(
xcodeproj: "MyAppRN.xcodeproj",
build_number: latest_testflight_build_number + 1
)

latest_testflight_build_number : Fetches most recent build number from TestFlight

  • Build App
build_app(
export_method: 'app-store',
scheme: "MyAppRN",
workspace: "MyAppRN.xcworkspace",
configuration: "Release"
)
  • Upload to TestFlight
upload_to_testflight(skip_waiting_for_build_processing: true)

This is all code:

lane :beta do
app_store_connect_api_key
cocoapods
match(
app_identifier: ["com.myapprn"], # [target 1, target 2] if you have multiple target need to Sign
type: 'appstore', # because init match appstore so this line must be appstore
readonly: true, # only fetch existing certificates and profiles from GIT, don't generate new ones
git_basic_authorization: '<project access token from Git Repo contains Cer and Profiles', # example aWlvb2xsOmdscAJ0LOVEYOUFOREVERALLMYLIFEyc0FT
)
update_code_signing_settings(
use_automatic_signing: true,
)
increment_build_number(
xcodeproj: "MyAppRN.xcodeproj",
build_number: latest_testflight_build_number + 1
)
build_app(
export_method: 'app-store',
scheme: "MyAppRN",
workspace: "MyAppRN.xcworkspace",
configuration: "Release"
)

upload_to_testflight(skip_waiting_for_build_processing: true)

end

Now you can use Fastlane to build app and upload to TestFlight

# in ios folder
fastlane ios beta
# beta is name of lane in Fastlane ios
fastlane ios beta

6# — CI/CD — Gitlab

If you just want to use Fastlane for reduce time Build App at local. You don’t need to keep reading. But if you want to use CI/CD on Git for automate Build and Upload App. Keep Reading.

Before using Gitlab CI/CD, you need gitlab-runner, If you don’t have a runner:

  1. Install GitLab Runner on your local machine.
  2. Register the runner for your project. Choose the shell executor.

When install correct, you can see in Gitlab Project >> Settings >> Expand Runners like this:

Gitlab Runners Assigned

After that, I created 2 branch for run automate:

  • staging-ios for run Fastlane iOs App
  • staging-android for run Fastlane Android App
Git branch

6.1# — Gitlab Integrations

To use Gitlab for automate Build and Upload App, Gitlab have two things for you, just go to your Gitlab Project (source of this App, not Git Repo Cert) >> Settings >> Integrations:

Gitlab Integrations

6.1.1# — Apple App Store Connect

Upload your AuthKey file p8 and “Save”

AuthKey_XXXXXXXXX.p8

This will help Fastlane iOs connect to App Store

6.1.2# — Google Play

Upload your json file (you was created it from “Init Fastlane android” before) and “Save”

Json File Google Play

This will help Fasstlane Android connect to Google Play

6.1.3# — Another Variables

To automate build and upload, and you need secure some properties from another developer in your team, so go to Gitlab project >> Settings >> CI/CD >> Expand Variables, and add some Variables like this:

PLAY_STORE_UPLOAD_KEYSTORE_FILE_BASE64

  • Android need a keystore file for build App, so I was converted this file to base64_string. In your local, go to folder contains keystore file and run:
cd keystore_folder
base64 -w 0 myfile.keystore -o outputFile.txt

Open outputFile.txt and I add this base64_string to Gitlab Variables

PLAY_STORE_UPLOAD_KEYSTORE_PASSWORD

  • Password for keystore file

PLAY_STORE_UPLOAD_KEYSTORE_ALIAS

  • Keystore alias

PLAY_STORE_UPLOAD_KEY_PASSWORD

  • Key password

MATCH_PASSWORD

  • This is “Fastlane Match Passphare”, when you run fastlane init match Fastlane will require you input this, now you need add this for CI/CD can read Signing.

IOS_MATCH_GIT_BASIC_AUTHORIZATION

  • This is “project access token” of Git Repo contains Cert and Profile, you have it at step fastlane init match

All Variables like this, be sure you choose Attributes is protected and Expanded :

Variables Upload App CI/CD

6.1.4# — gitlab-ci.yml

Now in you root folder on local, you need create .gitlab-ci.yml and config:

image: node:16.20.1

stages:
- deployiOS
- deployAndroid

variables:
LC_ALL: "en_US.UTF-8"
LANG: "en_US.UTF-8"

ios:upload_testflight: # use for upload TestFight
dependencies: []
stage: deployiOS
only:
- staging-ios # run only branch staging-ios
before_script:
- yarn install
script:
- cd ios
- gem install bundler
- bundle install
- export IOS_MATCH_GIT_BASIC_AUTHORIZATION=${IOS_MATCH_GIT_BASIC_AUTHORIZATION}
- export MATCH_PASSWORD=${MATCH_PASSWORD}
- bundle exec fastlane ios beta # launch the lane beta
tags:
- ios # tag to launch your specific gitlab runner
when: manual # use manual to review on gitlab before run

android:upload_internal: # use for upload Google Play
dependencies: []
stage: deployAndroid
only:
- staging-android # run only branch staging-android
before_script:
- yarn install
- echo -n ${PLAY_STORE_UPLOAD_KEYSTORE_FILE_BASE64} | base64 -d > ./android/app/myfile.keystore
- echo "keystorePath=myfile.keystore" > ./android/signing.properties
- echo "keystorePassword=${PLAY_STORE_UPLOAD_KEYSTORE_PASSWORD}" >> ./android/signing.properties
- echo "keyAlias=${PLAY_STORE_UPLOAD_KEYSTORE_ALIAS}" >> ./android/signing.properties
- echo "keyPassword=${PLAY_STORE_UPLOAD_KEY_PASSWORD}" >> ./android/signing.properties
- echo "sdk.dir=/<dir_to_android_sdk>/Android/sdk" >> ./android/local.properties
script:
- node -v
- cd android
- gem install bundler
- bundle install
- bundle exec fastlane android upload_internal # launch the lane upload_internal
artifacts:
paths:
- ./android/app/upload.keystore
- ./android/signing.properties
expire_in: 10 mins
tags:
- android
when: manual # use manual to review on gitlab before run

6.1.3# — Gitlab Jobs

Now when you push source to gitlab, and create “merge request” to staging-ios and staging-android , you will see 2 job is manual in Gitlab Project >> Build >> Jobs

Gitlab CI/CD Job

Click “Play” and Gitlab will automate Fastlane for you.

  • ios
fastlane ios beta GITLAB CI/CD
  • android
fastlane upload_internal

7# — Conclusion

When using Fastlane and Gitlab CI, I was reduced many time for build and upload app, really cool to use it.

More Fastlane: https://docs.fastlane.tools/

More Gitlab CI: https://docs.gitlab.com/ee/topics/build_your_application.html

--

--