Skip to content

iOS: opus.xcframework has no arm64 simulator slice — "Framework 'opus' not found" on Apple Silicon #31

Description

@danielgomezrico

opus_flutter_ios 3.0.1 ships an opus.xcframework that has no arm64 iOS-simulator slice. On an Apple Silicon Mac the simulator build targets arm64-ios-simulator, no slice matches, and the link step fails:

Error (Xcode): Framework 'opus' not found
Error (Xcode): Linker command failed with exit code 1 (use -v to see invocation)
Could not build the application for the simulator.

This makes the plugin unusable on the iOS simulator on any Apple Silicon machine, which is every Mac sold since 2020.

Environment

opus_flutter_ios 3.0.1 (latest on pub.dev)
Host Apple Silicon (uname -marm64)
macOS 26.2
Xcode 26.6 (17F113)
Flutter 3.41.1 stable

Root cause

The vendored xcframework contains only a device slice and an Intel-only simulator slice:

$ plutil -p ~/.pub-cache/hosted/pub.dev/opus_flutter_ios-3.0.1/ios/opus.xcframework/Info.plist
"LibraryIdentifier" => "ios-x86_64-simulator"   SupportedArchitectures => ["x86_64"]
"LibraryIdentifier" => "ios-arm64"              SupportedArchitectures => ["arm64"]
$ lipo -info .../ios-x86_64-simulator/opus.framework/opus
Architectures in the fat file: ... are: x86_64

The simulator SDK itself supports both architectures, so this is purely a gap in the prebuilt binary:

$ grep targets: $(xcrun --sdk iphonesimulator --show-sdk-path)/usr/lib/libSystem.tbd
targets: [ x86_64-ios-simulator, arm64-ios-simulator ]

Notes on related issues

  • Build failed for ios simulator #14 reported the same symptom in 2021 against the older slice layout (ios-i386_x86_64-simulator / ios-arm64_armv7_armv7s) and was closed as a duplicate of Cannot build example app #16. The slice layout has changed since then, but the arm64-simulator slice is still absent, so the failure remains on current versions.
  • Use source builds in iOS #11 ("Use source builds in iOS") would fix this permanently, because a source build produces every architecture the consumer asks for. The workaround below is the smaller, immediate fix if a source build is not close.

Workaround (verified)

I rebuilt the simulator slice locally and the app now builds, installs and runs on the simulator. Sharing the recipe in case it helps, and I am happy to open a PR with the regenerated xcframework if that is useful.

Build libopus for both simulator architectures. The autotools path cannot cross-configure for the simulator triple, but CMake can:

cmake -S . -B build-sim -G Xcode \
  -DCMAKE_SYSTEM_NAME=iOS \
  -DCMAKE_OSX_SYSROOT=iphonesimulator \
  -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
  -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \
  -DBUILD_SHARED_LIBS=OFF -DOPUS_BUILD_PROGRAMS=OFF -DOPUS_BUILD_TESTING=OFF
cmake --build build-sim --config Release

The existing device slice is a dylib, so the simulator slice has to be one too:

clang -dynamiclib -target <arch>-apple-ios13.0-simulator -isysroot "$SDK" \
  -install_name @rpath/opus.framework/opus \
  -Wl,-all_load libopus.a -o opus-<arch>
lipo -create opus-arm64 opus-x86_64 -output opus

Then copy Headers/, Modules/module.modulemap and Info.plist from the device framework, retarget the plist at iPhoneSimulator, and rebuild the xcframework with the device slice untouched:

xcodebuild -create-xcframework \
  -framework <device>/opus.framework \
  -framework <new-sim>/opus.framework \
  -output opus.xcframework

Result:

"LibraryIdentifier" => "ios-arm64_x86_64-simulator"
"LibraryIdentifier" => "ios-arm64"

Symbol check against the device slice: the only exports missing from the rebuilt simulator binary are _opusVersionNumber and _opusVersionString, which are Xcode's framework-versioning symbols rather than opus API. Every entry point the Dart FFI layer uses is present (opus_encoder_create, opus_decoder_create, opus_encode, opus_decode, opus_encoder_destroy, opus_get_version_string).

After this the app builds and launches on the simulator:

$ flutter build ios --simulator --debug
Xcode build done.
✓ Built build/ios/iphonesimulator/Runner.app

$ lipo -info build/ios/iphonesimulator/Runner.app/Frameworks/opus.framework/opus
Architectures in the fat file: ... are: x86_64 arm64

One caveat worth flagging: I built the simulator slice from opus 1.5.2, while the shipped device slice is 1.1.3 (per its Info.plist). The bitstream is frozen by RFC 6716 so this is compatible, but if you take this route it would be cleaner to rebuild all three slices from the same opus release so simulator and device runs exercise the same codec build.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions