In mobile application development, solid architectural patterns play an important role in building apps faster.
Therefore, in order to assure the quality and reuse of existing code in multiple apps, companies started to build reusable software development kits — commonly known as SDKs — to develop mobile apps.
In the case of iOS, Apple has created countless SDKs for app developers to build apps but companies still need to build their own custom iOS SDKs to enable sharing of the product specific common code in their apps.
To speed up iOS app development at YOOX NET-A-PORTER GROUP (YNAP), we build common iOS libraries that can be shared between the multiple iOS apps for NET-A-PORTER, MR PORTER, YOOX, THE OUTNET and the 30+ brands for whom we run the digital stores.
One of the major iOS SDKs is a layer between our client apps and the backend APIs, which for the purpose for the article we will call “iOS-SDK”.
What is iOS-SDK
YNAP has a number of iOS apps available on the App Store and many other enterprise apps that serve our colleagues.
We wanted to mirror the experience of our customers for our colleagues while reducing the duplication of coding.
To do so, we decided to build a common iOS-SDK that can easily be shared across our customer and enterprise apps.
This also allows us to streamline the build process by defining a single set of behaviours in terms of methods and return types. The people using the SDK then only need to implement the adapters for each app.
This means that iOS-SDK is a collection of iOS libraries that easily be distributed using CocoaPods Or Carthage.
We’ve written the library purely in Swift and it has an in-built example app which allows us to test client-specific features.
Why we need Continuous Integration and Continuous Delivery for iOS-SDK
As the iOS-SDK code can affect multiple client apps, it’s essential to ensure that the code quality of the iOS-SDK meets the requirements of all the supported apps.
To do so, it’s important to regularly run a solid set of automated tests to make sure that code changes for one client do not have a negative affect on any others.
To achieve this, we needed Continuous Integration (CI) to get immediate feedback from any changes to our iOS-SDK codebase, and Continuous Delivery (CD) to update the client apps accordingly.
By adding CI/CD to iOS-SDK, we get early feedback on Swift code updates and regularly test new features in our client app. Implementing CI/CD practices for the SDKs is equally important as doing so for normal iOS apps.
We started implementing CI/CD on the iOS-SDK library with build automation, custom scripting and use of Travis CI features. Our iOS-SDK CI/CD rocket is now ready for launch.
iOS-SDK Deployment Automation
Our iOS-SDK is a Swift library and we distribute it using CocoaPods Or Carthage. The manual deployment of the iOS-SDK library was a time-consuming, repetitive (and boring!) process.
Previously, whenever we wanted to release new version of iOS-SDK, developers needed to go through the manual process.
We needed to get past this and started looking for an automated release process which could do the following:
- When source code is merged in the Master branch, automatically build new versions of the library, run all the automated tests and automatically create a tag with the version mentioned in the Podfile or Cartfile automatically make Github releases of the library with release notes from Github Changelog.md
- Push the new version of the library to YNAP’s private Cocoapods repo so that we can download the specs from our private repository
- Automatically announce the new release of the library on Slack channel
We achieved this with a combination of custom scripts, Fastlane tools and Travis CI. There are few changes introduced as part of this process to help automated deployments of the iOS-SDK. The iOS developers have to maintain the Changelog.md file for release notes and bump version in each .podspec files so that the deployment script will read this information for release automation.
We can read the version from the .podspec file and create an automated tag on Github using Fastlane.
1 2 3 4 5 6 |
$version_from_podspec = version_get_podspec(path: “iOS-SDK.podspec”) UI.message(“Creating tag with #{$version_from_podspec} on github !”) add_git_tag( tag: $version_from_podspec ) sh “git push origin #{$version_from_podspec} “ |
This will create an automated tag on Github with a version mentioned in the .podspec files. The automated deployment script publishes the release on Github.
1 2 3 4 5 6 7 8 9 10 |
UI.message(“Creating New Release on Github”) set_github_release( repository_name: “YTech/iOS-SDK”, api_token: ENV[“GITHUB_TOKEN”], name: “iOS-SDK “ + $version_from_podspec + “ Release “, tag_name: $version_from_podspec, commitish: “master”, description: release_notes, is_prerelease: false ) |
This script will publish the release on Github with release notes attached to it. Once the release is published on Github, then our client apps can access the new code release via Carthage by pointing the apps to specific versions in the Cartfile of the client apps.
However, for the clients using CocoaPods, we need to push the library to YNAP’s private Cocoapods repository using following code:
1 2 3 4 |
UI.message(“Pushing Pods to YNAP CocoaPods Spec Repo”) all_pods.each do |uploading_pod| pod_push(path: “#{uploading_pod}.podspec”, repo: “YNAP-CocoaPods”) end |
We have an array of pods that need to be uploaded on the private CocoaPods repo. Once that process is complete, it triggers an automated message on our Slack channels, as below:
Automated Enterprise Distribution of Client App
After the automated release process of our iOS-SDK, the next step is to enable CD of the client app to ensure that the new features are available for testing by our QA engineers or the Product Owners.
In order to achieve CD of the client iOS app from Travis CI, we use enterprise distribution methods. Enterprise Distribution allows anyone to download the client iOS app as long as they trust the settings from the enterprise account.
The process of setting up the automated distribution from Travis CI requires lots of scripting and security configuration.
Travis CI creates the fresh VM for each build and destroys the VM after the build finishes. This can create a lot of setup per build including encrypting and decrypting certificates, creating the keychain, importing certificates in the keychain, downloading provisioning profiles, and the list goes on…
Fortunately, Apple has a security command line tool which allows us to handle all these tasks without any pain, and we can easily wrap these using Fastlane tools.
The main challenge with Travis CI is dealing with certificates. For security, we have encrypted our certificates with strong encryption keys and we decrypt the certificates back to p12 format in the Travis before_all step.
1 2 3 |
before_install: — chmod a+x ./fastlane/*.sh — openssl aes-256-cbc -k $ENCRYPTION_PASSWORD -in path_to/certificate.enc -d -a -out cerificate.p12 |
Now that, we have our certificate on the Travis VM, we can add that to our keychain using Fastlane actions create keychain and import certificates.
Once we have a certificate in the keychain we can perform further code signing activities like downloading provisioning profiles and codesign the app.
Next, we have to archive the app using the enterprise distribution method, create a .ipa file, upload it to the Fabric for beta testing and finally send a Slack message to the team to notify that a new version of the app is ready to test.
We gather the test notes from the last 10 commits from GitHub and send the builds to the specific Crashlytics group.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
gym( export_method: “enterprise”, scheme: “iOS-SDK-Client”, skip_profile_detection: true, configuration: “Release”, include_bitcode: false ) crashlytics( crashlytics_path: “./Pods/Crashlytics/”, api_token: ENV[“CRASHLYTICS_API_KEY”], build_secret: ENV[“CRASHLYTICS_BUILD_SECRET”], groups: “iossdk-testers”, notes: get_crashlytics_release_notes(), notifications: true ) |
This will upload the iOS-SDK client app to Fabric and then QA engineers and Product Owners will receive the enterprise build to download on their iOS devices.
The process of downloading the enterprise build is a bit different than ad-hoc builds. The step by step guide can be found here.
Customising Travis CI
So, we now have a completely automated release of the iOS-SDK and continuous enterprise distribution of the iOS-SDK client app.
The final step is to customise the Travis CI set up so that we can achieve the benefits of the build matrix feature without duplicating CocoaPods releases and enterprise app distribution.
To achieve this, we need to set up some environment variables in the build matrix and write a custom script to trigger the builds conditionally.
1 2 3 4 5 |
matrix: include: — env: CI_IOS=’10.2' CI_DEVICE=’iPhone 7' ARCHIVE=1 POD_RELEASE=1 — env: CI_IOS=’11.2' CI_DEVICE=’iPhone 6s’ — env: CI_IOS=’11.2' CI_DEVICE=’iPhone 8' |
Once we set the environmental variables, we can conditionally check which scripts need to be executed.
1 2 3 4 5 6 7 8 |
if [[ “$BRANCH” == “master” && “$POD_RELEASE” = “1” && “$ARCHIVE” = “1” ]]; then echo “====Making Pod release for the SDK ====” bundle exec fastlane pod_release bundle exec fastlane destribute_enterprise_app else echo “===== Executing Normal Build =====” bundle exec fastlane ios default fi |
Using this approach we can avoid creating duplicate releases and multiple distributions of the iOS-SDK client app.
As shown in the above build matrix, the first build takes the longest as Travis creates the release and distributes the app. The subsequent builds take far less time as they are normal iOS builds and less complex.
Bingo! We have launched our iOS-SDK CI/CD rocket.
What’s Next?
Although we have completely automated the release and distribution process, we can always improve. Next steps:
- Automate the remaining bits to achieve one-click deployments of Mobile SDKs. e.g automatically create release notes from Pull Request titles
- Automatically delivering SDK Release notes to the wider business highlighting the main features delivered
- Evaluate CI services either in-house or on the Cloud depending on which better suits in our Mobile development processes
Conclusion
CI/CD practices might be considered important in iOS app development, but they are absolutely crucial for building and maintaining iOS SDKs.
The code quality requirements for SDKs are much higher and need to meet with all the client iOS apps specifications.
Through the combination of custom scripts, Fastlane tools and Travis CI, we have achieved the Enterprise Continuous Delivery of our iOS-SDK library.
Do you have CI/CD for your mobile SDKs? Share your experience on CI/CD for mobile SDKs in the comments below. .
P.S : Big thanks to Luca D’Antona (Mobile Engineering Manager) at YNAP, for reviewing this article and supporting me during the setup of CI/CD for mobile frameworks.