The State of the Art of iOS DevOps

Dino Bozic
Azikus
Published in
14 min readDec 4, 2020

--

One of the main aspects of software development is software quality. Products with high quality tend to go closer to the top in different statistics charts, especially financially oriented ones. Thanks to DevOps, applying the best software development practices and techniques has never been easier.

The definition of DevOps (Development and Operations) has never been clearly defined. Its meaning combines multiple aspects of each software product life-cycle. The main goal which every DevOps engineer tends to achieve is to ensure continuous and fast product delivery with the highest possible quality.

Nowadays, mobile development companies and clients are focused on fast product delivery where quality is often neglected. DevOps is a good solution for those fast, agile product developments since it unites the need for speed and final product quality.

Things covered by DevOps usually involve:

  • Project startup — Xcode, package management
  • Code management — keeping your code clean and well-formatted
  • Automated testing
  • iOS code signing
  • Deployment — App Store Connect, CI

The new beginnings

“The early bird catches the worm”

Every high-quality project starts with a high-quality start. It is important to invest time into the initial setup to make the future development process easier. That means spending resources to choose an appropriate architecture, set up a project structure, and select required tools.

This results with less bugs, a high stability rate, easier development for the engineers, and overall fewer resources being unnecessarily spent.

Xcode

Every iOS project starts with Xcode, an IDE developed by Apple, containing all the required toolset to create apps for Mac, iPhone, iPad, Apple Watch, and Apple TV. This is the ultimate root of creating a high-quality app. With the initial app setup in Xcode, you can save yourself a lot of time in future app deployments.

Targets

Xcode setup begins with managing your project targets. Each target represents a product to build. That being said, there are multiple ways you can distinct your targets. The most obvious one is the distinction between app types based on the device — the target can represent an iOS app, watchOS app, or OS X app. Another important distinction is another one widely used — test/production distinction.

Xcode target settings

A target is defined by its own bundle identifier, which is used as an identification to the operating system and App Store. It is also commonly used as an asset to connect with different services, like Facebook, Google, Firebase, etc. With different targets you can adjust their settings and with their references you can produce code run only by the desired target. This way, you can separate your test/production API calls, adjust UI and do whatever is needed in each environment. Often, this target distinction is usedAlso, targets are a great way to manage whitelabel apps with said possibilities.

Xcode configuration

An elegant alternative to the multiple target creation is managing your build configuration settings with xcode configuration file (.xcconfig). Xcode offers “Debug” and “Release” configurations by default when we create a new project. You can additionally add your own personalized configurations which are further configurable via .xcconfig files.

Xcode configurations

Xcode configuration file (.xcconfig) is a simple key-value based file where you store your build settings. For each configuration you create you can set a belonging configuration file which then dictates build behaviour.

More on this: https://www.appcoda.com/xcconfig-guide/

Schemes, build phases, build settings

Schemes are used to build and run apps from Xcode. They are usually closely connected to targets — each target has its own running scheme. However, you can create additional schemes for additional purposes, like running different scripts before building the same codebase or running with memory management diagnostics. Scripts are managed in the build phases section of Xcode project settings. For each different script you have to add the new phase to the build. These sections also manage compile sources and bundle sources for each target. Finally, duty of build settings sections is to handle different types of application settings, like supported architectures, code signing, etc.

Xcode scheme management

Package managers

CocoaPods

http://cocoapods.org

CocoaPods is a tool which manages dependencies for Xcode projects. The secret lies in its simplicity, where all your dependencies are specified in a simple text file called Podfile. CocoaPods simplifies the usage both for library authors as for library users. It is personally my favorite tool to use. It is easy to install via sudo or command line and it’s also really intuitive to use. You just have to add the library you desire to install into Podfile, optionally you can add the version of the library and that’s it. Eventual updates are managed quite easily with pod install or pod update commands. The downsides of CocodaPods are increasing the value of build time and hard process of making the code work if something goes wrong with the imported library. Also, since CocoaPods are implemented in Ruby, an additional language knowledge is mandatory to introduce new libraries.

Podfile example

Carthage

https://github.com/Carthage/Carthage

Just as CocoaPods, Carthage is also a tool to simplify dependency management for an iOS application. Dependencies are also stored in a special file which is then used for framework installations and updates. Carthage is also written in Swift which means that iOS developers can be easily familiar with the Carthage implementation and further development. One of the best benefits that Carthage offers is it being flexible and it is easy to integrate it in big and ongoing projects, as opposed to Cocoapods. It can also be easily integrated into a CI server. However, Carthage currently supports only dynamic frameworks without any support for static libraries. And since Carthage is a relatively new tool, some important libraries may not be available.

Cartfile example (Carthage github documentation)

Generally, both Carthage and CocoaPods are more than advisable to use in your projects. On one hand, Carthage is preferable because of its Swift origin and it excels with bigger projects, since only one build is required for libraries to be integrated. CocoaPods on the other hand increases overall build time since the tools has to build pods on every clean build. But it excels in its simplicity and wide-range of supported libraries. Technically, you can use both tools in your project and divide the work based on their adaptability to the problem.

Swift package manager

Swift package manager is a tool integrated with the Swift build system, used for managing distribution of Swift code. Swift organizes its code into modules, which are then reusable for any other wanted purpose. Dependencies are also represented by modules, with their known source and requirements for the package version. Package manager recursivelly runs the automated process of downloading and building all of the project dependencies.

Code clean as a whistle

“It is not enough for code to work.” ― Robert C. Martin

In my engineering career, I don’t think i have heard a syntagm being said more than “clean code”. The importance of clean code is probably known to any decent mobile developer. It is a must have for a quick and successful product development. Clean code is primarily important to enable future product improvements and deployments. DRY (Don’t repeat yourself), KISS (Keep it simple stupid) and YAGNI (You ain’t gonna need it) are just some of the numerous clean code principles which are hardcoded in the successful developer’s minds.

There are a few tools to assist with the clean code management in the iOS world, besides handling indentations with Ctrl+I.

SwiftLint

https://github.com/realm/SwiftLint

SwiftLint is a rule-based tool for Swift code management. It is easy to install and easy to maintain. After an easy integration with the project and Xcode, developer has to choose the set of rules to help him format the code. There are over 100 available rules, thanks to SwiftLint community. Everyone can participate in the future development with their own pull requests on the SwiftLint’s github. Rules can be optional — warning will pop up in the Xcode editor, or they can be mandatory — Xcode would throw an error. Additionally, you can create your custom rules. Also, there is an option to “break” the rule in the editor if that’s really necessary.

All in all, SwiftLint is an all-around tool considering code cleanliness and trivial to use.

SwiftLint file example

SwiftFormat

Just as the name suggests, SwiftFormat is a tool to format Swift code. SwiftFormat thanks its success to its versatility — it is used in 4 different ways: As a manually ran command tool, as a source editor extension, as a build phase in Xcode project and as a Git pre-commit hook. Next to standard formatting, SwiftFormat handles additional Swift clean code problems, like implicit self, redundant parentheses and other code deviations. It also enables a developer to customize a set of rules and option used to format the final code product. A difference to SwiftLint is that a SwiftFormat fixes the deviant code, rather than telling you to do so.

Testing, attention please

“One test a day keeps the doctor away”

As you already know, tests are the only sufficient way of making sure your code works as it is imagined. For sure, you have crossed paths with some edge cases ending up on your beta test. Testing is a reliable way to detect your logic failures, double-check your UI and acquire performance data.

Xcode offers testing tools within its XCTest framework. Probably the most important and time investing part of testing is Unit testing. Each unit test should assert the expected behaviour of a method or a function in your code. Ideally, one test is required for every scenario a method or a function offers. One should especially identify and cover all the edge cases with the highest quality test writing. Similar type of tests are Integration tests. Instead of covering a small logic part, integration tests cover the behavior of larger systems of functions. These tests are used to make sure that the components/modules work together. Other than Unit and Integration testing, XCTest is able to produce UI testing. UI testing compares recorded state of the UI with the state produced by a more recent build. That means that there are two modes to pay attention to — recording mode and testing mode. Finally, there are Performance tests where user can measure performance information, like time taken or memory used.

Testing in Xcode is done by importing XCTest and subclassing XCTestCase. When you add a new testing class, Xcode kindly offers you a template to fill your testing needs.

Xcode test example on XCTestCase template

More on native Apple testing on: https://developer.apple.com/documentation/xcode/testing_your_apps_in_xcode

Testing coverage

Code/Test coverage is a measure that quantifies the amount of code covered by existing tests. It is a measure that allows the identification of chunks of codes that are left untested. It, however, does not include the test quality.

You can enable code coverage in Xcode by going into test options and marking the Code coverage checkbox. Coverage report is then generated for every subsequent testing session in the Report navigator. Newly retrieved coverage data can then be shown inline by checking on the Code coverage option in the Adjust Editor Option button.

Enabling code coverage while testing
Enabling code coverage information inline

Codebeat

https://codebeat.co

Code review is an important part of any product development since it combines more viewpoints on the same codebase with an extra pair of eyes to capture mistakes. Codebeat is a tool that automates the process of code review. There is a certain amount of things we always check when reviewing code, like coding style, coding practices or outdated APIs.

Codebeat offers an automated code analysis when ran. It marks occurrences of well known code imperfections, for example code duplication. Similar to SwiftLint, Codebeat also supports customizable rules where you adjust your code review to your personal liking. That being said, during the process of code review, you can pay attention to things that lint or code format ignored. That includes cyclomatic complexity, block nesting or other generic limitations you require.

Signature here, here and here please

“Finish strong.”

After your code is clean, your bugs are resolved, features working, tests are successful and the QA has given you a green light, it is time to deploy your application.

iOS code signing

One of the things Apple requires from you is code signing. Code signing is Apple’s way to make sure applications are signed by trusted developers with valid certificates. Various security elements are involved in the process and the final outcome is making sure that the code wasn’t changed or corrupted after it had been signed.

To start with the code signing process, you will need a certificate signing request, created from your own Keychain Access. This is used to confirm developer’s identity in the process.

There are two types of certificates you need to worry about. Development certificates are used for internal deployment. They are necessary for testing your application on internal devices. On the other hand, when you’re ready to publish your app, you will need a distribution certificate which is necessary to run your app on any device.

More on certificates on: https://developer.apple.com/support/certificates/

Along with certificates, you need to take care about provisioning profiles. Just like the case is with certificates, you have both development and distribution provisioning profiles. Once again, development profiles are used for internal app testing, while distribution profile is needed to publish your app on the AppStore. Provisioning profiles are a combination of the app data, like app ID and the belonging certificates.

More on provisioning profiles: https://developer.apple.com/documentation/appstoreconnectapi/profiles

After you’ve acquired all needed certificates and provisioning profiles, you need to import them into the Xcode. You can then select whether you want Xcode to automatically manage your code signing, where you just have to select your team, or you want to manually upload the provisioning profiles.

A great read which explains code signing in detail: https://blog.codemagic.io/how-to-code-sign-publish-ios-apps/

Deployment

“It is time to meet the world”

App Store Connect

App Store Connect is an Apple’s service to upload, submit and manage your applications. It is necessary to create a new application for every build you want to test (if you are testing through Apple services) or deploy. For example, if you want to deploy the build of every of your project’s targets, you’ll have to create an application for every target with its own bundle ID.

After creating your App Store Connect, it is time to return to Xcode. There, you can upload your app via “Archive” option. Xcode then automatically checks all your signatures, profiles, app versioning and other important settings. After the checks have passed and upload finished, you will have a build uploaded to the App Store Connect.

Apple offers TestFlight as a solution to internally test your apps on real devices. You just have to send invites to testers and they will be able to test every uploaded build. They will be able to send you feedback with screenshots and the service will register crashes. Additionally, TestFlight offers Beta testing, with external groups option.

App Store Connect also offers enabling in-app purchases and subscription methods. It also covers app privacy settings, ratings and reviews.

Finally, your application has to pass a review. You have to provide crucial information about your application — Final name, app screenshots, review information, etc. Apple personnel will carefully go through your app and inform you about eventual tweaks you have to make to publish the app. If everything goes smoothly, you’ll get a green light and showcase your app to the world (Or part of it, depending on your settings).

More on App Store Connect: https://developer.apple.com/app-store-connect/

Continuos integration (CI)

Continuous integration is an automated process of testing and deploying new builds of an application. It is an elegant way to reliably test new code updates and deploy builds once the application is ready to update. A good CI tool will help you save time on the monotonous repeating actions of producing application versions.

Using native Apple developer tools for CI has always been a secure way to handle the deployment process. They can’t be manipulated by third-party frameworks and they are well documented by Apple. Some of the major tools include xcodebuild, altool, agvtool, codesign and security. You can already realize that these tools are used via command line, so you will have to spend some time to understand what each of these tool’s possibilities are. All of the tools produced by companies are using the native Apple developer tools for testing and deployment under the hood.

Fastlane

https://github.com/fastlane/fastlane

Fastlane is one of the most popular choices for a CI. It is an open-source platform based on Ruby, and its functionality is defined in a configuration file called Fastfile. Fastfile is structured from lanes where each lane represents one process which is being automated. Those lanes help the developer to run desired commands in order every time a new build is introduced. Fastlane offers over 170 actions which are available for Android, iOS and Mac platforms.

Bitrise

Next to Fastlane and native development tools, there are a lot of cloud-based paying options which handle your integration process. There are Nevercode, CircleCI, there used to be BuddyBuild and there is Bitrise. Bitrise is really hot right now, and one of the most favorite developer selections for CI. Bitrise supports a wide range of technologies, from iOS and Android to React Native or Flutter. It also includes a large number of services available to automatically connect your application with (Firebase, Slack, Github, …). Bitrise is very well documented so its usage is quite easy to handle.

Long story short

All these tools and techniques mentioned are just a fraction in the overall DevOps possibilities. There are a countless possible ways to combine stages of the development and deployment process and it all unites under the DevOps term.

It is definitely smart to invest into DevOps which is supported by a recent surge in DevOps engineer position openings on the market. For a big company with complex products, DevOps engineer is a must have to handle and simplify the overall production process.

Dino is a valuable member of our iOS team.
At Azikus, we design and develop top notch mobile and web apps.
We are a bunch of experienced developers and designers who like to share knowledge, always staying up to date with latest and newest technologies.
To find out more about what we do, feel free to check our
website.

--

--