Continuous Integration and Delivery for iOS

Jimmy Harijanto
Blibli.com Tech Blog
5 min readDec 5, 2021

--

Continuous feedback is one of the most important things to have not only from our customers but also from our own team. And, adding Continuous Integration/Continuous Delivery to the project makes everyone better. :)

Credits to Google

Continuous Integration enables better transparency and farsightedness in the process of software development and delivery. It not only benefits the developers but all the segments of that company.

Tools required

You will need at least Bitbucket, Jenkins, JFrog, and Fastlane installed on your laptop or the integration machine. We won’t dig down to the installation process on this part.

Design

Let’s take a look at below simple flow for the integration/delivery.

Integration/Delivery Flow

Jenkins can be triggered automatically by stash notifier, and manually by user. Notice there are 3 diamonds indicates decisions. We gonna discuss about these decisions on the next part.

So, once the Jenkins has been triggered, it will checkout the code from the Bitbucket. If the increment build number option is checked, it will increment the build number using the Fastlane command, and push the changes right away back to the Bitbucket. Then it will start the build process.

After the build process, it will either archive the project and upload the IPA to the TestFlight, or upload the artifact to the artifactory. Both are optional, then the last step is notifying the Teams, or whatever team collaboration tools.

As you can see from above diagram, there is no testing process covered. And yes, we’ll cover the testing process on the coming up article.

Fastfile

Let’s prepare the Fastfile first before we dig the Jenkinsfile. You will need to create a new lane, named incrementVersion.

Put clean_build_artifacts to deletes files created as result of running gym, cert, sigh or download_dsyms. Then you will need to increment the build number by using increment_build_number(check this reference for more info).

If the process done, push the changes to the Bitbucket. You will have to ensure that there are no uncommitted git changes by using ensure_git_status_clean to make sure it raises an exception if there are any uncommitted git changes, otherwise it will push all the unwanted changes to the repository.

To make sure your team are aware that the build number has been incremented, post the notification to the collaboration tools, in above example we use Teams, and we need to get the complete version as the message to post.

Click here for full source.

Jenkinsfile

Great! Now it’s Jenkinsfile turn. We will use scripted pipeline for the Jenkinsfile. You can use declarative pipeline if you want for your project.

We will have 3 options for the user to choose if they would like to trigger the job manually. These parameters will be ignored on the auto build triggered by the Stash Notifier.

We will need INCREMENT_BUILD_NO if the user need to increment the build number. This has something to do with UPLOAD_IPA to the TestFlight as the TestFlight required us to upload IPA with unique build number. We can ignore the increment build number option if we would like not to upload the IPA to the TestFlight. We can upload the IPA to somewhere else like Diawi and do not really need to increment the build number.

The last option is UPLOAD_TO_ARTIFACTORY. If the user check this option, it will upload the artifact to the designated artifactory.

The first step we need to do is checkout the code. You just need to state it as checkout scm as it will checkout the code from the repository you will define later in the Jenkins UI. Make sure to empty and build folder and recreate it.

Now, we will build the code. We read the INCREMENT_BUILD_NO option inside this step. If it’s checked, then we have to increment the build first before we can proceed to build the project. As we name the previous lane incrementVersion, we can call it using fastlane incrementVersion.

Before we can proceed, you will need to have your ExportOptions.plist file with you. To get this file, you can try to archive the project manually using Xcode, and export it. You will get the .ipa file along with the ExportOptions.plist file. As this step is to upload the IPA to the artifactory, ensure that the chosen archive option parameters reflect the upload steps.

Ensure the value is something like:

<key>destination</key>
<string>upload</string>
<key>method</key>
<string>app-store</string>
<key>signingStyle</key>
<string>automatic</string>

For this step, we will also need the ExportOptions.plist but you will have to change the value to:

<key>destination</key>
<string>export</string>
<key>method</key>
<string>ad-hoc</string>
<key>signingStyle</key>
<string>automatic</string>

We suggest you to have two separated ExportOptions.plist files for easiness.

You can upload whatever files produced by the archiving process, but in this example we will only upload the IPA file. Make sure you setup the artifactory instance details on the Jenkins setting page.

Click here for full source.

Jenkins

Add Multibranch Pipeline by clicking the New Item, then click the Add Source button and choose the type of repository you want to use and fill the details. It will automatically detect each branches that includes the Jenkinsfile and show it on the board.

The steps defined in the Jenkinsfile will be reflected on the branch page (see image below).

And the options can be accessed through Build with Parameters page for the manual triggered build. So anytime when your peers or managers asking you to upload the build to the TestFlight, you can save time instead of building it manually in your Xcode, you can just open the Jenkins, choose the branch you would like to build, and trigger the build by this page and just waiting for the build process to finish.

Anddddd, for the automatic triggered build by stash notifier, the build status will shown along with the branch / pull requests line on the Bitbucket. Isn’t it refreshing to see that everything is green? ❤

Conclusion

I’ve shown you on how to implement the continuous integration/continuous delivery for iOS. It might be not a perfect solution yet, but still, if you have anything better, please do share your ideas on comment below.

Beste! :)

--

--