They say naming things is the hardest part of programming, but I’d say versioning is the most painful. Versioning your mobile app takes marketing insight and a grasp of app store constraints. For rapid, fast-paced development teams, it can be a nightmare trying to upload builds successfully. Having an app store reject your upload because of a pesky version number is a time-consuming mistake, costing you a recompile of your entire app. It hurts. But, I found it can be less painful with automation!
Here, I’ll show you three techniques I employ on my current project to appropriately set the version and build numbers with fastlane.
Understanding the Project Files
First, it’s important to understand how the version and build numbers get set. The iOS and Android compiler encode these values into the app binaries, reading the version and build numbers from certain project files. Both those values must be set in the project files before compiling the app since they can’t be set afterwards. Below are the project files used for versioning on each platform.
File | Version field | Build number field | |
iOS | Info.plist | CFBundleShortVersionString | CFBundleVersion |
Android | build.gradle | versionName | versionCode |
Now that we know how versions are set, it’s time to leverage some actions in fastlane to automate this process.
Incrementing Version
The version is the user-facing value that distinguishes different releases of your app in the app stores. Typically, this value is a series of integers joined by periods. Incrementing these values indicates to your users that bugs were fixed or features were added since your last app release.
Version Examples
1.0.0 # new release 1.0.1 # bug fix 1.1.0 # new feature added
I follow semantic versioning guidelines to a varying degree on my apps. And find it best to manually set the version of my app prior to submitting a new release. With my React Native codebase, I like to keep the iOS and Android apps on the same version because I usually release updates for them at the same time.
When uploading new builds of my app, I have fastlane setting the version for both platforms by reading a single value saved in the VERSION
txt file in the root of my codebase. Here’re the fastlane actions I use.
# Fastfile
# iOS
increment_version_number(
xcodeproj: project,
version_number: File.read("../VERSION")
)
# android
increment_version_name(
gradle_file_path: gradle_file_path,
version_name: File.read("../VERSION")
)
The VERSION
file gets checked into my Git repo, and this is the only manual step in my versioning process.
Auto Incrementing Build Number
Build numbers are an integer that identify different builds for each version of your app. Your users don’t see this number; it’s only for internal use. The app stores require the version and build number pair to be unique together and increase in value from previous builds.
Since there is no marketing significance in the build number, I like to increment it automatically on each app build and upload. This enables my dev team to rapidly develop without having to think about the build number. Also, it keeps the Git history clean because the new build numbers do not need to be checked into the repo.
I use fastlane actions to fetch the latest build number from the app stores and +1 the returned value for the new builds. Below are examples of the fastlane actions I use.
# Fastfile
# iOS
previous_build_number = latest_testflight_build_number(
app_identifier: app_id,
api_key: api_key,
)
current_build_number = previous_build_number + 1
increment_build_number(
xcodeproj: project,
build_number: current_build_number
)
# android
previous_build_number = google_play_track_version_codes(
package_name: app_id,
track: "internal",
json_key: json_key_file_path,
)[0]
current_build_number = previous_build_number + 1
increment_version_code(
gradle_file_path: gradle_file_path,
version_code: current_build_number
)
Add Git Tags
After you upload builds to the store, it can be tricky to remember what bug fixes or features are in each build. This is definitely apparent if you are like me and forget to add release notes for internal test builds. Using Git tags is a simple way to alleviate this issue.
I find it helpful to tag my latest Git commits with the platform and build number each time I build my apps. By doing so, I can view all the builds in my Git history and infer what code changes were included in each of them.
$ git log commit GIT_HASH (tag: builds/ios/130, tag: builds/android/131) Author: Nathan Papes Date: Fri Jan 7 13:15:19 2022 -0500 ...
In fastlane, you can refer to the action below as an example for tagging your app builds.
# Fastfile
add_git_tag(
grouping: "builds",
includes_lane: false,
prefix: "ios | android",
build_number: current_build_number,
force: true,
)
Mobile app versioning may not be too bad after all, with the help of a little fastlane automation. With the techniques above, I experience less pain from version-related issues. It isn’t a perfect process, but it’s a step in the right direction.