Modernizing Your iOS App: How to Successfully Migrate Complex Projects from Cocoapods to SwiftPM

Dependency Management Sep 1, 2023

At Halodoc, we constantly look for opportunities to upgrade our tech stack and take advantage of the enhancement and improvements it provides. Having said that SwiftPM was on our radar for quite some time. We were looking forward to migrating from Cocoapods to SwiftPM since it was not mature enough back then we waited for a while.

What are CocoaPods & SwiftPM

Both are Dependency Managers for Xcode projects. CocoaPods is a 3rd party open source tool whereas SwiftPM is Apple’s native open source tool.

Why Migrate?

SwiftPM is Apple's Official dependency manager, it provides native integration, and faster performance by taking advantage of the Swift build system.

Steps involved in Migrating from Cocoapods to SwiftPM

  1. Understanding the existing Project setup
  2. Understanding the need for Project generation tool
  3. Explore Project Generation Tools
  4. Upgrade Third-Party Dependencies and Private modules
  5. Project/Workspace generation for different environments (Local, CICD)

1. Understanding the existing Project setup

Our existing system is vast and quite complex. We follow a "Modular Architecture", which has resulted in more internal modules and SDKs. Using CocoaPods, we are able to generate new Xcode projects and workspaces during pod install or pod update by pointing internal modules and SDKs to different sources for different environments, such as

  • Remote repos ( remote URL via version/ branch) for the CICD release pipeline
  • Local path for development.
Workspace/Project generation via Cocapods

2. Understanding the need for Project Generation Tool:

  • Our existing system requires Xcode workspace generation on demand for different environments. We have a lot of internal modules/SDKs and these module's origin will either be local path(Development) or remote repos(CICD release).
  • If we use SwiftPM directly then we have to keep changing the origin of these internal modules(local path or remote repo) or  We need an Xcode project generation tool like cocoapods which generated workspace with different origin based on the environment.

3. Explore Project Generation Tools

Xcodegen, Tuist , xcake, struct are some well-known Xcode project generation tools. After evaluating all tools we decided to go with Tuist, as it provides basic features that Xcodegen provides like Xcode project generation, dependency graph, pre-post actions and much more benefits like manifest file linting, build time improvement via cache etc.

Project generation tool comparison

Tuist Installation

  • curl -Ls https://install.tuist.io | bash

4. Upgrade Third-Party Dependencies and Private SDKs

Third-party libs

  • Upgrade the libs version to the latest with Swift Package support.
  • Some libs that don’t have support were replaced with alternative libs like cocoapods-key(with Arkana).

Private/Internal SDKs

  • Add SwiftPM support via Tuist project for internal SDKs(Its 3rd party libs dependencies).

5. Project/Workspace generation for different environments:

After adding Tuist project support for Internal modules, we have to add Tuist project support for main project with required Schemes(Stage, Prod, UnitTest, UIAutomation  etc), Target(App, NotificationExtension, UnitTest, UITests etc), build settings(use Tuist migration to extract build settings from existing project) and create Workspace manifest file as per needs.

Local development

  • Use Internal SDKs from the local path. i.e.  Root project directory -> SDKs directory

CICD

  • Clone Internal SDKs from Remote repos into the same SDKs directory within root project directory
Project/Workspace editing

Development Workflow comparison:

Challenges that we encountered and how we overcame it

1. IPA size was increased since  App Store no longer accepts bitcode submissions from Xcode 14 so we disabled bitcode as part of migration. It increased the IPA size.

2. Build time slightly increased in CICD, we took following actions to rectify it,

  • We used only required xcframeworks as some third party libs where being downloaded as Binary artifact and it had other frameworks that we don’t need.
  • Removed unnecessary armv7 architecture from frameworks.
  • Build time can be further reduced by making using of Tuist caching in CICD pipeline.

3. Adding Swift Package support for Internal SDK/Modules.

  • Since Unit Test targets were not accessible via Swift Package, later  we converted it into Tuist Project

4. Cocoapods subspec

  • In Cocoapods, we can have subspec and during "pod install/update" based on the config, Target will be created using specified subspec. We can create n number of Targets with source files from any sub directory of a module/pod.
  • SwiftPM doesn't work in that way, We must have unique Target name for different sub directory under a module/pod.
  • Due to this we have to specificly create unique targets in a module to replace subspec and import in source code based on needs.

5. Compiler errors, Crashes

  • Since the workspace is generated each time, xib's "custom class module" property were referring to bundle target if 'Inherit Module From Target' is checked. Which resulted in crash on run time, we had to set module manually.
  • For Obj-C module/libs(Internal lib, Realm etc), explicitly HEADER_SEARCH_PATHS had to be set.

Benefits

  • Download size reduced(~40%) by ~15mb and installation size reduced by ~105mb for our Consumer App
  • Taking advantage of Swift build system performance during dependency management.
  • Tuist caching helps us to pre-compile third-party libs and use it, which reduces the project build time even further.
  • No more merge conflicts in xcodeproj. Since we generate workspace, xcodeproj files each time via Tuist and we don't commit it in the repo.

Summary

Overall, We had some challenges during this migration process like increase in size, build time, crashes etc and we overcame it and are very happy with the results. Now our App's size has reduced by 40%, we have a better dependency management which uses Swift build system, build time is reduced via Tuist caching, and our MRs are more clean now with lesser merge conflicts.

Join us

Scalability, reliability and maintainability are the three pillars that govern what we build at Halodoc Tech. We are actively looking for engineers at all levels and if solving hard problems with challenging requirements is your forte, please reach out to us with your resumé at careers.india@halodoc.com

About Halodoc

Halodoc is the number 1 all around Healthcare application in Indonesia. Our mission is to simplify and bring quality healthcare across Indonesia, from Sabang to Merauke. We connect 20,000+ doctors with patients in need through our Tele-consultation service. We partner with 3500+ pharmacies in 100+ cities to bring medicine to your doorstep. We've also partnered with Indonesia's largest lab provider to provide lab home services, and to top it off we have recently launched a premium appointment service that partners with 500+ hospitals that allow patients to book a doctor appointment inside our application. We are extremely fortunate to be trusted by our investors, such as the Bill & Melinda Gates Foundation, Singtel, UOB Ventures, Allianz, GoJek, Astra, Temasek, and many more. We recently closed our Series D round and in total have raised around USD$100+ million for our mission. Our team works tirelessly to make sure that we create the best healthcare solution personalised for all of our patient's needs, and are continuously on a path to simplify healthcare for Indonesia.

Download Halodoc app via iOS and Android.

M Shyam Kumar

Software Development Engineer @ Halodoc