Building an iOS Universal Static Library or XCFramework

The Chilkat iOS download includes a separate static library for each supported architecture slice. This page shows how to combine those slices correctly — either into universal static libraries or into a modern XCFramework.

Jump to: Library layout Recommended outputs Device lib Simulator lib XCFramework Full script Don't mix slices Inspect a slice

The one rule to remember A “universal” static library can combine multiple architecture slices, but only when those slices belong to the same Apple platform:
  • Combine device slices with other device slices.
  • Combine simulator slices with other simulator slices.
  • Never combine device and simulator slices into one traditional .a file.
For modern Xcode projects, the preferred format is an XCFramework, which bundles the device and simulator libraries together while keeping their binaries properly separated.

Chilkat iOS library layout

The download contains static libraries organized by architecture slice:

lib/
├─ arm64/       libchilkatIos.a   device
├─ arm64e/      libchilkatIos.a   device, A12 and later
├─ armv7/       libchilkatIos.a   older 32-bit device
├─ armv7s/      libchilkatIos.a   older 32-bit device
├─ arm64-sim/   libchilkatIos.a   simulator on Apple Silicon
└─ x86_64/      libchilkatIos.a   simulator on Intel

The important distinction is not only the CPU architecture, but also the Apple platform the slice was built for. For example, arm64 and arm64-sim are both ARM64, yet they are not interchangeable — one targets physical iOS devices and the other targets the iOS Simulator.

SlicePlatform
arm64iOS device
arm64-simiOS Simulator on Apple Silicon
x86_64iOS Simulator on Intel

Recommended outputs

There are two good ways to package the Chilkat iOS static libraries.

Option 1 — Two universal static libraries

Build one library for device and one for the simulator, and link whichever matches your current build target.

LibraryUsed for
libchilkatIos.aPhysical iPhone / iPad
libchilkatSimIos.aiOS Simulator
Recommended

Option 2 — XCFramework

Package both libraries into Chilkat.xcframework. Xcode automatically selects the right binary for each build, so this is usually the best format for modern projects.


Build the iOS device universal library

Combine only the device slices:

From the chilkat-ios directory
cd lib

/usr/bin/libtool -static \
  arm64/libchilkatIos.a \
  arm64e/libchilkatIos.a \
  armv7/libchilkatIos.a \
  armv7s/libchilkatIos.a \
  -o libchilkatIos.a

cd ..

This creates lib/libchilkatIos.a, for use when building for a physical iOS device.


Build the iOS Simulator universal library

Combine only the simulator slices:

cd lib

/usr/bin/libtool -static \
  arm64-sim/libchilkatIos.a \
  x86_64/libchilkatIos.a \
  -o libchilkatSimIos.a

cd ..

This creates lib/libchilkatSimIos.a, for use when building for the iOS Simulator.


Build an XCFramework

After creating the separate device and simulator static libraries, package them into an XCFramework. You also supply the Chilkat public header directory — replace include below with the actual path to the Chilkat headers.

xcodebuild -create-xcframework \
  -library lib/libchilkatIos.a -headers include \
  -library lib/libchilkatSimIos.a -headers include \
  -output Chilkat.xcframework

The resulting Chilkat.xcframework can be added to an Xcode project as a binary dependency.

Point -headers at a directory The -headers option must point to the directory containing the Chilkat .h files (for example include or /path/to/chilkat/include) — not at a single header file.

Complete example script

This script builds the device library, builds the simulator library, and packages both into an XCFramework.

#!/bin/bash
set -e

# Run from the directory that contains lib/ and include/,
# where lib/ holds the Chilkat architecture slices and
# include/ holds the Chilkat header files.

rm -f  lib/libchilkatIos.a
rm -f  lib/libchilkatSimIos.a
rm -rf Chilkat.xcframework

echo "Building iOS device universal static library..."
/usr/bin/libtool -static \
  lib/arm64/libchilkatIos.a \
  lib/arm64e/libchilkatIos.a \
  lib/armv7/libchilkatIos.a \
  lib/armv7s/libchilkatIos.a \
  -o lib/libchilkatIos.a

echo "Building iOS Simulator universal static library..."
/usr/bin/libtool -static \
  lib/arm64-sim/libchilkatIos.a \
  lib/x86_64/libchilkatIos.a \
  -o lib/libchilkatSimIos.a

echo "Building XCFramework..."
xcodebuild -create-xcframework \
  -library lib/libchilkatIos.a -headers include \
  -library lib/libchilkatSimIos.a -headers include \
  -output Chilkat.xcframework

echo "Done."
Use Apple's libtool The scripts call /usr/bin/libtool explicitly on purpose. A different libtool (from Homebrew, MacPorts, etc.) may take precedence on your PATH and will not produce a valid static library. Verify with which libtool.

What happens if device and simulator slices are mixed

Do not do this Combining device and simulator slices into one traditional .a mixes different Apple platforms into a single library:
# WRONG — mixes device (arm64/armv7) with simulator (arm64-sim/x86_64)
/usr/bin/libtool -static \
  arm64/libchilkatIos.a \
  arm64-sim/libchilkatIos.a \
  x86_64/libchilkatIos.a \
  armv7/libchilkatIos.a \
  armv7s/libchilkatIos.a \
  -o libchilkatIos.a

The linker may then fail with an error similar to:

ld: building for iOS Simulator, but linking in object file built for iOS

or:

ld: building for iOS Simulator, but linking in object file built for macOS,
for architecture x86_64

The exact wording varies, but the cause is the same: the app is built for one platform while the linker found an object file built for another.

Why architecture alone is not enough

A slice is identified by its CPU architecture and its target platform. The same architecture name can refer to more than one platform:

ArchitecturePossible platforms
arm64iOS device, iOS Simulator, macOS
x86_64iOS Simulator, macOS

These are different build targets and cannot be freely mixed simply because the architecture name matches.


Inspecting a static library slice

To confirm what platform a slice was built for, inspect its load commands:

otool -lv libchilkatIos.a

Look for platform-related load commands such as:

LC_BUILD_VERSION
LC_VERSION_MIN_IPHONEOS
LC_VERSION_MIN_MACOSX

A device library should identify as an iOS device build, and a simulator library should identify as an iOS Simulator build — a simulator slice should not appear as a macOS slice merely because it uses x86_64 or arm64.


Summary

  • Build two separate universal static libraries: libchilkatIos.a (device) and libchilkatSimIos.a (simulator).
  • Combine device slices with device slices, and simulator slices with simulator slices — keep the two binaries separate.
  • For modern Xcode projects, package both into Chilkat.xcframework so Xcode picks the correct binary automatically.