Origin of the problem

Personal blog – Star sky

Until Apple released the M1 chip, it used Intel chips without any problems. After the release of M1 chip, due to the different architecture (M1 is ARM64 architecture, Intel is X86_64 architecture), resulting in a lot of software running problems. If you use Xcode to build an emulator on M1, you may encounter the following error:

ld: In youpath/Pods/UMCommon/UMCommon_7. 3.5 / UMCommon framework/UMCommon (UMComBaseEvent. O), building for the iOS Simulator, but linking in object file built for iOS, The file 'youpath/Pods/UMCommon UMCommon_7. 3.5 / UMCommon framework/UMCommon' for architecture arm64Copy the code

or

ld: warning: ignoring file YoupPth/Build/Products/Debug-iphonesimulator/FMDB/FMDB.framework/FMDB, 
building for iOS Simulator-x86_64 but attempting to link with file built for iOS Simulator-arm64
Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_FMDatabaseQueue", referenced from:
      objc-class-ref in SqflitePlugin.o
ld: symbol(s) not found for architecture x86_64
Copy the code

These errors are caused by the existence of a. A or. Framework static library in the project. Previously, when we created static libraries, we would package one copy for the real machine (ARM64) and one copy for the emulator (x86_64), then merge the two copies into one package and import them into the project for use. On Intel models, the arm64 directive is used on the real machine and the x86_64 directive is used in the emulator (x86_64), so there is no problem. However, on the M1 model, the emulator runs in ARM64, and obviously running in X86_64 will cause problems.

Some of you might think that there are arm64 instructions in the package (real machine), which can be used by the arm64 emulator. Actually, that’s not what xcode does underneath, it looks for the real machine and the emulator looks for the emulator.

The solution

Common solution

For this type of architecture error, there are two solutions available online:

  1. Run Xcode in Rosetta mode.
  2. Modify theBuild Settings -> Excluded ArchitecturesAdd the Any iOS Simulator SDK option and set the value to arm64. The illustration is as follows:

Both solutions solve compilation problems, but they also have problems.

In iOS12 and later, iphone5 and below are no longer supported, and the later models are arm64 architecture, so here is no longer the previous armv6/armv7/armv7s/i386 instruction set description.

Rosetta solution description

Running in Rosetta mode is a solution to the problem of x86 software running on M1 machines. It translates x86 instructions into ARM instructions. This translation obviously has a performance cost, which is around 20% to 30%. This is not recommended unless it is absolutely necessary.

An Inspiring scenario is illustrated

Modifying the Excluded Architectures option has its problems. We set arm64 exclusion in the emulator to solve the problem that the emulator cannot compile ARM64.

It’s a little confusing that this setting works, as we know that on Intel machines the emulator is designed to bex86Mode of operation, exclusionarm64No effect at all. But in theM1On model, the simulator is based onarm64Way to run, ruled outarm64Instead, I can run. Isn’t that rubbing my IQ on the floor? But that’s what apple does whenM1On the model, the simulator is excludedarm64After the architecture, the emulator will still bearm64But the app in the emulator isx86The apple of this SAO operation we have to accept. The illustration is as follows:

In this case, the simulator and application can communicate via XPC, although there will be no problem in theory, but the communication time is not long, lead to some problems can rely on the timer judgment logic, sliding gestures, for example, the acceleration of judgment will be out of some problems, lead to the simulator in most cases the following table can’t trigger the inertial scrolling. – by kem

Other problems

Sometimes when an “Excluded Architectures” option excludes the arm64 instructions of the emulator and still fails to compile, this is usually caused by a discrepancy between the project Settings and cocoapods Settings, which usually resolves the problem. This can be resolved by adding the following to your Podfile:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = "arm64"
    end
  end
end
Copy the code

The optimal solution

From the above, we know that the problem is caused by the presence of.A or.framework in the project, which provide an incomplete instruction set. Apple also offers a solution to this problem, so let me elaborate.

Taking Xcode13 as an example, when we create the static library, the package compiled by the real machine only contains arm64 instructions, while the package compiled by the simulator contains both ARM64 and x86_64 instructions. I read some online tutorials that tell people to remove the arm64 from the emulator, but you don’t have to. Because to support M1 machines to run the simulator properly, the simulator must contain both arm64 and x86_64 instructions.

At WWDC 2019, Apple offered a new framework package format, XCFramework. Packages that were previously merged into different sets of instructions using LIPo are now merged into the XCFramework format using the new instructions.

Package into framework in the following format:

$ tree Release-iphoneos/TestFramework.frameworkRelease - iphoneos/TestFramework framework ├ ─ ─ Headers │ ├ ─ ─ TestFramework. H │ └ ─ ─ the TestManager. H ├ ─ ─ Info. The plist ├ ─ ─ │ ├ ─ ├ ─ TestFramework
$ tree Release-iphonesimulator/TestFramework.frameworkRelease - iphonesimulator/TestFramework framework ├ ─ ─ Headers │ ├ ─ ─ TestFramework. H │ └ ─ ─ the TestManager. H ├ ─ ─ Info. The plist ├ ─ ─ │ ├── ├─ ├─ ├─ ├─ ├── ├─ ├── ├── ├── ├── ├── Critique Exercises ─ Critique exercisesCopy the code

After being packaged into XCFramework, the format is as follows:

$ tree TestFramework.xcframeworkTestFramework. Xcframework ├ ─ ─ the Info. The plist ├ ─ ─ ios - arm64 │ └ ─ ─ TestFramework. The framework │ ├ ─ ─ Headers │ │ ├ ─ ─ ├─ ├─ testFramework.h │ ├─ ├─ testFramework.h │ ├─ ├─ testFramework.h │ ├─ ├─ testFramework.h │ ├─ ├─ testFramework.h │ ├─ ├─ testFramework.h │ ├─ ├─ ├─ ├─ ios, ├─ ├.h │ ├─ ios, ├─ ├.h │ ├.h │ ├─ ├── ─ ├─ ├─ ├─ ├─ ├── ├── ├── ├── │ ├─ │ ├─ │ ├─ │ ├─ │ ├─ │ ├─ │ ├─ │Copy the code

As you can see from the above, XCFramework simply puts the frameworks of two different instruction sets into the same folder (.xcFramework) and generates a configuration file info.plist. The resulting XCFramework perfectly solves the problem of M1 machines not being able to compile emulators.

The XCFramework creation instructions are also simple:

#-- A --
#Instructions:xcodebuild -create-xcframework -library <path> [-headers <path>] [-library <path> [-headers <path>]...]  -output <path>#Sample:
xcodebuild -create-xcframework -library youpath/TestFramework.a -headers youpath/TestFramework -library youpath/TestFramework.a -headers youpath/TestFramework -output youpath/TestFramework.xcframework

#-- For. Framework --
#Instructions:xcodebuild -create-xcframework -framework <path> [-framework <path>...]  -output <path>#Sample:
xcodebuild -create-xcframework -framework Release-iphoneos/TestFramework.framework -framework Release-iphonesimulator/TestFramework.framework -output TestFramework.xcframework
Copy the code

The key to overcoming the M1 model’s inability to compile emulators is that the emulator package contains both the ARM64 and x86_64 instruction sets. If you use an emulator package that only supports the X86_64 instruction set, this problem will persist even if packaged as XCFramework.

Afterword.

As it is, many third-party frameworks do not use XCFramework, and any framework in a project that does not support the arm64 directive for emulators will only run applications in Rosetta mode on M1 machines.

The resources

  • Apple core change, a developer’s nightmare?
  • Armv6, ARMV7, ARMV7S, ARMV8, ARMV64, i386, X86_64
  • Detail the iOS static and dynamic libraries
  • The XCFrameworks framework for Xcode11