How I release Twine on Android & iOS using Tramline

How I release Twine on Android & iOS using Tramline
Photo by Alexandr Bormotin / Unsplash

A year ago as I was preparing to launch my brand new app called Twine - RSS Reader on the App Store and Google Play, I started going through the usual process of generating release artifacts, submitting the app to their respective stores, waiting for reviews and then finally handling rollouts or in some scenarios hotfixes in early stages. As a mobile developer, I am pretty used to this process, I have done this hundreds if not thousands of times, and I still hate this.

There are a few reasons why I'm not too fond of this process

  • It's a manual process, generating artifacts for both platforms, tracking versioning, creating releases in respective stores and then handling phased rollouts individually.
  • It doesn't scale well if I need to maintain different app variants, such as nightly builds and production releases.
  • Trying to do hotfixes to these releases is a cumbersome process.

But the good thing is, I only had to do that for the first release. After that, I set up Tramline to release my app. Tramline is a service that lets you release your app without drowning in the process.

I had already used it for another app of mine called Pinnit. I loved it, so this was the first thing I set up when preparing to release the app.

In this article, I won't cover the setup process, as it's already well-explained in their documentation. Instead, I will now show what the release process for Twine looks like.

Release Process

My usual release process goes like this for Twine, I commit/merge all my changes to my main branch, and every night Tramline triggers a nightly release and deploys it to TestFlight and Google Play Internal Testing. Once I am happy with the changes, I click a couple of buttons and it triggers my GH Actions to build the artifacts with proper versioning and then deploy them to respective stores and I can manage hotfixes and phased rollouts right from the Tramline dashboard.

Now let's look at the 2 release trains (Release trains are the building blocks of the Tramline) Twine has.

  • Nightly
  • Release

Nightly Train 🌃🚃

The nightly release train is an automated train, that takes changes from my main branch every night and back merges them into a separate branch called nightly

During this process, Tramline handles building the artifacts using GitHub Actions, tracks app versioning using semantic versioning (semver) and generates the automated release notes for the nightly release.

In the above screenshot, two releases are skipped for the nightly release, that's because Tramline is smart enough to check if there are any changes in the trunk since the last release to prepare a new release.

Most of the time I don't do anything with this train, I just let it run automatically. But if there are scenarios where I want to test a new change immediately from the store, I do a manual release using the "prepare new release" option.

Next, let's look at the main release train.

Production Train ✨🚃

With the production release train, I don't have any automated releases configured. Instead, I do a manual release.

It goes like this, I commit/merge all changes to the main branch. Then, I click on the "prepare new release" option on Tramline.

That lets me select what kind of release I want to make, which determines the app versioning on both Android & iOS.

Once I click on start, Tramline will cut a new branch from the main branch with r/release/yyyy-mm-dd format and start the release process. Similar to the Nightly train, it starts the GitHub Action to build the artifacts and uploads them to TestFlight and Internal Testing as the first step, and then we start the production release.

First I edit the release notes, since it's a multiplatform app built using Compose and Kotlin. The changelog is mostly similar, for both Android and iOS.

(I wish there was a unified text entry for this with an option to add a platform-specific changelog. Makes it a bit easier if it's a common codebase)

After that, I click start deployment, which starts the production release. Since these stores require reviews, Tramline shows an option to submit it for review and track the status of the review before making the release. (It's only available for iOS at the moment since Google Play API doesn't have this functionality). If you add Slack integration, Tramline can send notifications about these changes along with other useful release information.

Once the review process of the app is done, I start the release and a phased rollout starts. This is something I have configured when creating the release train step. App Store doesn't allow custom phase rollout strategies, so I am matching my Google Play rollout percentages with default App Store ones. That's why you use the "Increase Rollout" option in only one of them.

If everything is good with the release, it goes to 100% automatically. But if I notice any issues/crashes with the release that are critical. Which is easy to observe with the BugSnag integration in the Tramline dashboard. I just push my fixes to my release branch. Tramline then provides me the option to apply those fixes to individual releases.

If I apply these patches, the release now goes into patch mode. It starts to increase the patch version of the release and does the whole above process again.

Once the release is done, it's now locked and shows the reldex score and appropriate release information.

It also displays the release health information from BugSnag like adoption rate and stability right in the Tramline dashboard.

That's it, now new release of Twine is released to the App Store and Google Play. For me, Tramline gets rid of the manual release process of generating artifacts locally and handling the releases in individual stores, and also I don't have to directly interact with the App Store Connect or Google Play APIs.

I don't have to drown in the process and just start deploying, that's what I like about Tramline.

If you like this, you can try it by signing up at

You can check out the GitHub Actions I have used for generating my release artifacts, here. Feel free to star the project on GitHub and leave a review on the App Store or Play Store.

GitHub - msasikanth/twine: Twine: A multiplatform RSS reader built using Kotlin and Compose
Twine: A multiplatform RSS reader built using Kotlin and Compose - msasikanth/twine

Thanks for reading, until next time ✌🏾