Mastering Fastlane: Overcoming Challenges in Passing Build Properties to Native Layers

Authors
Adrian Buciuman
Software Engineer
@
Callstack

Fastlane is a popular set of tools created to simplify monotonous tasks for developers. It is widely used across numerous application development processes because it offers a wide range of preinstalled plugins called lanes, which can be customized to automate various tasks.

This article will specifically discuss the handling of plist updates in iOS projects and the management of property passing in Android builds. Please note that this is a targeted exploration and does not cover all the interactions Fastlane has with these build systems.

The update_plist action in iOS

Fastlane's update_plist action offers a convenient way to modify the values of keys in plist files. As per the official Fastlane documentation, the syntax for the update_plist action is as follows:

update_plist(
plist_path: "path/to/your_plist_file.plist",
block: proc do |plist|
plist[:CLIENT_ID] = "new_client_id"
plist[:GOOGLE_APP_ID] = "new_google_app_id"
end
)
view raw plist.ruby hosted with ❤ by GitHub

While this syntax generally works as intended, there have been instances where it didn't yield the expected results. In our experience, we encountered situations where only some of the values in Info.plist were updated, despite the code appearing correct.

After investigation, we found that an alternative syntax could offer more reliable outcomes:

update_plist(
plist_path: "path/to/your_plist_file.plist",
block: proc do |plist|
plist['CLIENT_ID'] = "new_client_id"
plist['GOOGLE_APP_ID'] = "new_google_app_id"
end
)
view raw plist2.ruby hosted with ❤ by GitHub

The key distinction here is in how we're referencing hash keys: we're using strings (plist['CLIENT_ID']) rather than symbols (plist[:CLIENT_ID]).

This is not an isolated observation. We found that others in the Fastlane community have encountered a similar situation, as evidenced by open tickets regarding this issue. It's important to note that while the symbol syntax works in many scenarios, using strings instead could potentially improve reliability when updating multiple values.

The gradle action in Android

Fastlane's gradle action offers a way for developers to pass custom properties to Gradle builds. For example:

gradle(
properties: {
"CLIENT_ID" => "new_client_id",
"GOOGLE_APP_ID" => "new_google_app_id",
}
)
view raw gradle.ruby hosted with ❤ by GitHub

In our experience, we faced issues with Gradle not recognizing certain properties during the build process. To solve this problem, we found it was necessary to define these properties beforehand in our project's gradle.properties file. This file is situated in the root directory of your project, and it's read every time a Gradle command is invoked.

Once these properties have been passed to the Gradle build, they can be accessed within your build.gradle file. Here's how you can access these properties:

android {
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
...
resValue "string", "CLIENT_ID", "\"$CLIENT_ID\""
resValue "string", "GOOGLE_APP_ID", "\"$GOOGLE_APP_ID\""
....
}
}
view raw gradle2.groovy hosted with ❤ by GitHub

In this block, the CLIENT_ID and GOOGLE_APP_ID properties are passed as string resources to the Android application, and they can be used within the application's code like any other string resource.

Conclusions

When working with Fastlane, paying attention to certain details can lead to more efficient application development.

For iOS, being mindful of the possible issue with using symbols instead of strings in the update_plist action can save valuable debugging time. On the Android side, specifying properties in the gradle.properties file and adhering to the correct syntax can streamline the build process, reducing the risk of unexpected errors.

These nuances may seem small, but they play a vital role in the overall development experience.

Insights

Learn more about CI/CD

cover

Aug 21Article

Automating React Native Evals With Airflow

How Airflow coordinates React Native Evals, runs solver and judge models separately, and keeps every result traceable through human review.

cover

Jun 19Article

Caching React Native Builds on S3 and R2 With Rock

Learn how to cache your React Native builds and store them on Amazon S3 or R2 to reuse across your team.

cover

Oct 29Podcast

How to Handle App Center Retirement

Discover App Center alternatives for React Native. Explore migration tips and Expo's role in the transition from CodePush & other App Center functionalities.

cover

May 14Article

Ship OTA (Over-The-Air) When in an Emergency

Shipping Over-The-Air may save your app from a lot of trouble. Find out why and how you can make your application OTA-ready.

cover

May 10Article

Shipping Fast With Continuous Deployment

Continuous Deployment (CD) lets you ship new features or check for critical bugs fast. Find out more about developing apps with a CD setup.

cover

Dec 13Article

How Can Continuous Integration (CI) Improve Your React Native Apps?

Read the article and find out how Continuous Integration (CI) can improve the building, testing, and distribution of your React Native apps.

cover

Apr 24Article

Continuous Delivery - Integration for Everyone

A quick guide on how to tame Continuous Delivery and Integration. Dive into a real-world scenario from a Callstack client project.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

Writing a Swift TurboModule in React Native

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.