How to Migrate iOS projects and CI/CD to M1 Macs by@codemagic

How to Migrate iOS projects and CI/CD to M1 Macs

image
Codemagic CI/CD HackerNoon profile picture

Codemagic CI/CD

CI/CD for mobile apps

Apple silicon processors are a revolution in the world of desktop CPUs. The transition from Intel's x86_64 architecture to Apple's arm64 was quite smooth for the consumers, thanks to the Rosetta 2 emulator, which allows translation of x86_64 commands to arm64 without a significant loss in performance. The developers, however, also needed to rely on the emulation because when the new architecture was first released at the end of 2020, most developer tools and libraries didn't support arm64 architecture. And that could result in either unstable performance or crashes in some cases.

Now that the new Apple silicon chips have been around for almost two years, things have changed. It is the best time to look into migrating your project for the arm64 architecture if you have not done so already. Compared to last year or the end of 2020, most dependencies and tools now support the new architecture, so you won’t face any major hurdles in the transition during the migration process.

In addition, we can’t help but mention that your favorite CI/CD provider Codemagic has released the first Mac mini M1 build machines to the public! Some of our customers have already tried the powerful build machines with the new efficient chips and are getting much faster build times. So, let’s discuss Intel-to-M1 migration: migrating the CI/CD pipeline for your project from the good old x86_64 to the shiny new arm64.

This article covers the following topics:

  • Requirements
  • Working with the new architecture
  • Updating third-party dependencies
  • Build times for Mac mini M1 and Mac Pro

So let’s get started!

Requirements for migrating to M1

At the time of writing, to migrate your workflows to the new Apple M1 virtual machine on Codemagic, you must use Xcode 13.3+ because the base image of the macOS M1 build machine has it preinstalled. Xcode 13.3.1 (13E500a) is the latest version and is used by default when you set 13.3, 13.3.1, edge, or latest in your codemagic.yaml file or select it in the build settings in the Workflow Editor for Flutter apps.

When Apple announced its custom silicon in 2020, it began a two-year transition to the new architecture. Developers could take advantage of the Developer Transition Kit (DTK) to test, update, and transition their apps to the Apple silicon.

In the beginning, developers used Rosetta emulation to use apps built for a Mac with an Intel processor. Many CocoaPods dependencies and Swift packages were incompatible with the arm64 architecture in the initial months. As the developers started transitioning and getting the latest M1 and M1 Pro/Max/Ultra series devices, they requested open-source projects and different SDKs to support the arm64 architecture.

Now that we are almost at the end of the transition, numerous popular open-source projects, like Alamofire, Kingfisher, or the Firebase SDK, have added support for the Apple silicon machines. Now, you no longer need to use Rosetta emulation in the majority of cases and can take advantage of the raw processing power of Apple’s efficient chips.

Working with the New Architecture

While the architecture of the iPhone is already arm64, Intel used x86_64 simulators. With the new M1 series devices, the simulators also run on the arm64 architecture.

Apple has provided a detailed article for resolving architecture build errors on Apple Silicon

Xcode already provides the default architecture build settings that help you build and ship on all the platforms. One of the settings is Build Active Architectures Only, a boolean value with the default value as Yes for Debug and No for Release configurations. Xcode automatically builds for the selected architecture if the value is set to Yes. So, if you are using an Apple Silicon machine, it builds for the arm64 simulator, and if you are working with an Intel machine, it builds for the x86_64 simulator.

While your device and simulator may have different architectures, especially when using an Intel machine, you only want Xcode to build for one or the other.

Go to your project, select your target, and select Build Settings. Under the Architectures section, set Build Active Architecture Only to YES for debug builds. This ensures that the compiler generates the binary for only one architecture.

image

Do the same for the Pods as well:

image

If you set it to NO, Xcode will build the project for all the valid architectures. However, you may see an error that looks something like this:

Could not find module 'ColorKit' for target 'x86_64-apple-ios-simulator'; found: 
arm64-apple-ios-simulator, at: /Users/rudrankriyam/Library/Developer/Xcode/DerivedData/Musadora-ccjzyuildzfnokedenrcjqixspxo/Build/Products/Debug-iphonesimulator/ColorKit.swiftmodule

Setting Build Active Architecture in Xcode to YES also reduces the build time drastically, as you are compiling for one architecture instead of two. This is a win-win situation for you!

Updating third-party dependencies

Your project may use CocoaPods, Carthage, and/or Swift Package Manager to manage third-party dependencies.

It’s simple to manage CocoaPods if you use Homebrew on an M1 Mac:

brew install cocoapods

If you use Carthage, the best solution is to use XCFrameworks. This helps to build only the valid architectures that you need to build the app:

carthage update --use-xcframeworks

You might need to update your pods or packages to the latest version. This increases the likelihood of getting support for a universal binary (which contains executable code for both x86_64 and arm64 architectures) so that you can easily migrate from an Intel machine to an Apple silicon machine without any hiccups.

In this post, we use a sample project with a combination of third-party dependencies. We use both CocoaPods and Swift Package Manager.

The project’s dependencies as Swift packages are:

  • Kingfisher
  • Swinject
  • Alamofire

The project’s dependencies as Pods are:

  • SwiftLint
  • AppsFlyerFramework

The initial project has an old version of a framework to showcase the error that occurs when an XCFramework does not support the arm64 architecture.

pod 'AppsFlyerFramework', '6.2.0'

We can’t build a universal binary ourselves, as we don’t have the source code. If you run the project, you get an error similar to the following:

ld: in /Users/rudrankriyam/Downloads/Silicon/Pods/AppsFlyerFramework/iOS/AppsFlyerLib.framework/AppsFlyerLib(AFSDKKeychainFactory.o), 
building for iOS Simulator, but linking in object file built for iOS, 
file '/Users/rudrankriyam/Downloads/Silicon/Pods/AppsFlyerFramework/iOS/AppsFlyerLib.framework/AppsFlyerLib' 
for architecture arm64

You need the arm64 architecture to run the project in the simulator. But this version of the framework doesn’t support it.

To fix this issue, search the framework’s repository and check if they have released a version that adds support for arm64. As mentioned earlier, many priority frameworks and open-source projects have provided support for running on the latest silicon chips. Fortunately, arm64 support was added for this framework in version 6.3.0, and we can update the Podfile to fetch the latest version.

pod 'AppsFlyerFramework'

After you enter the command pod update, you can run the project successfully on M1 devices and Intel machines.

If some dependency does not yet support arm64, you can ask the framework’s author to provide a newer updated version that supports both architectures. Taking the previous example of an iOS SDK that transitioned to support arm64 by providing a universal XCFramework, AppsFlyer iOS SDK released arm64 support in April of last year after developers requested it.

Despite many dependencies providing quick support, the framework you are using may be outdated. It may also be a proprietary framework that doesn’t give you access to the source code. Suppose you don’t see any updates for these frameworks/packages in the foreseeable future. In that case, you can have a look at this article on “Hacking native ARM64 binaries to run on the iOS Simulator”. As the name suggests, it’s a hack to get things working, and we only recommend using this extreme workaround as a last resort.

Build times for M1 Mac mini and Intel Mac Pro

You can take inspiration from open-source projects that have been successfully migrated and work on the new chips. Take a look at a few examples of such projects here.

To understand the build time differences between the M1 and Intel machines and get a better idea of the superior efficiency and speed of M1 Mac minis, we have built and run tests on the official Wikipedia iOS app. This demonstrates a real-world scenario with hundreds of tests. It’s an open-source app that you can explore yourself.

Test name

Mac mini M1

Mac Pro

Installing scripts

17s

163s

Building project

254s

280s

Running tests

270s

395s

Overall

573s

883s

You can see the significant differences in the numbers, especially while running tests — they run substantially faster on the M1 build machines. After running the same tests for this app multiple times, we see a decrease of 35–40% in overall build times. The gap between the numbers adds up when running many builds and saves considerable time for a faster iteration process.

Conclusion

Apple silicon chips are the future of the Mac hardware, and it only gets better from here on out. Popular open-source frameworks and SDKs already support the new chips, so the transition is much easier than it was a year ago.

Reminder: M1 Mac mini build machines are already available on Codemagic. If you’re on the pay-as-you-go plan, just use instance_type: mac_mini_m1 to try them out. If you’re on the Professional plan and want to set up a trial for the Mac mini M1, reach out to our customer engineering team to help you get fast green builds on the new machines!

Sign up to try out Mac mini M1 build machines

Also posted on Codemagic blog and written by Rudrank Riyam.

Comments

Signup or Login to Join the Discussion

Tags

Related Stories