Release your iOS App with just One Line Slack Command

A journey from Slash Commands to Fastlane

César Vargas Casaseca
Axel Springer Tech
8 min readApr 22, 2020

--

In WELT we rely heavily on our Continuous Integration implementation. We use it not only to compile the code and run unit tests to ensure a valid app status at any time, but also deliver our product to TestFlight. Besides creating a new build automatically each time a new feature is merged, we can trigger those whenever a new release is requested. Automatizing this step we are sure that no human error spoils the process, as might happen when building and uploading manually from Xcode.

Given the expense of maintaining a local CI Server such as Jenkins, we moved to Travis CI, a hosted solution where all that functionality is included in their paid plan. We configure our tasks by adding a file named .travis.yml, which is a YAML format text file, to the root directory of the repository. This file specifies the programming language used, the desired building and testing environment (including dependencies which must be installed before the software can be built and tested), and various other parameters. (Wikipedia) Thanks to that we can select and change the Xcode version by only changing one line of that file, skipping all the hassle that it used to involve in Jenkins. In our article we will thus use Travis, but the same process can applied to any CI Platform that accepts jobs triggering via their API.

Every time a new push is performed in our GitHub repo that file is read and the configured action triggered. Following that, we arranged that whenever we push in a branch with the prefix release, a new Build is created and delivered with the content of that branch.

That is relatively easy to perform for us developers, but what about QA, Product Owners and other stakeholders without access to our repo? Wouldn’t it be awesome if they could do it every time they want a new build without having to tell us? Wouldn’t it be nice that we make that even easier for us? Travis allows the triggering of a Build via their API, but we still need to trigger that in a way that is easily accessible for all the members of our team. This is when Slack comes into play.

Since Slack is our favourite medium for communication, we chose Slash Commands to do that. Slash Commands allow you to listen for custom triggers in chat messages across all Slack channels. When a Slash Command is triggered, relevant data will be sent to an external URL in real-time.

Ok, we can call an URL, but this we are very far away to trigger a release. We need an intermediate Server Application that receives the URL call, checks that the right credentials are there, extracts the requested parameters and convert them to execute the triggering script through the Travis API. Once the job is launched, it will use Fastlane — as usual — to build and deliver.

We are almost setup, but we still have to choose the right technology for our Server App, and where to host it. Although still in an early stage, I opted for Swift Vapor to implement it. Vapor is a web framework for Swift, that provides a beautifully expressive and easy to use foundation for a website, API, or cloud project. As a Swift Developer, getting deep into Vapor was something I yearned to since its release, and this simple project was the perfect sandbox to try it. It is like a dream being able to use the same language I use for iOS apps in the server! In our Native Development Team at WELT we use Kotlin for our backend Spring Boot App and the Android App, so I was a bit “jealous”. Now it is time for my revenge!
Given its straightforward and smooth deployment, it would be hosted and run in Heroku.

In conclusion, we will:

  • Call the process from a Slash Command in Slack
  • Process the call with Swift Vapor in a Server App
  • Run the Server App in Heroku
  • Trigger the build in Travis CI
  • Build and deliver with Fastlane

The Slash Command

Once determined the toolset we are going to use to implement our task, we start from the entry point, the Slash Command. Slash Commands allow users to invoke their app by typing a string into the message composer box.

We need at first to define a meaningful name command, something of course is not already taken by other one in your team or those built into Slack. /apps-deploy will do it for our example.

In addition we want to add parameters: project, configuration, and from which branch we want to build. This all will be wrapped into one simple text POST parameter called text of the invoked URL, so our Swift Vapor Server App will be in charge of parsing it into single relevant parameters.

To illustrate this, we can invoke our release as:

/apps-deploy news release develop

where news is the name of our project, release the configuration and develop our repo branch.

In order to verify that the request came from Slack, it is signed using a secret that’s unique to your app. On each HTTP request that Slack sends, a X-Slack-Signature HTTP header is added. Our Swift App will then compute a signature based on that request, to finally make sure that the signature we computed matches the signature on the request.

Furthermore, we have to define an URL to be invoked when the command is triggered. Given that the our parameters will be POST, and that we will run our Server App in Heroku, we will add this one:

http://medium-app-deploy.herokuapp.com/triggerJob

The Swift App

As you probably have inferred, the core of our process will be handled by the Swift App. Here we:

  • Read the URL
  • Verify the Request so we can ensure that it came from our Slack channel, otherwise fail gracefully
  • Parse it to get the required parameters
  • Trigger the job via the Travis CI API

Let’s get down to business!

Read the URL

To read the called URL we need to register a route in Swift Vapor with the path described previously. In Vapor, routing is the process of finding the appropriate response to an incoming request. So, in the file routes.swift, automatically generated when initialising our Vapor project, we add it as:

Where:

  • TriggerRequestMetadata is a subclass of the Content Vapor type, to automatically decode the POST parameters we want to handle, in this case text
  • “triggerJob"is the URL path we want to react to
  • CIController().triggerJob is the closure that handles the request, here encapsulated as a function of the CIController class

As you can see, Vapor makes it very easy to read a request, automatically decoding into the desired type. Kudos to them!

The Request Handler

Therefore the substance of our App is contained in the CIController class:

We:

  • Check that the credentials are valid as illustrated before, and fail otherwise.
  • Convert the request parameters into our Job model class, that contains the info of a Travis job:

Calling the Travis API

With this we trigger the job out Network Service class that connects with the Travis API. We authenticate with our token adding it to the headers in triggerJobHTTPHeaders()

And add our configuration in triggerJobHTTPBody. For more info about the Travis API, you can take a look here.

The script is the shell command to be executed in the CI, in our case the Fastlane command to our custom lane, where we specify the desired configuration:

After we obtain the answer from Travis, we can convert and pass it to the original request, so we display a meaningful message in Slack:

Once we have our Application properly running locally, it is time to deploy it.

Heroku

Heroku is one of the most popular cloud platform as a service (PaaS) supporting several programming languages, among them, Swift. With this all in one solution, we can host and run our app, so it can be called from the Slash Command. I chose it because of their easiness, support and pricing (free for these kind of small experiments)

In order to deploy it you need to:

  • Sign up in Heroku
  • Install the Heroku Command Line Interface (CLI)
  • Log in and create and application in the Heroku Dashboard
  • Put the project into a Git Repo, and connect it to Heroku as a remote
  • Let Heroku know that we are using Vapor by setting the Buildpack. Swift is not officially supported, however we can achieve this step if we use one of the many we can find in the Internet, this one for instance.
  • Include a Procfile to let Heroku know the commands that are to be executed by the app on startup. This tipically look like web: Run — env production — hostname 0.0.0.0 — port $PORT
  • Commit your changes, and once you want to deploy, push them to the Heroku remote.

As you can see, the process is very straightforward. Nevertheless, given the mentioned fact that Swift Vapor is not officially supported in Heroku, some problems might arise. In that case, you may want to check the Vapor documentation here, or this article about how to deploy easily.

Travis & Fastlane

We are almost done, in fact the remaining step was already accomplished when we implemented our CI pipeline. However, for completion sake, I will also describe it here.

As expressed before, we send in our Travis request body the required info to trigger the build we want:

Here we are specifying the script and the branch of our repo from where it should be executed. As you can see, we are calling Fastlane to build, sign and deliver our app to TestFlight and App Store Connect. All these steps are defined by our custom lane production:

“bundle exec fastlane production”

That lane should be contained in the Fastfile in the root of our repo. In our case it looks like:

Here we are:

  • Preparing the app to be signed and built.
  • Building it with the production configuration.
  • Uploading it to the App Store.
  • Notifying Slack and Jira after the release was successful.

These might look obscure for the novice in Fastlane, in that case I recommend to get started with this amazing tutorial.

Wrapping It Up

That’s all folks! Now anyone in our team with the right permissions can trigger a new release whenever they want.

In this article, we have seen how we can create a Slash Command, connect it to a Swift Vapor Server App running it Heroku, and trigger a Build in Travis via Fastlane.

There are still many things to experiment with, given Slack and a Server App, the possibilities are basically limitless. Nowadays more and more platforms expose their interface with an API, so we can always bear that on mind to make the life easier for us and our colleagues.

I would love to read your ideas questions, or comments about this topic. Please leave them below if you have any.
Safe journey!

--

--

César Vargas Casaseca
Axel Springer Tech

Senior iOS Developer at Automattic. Allegedly a clean pragmatic one. Yes, sometimes I do backend. Yes, in Kotlin, Javascript or even Swift.