The development host is Linux x86_64; the iOS toolchain (Xcode, xcrun,
codesign, iPhoneOS SDK) is macOS-only under Apple licence and cannot
be cross-compiled from Linux. This commit adds the instructions for
producing the iOS v0.2.0-beta.1 build on a macOS host plus a bash
helper that automates the build itself.
docs/release/ios-build.md (v0.1.0):
- Toolchain pin table (macOS 14+, Xcode 26+ per DEC-021, iOS SDK
26+, iOS deployment target 13.0 per DEC-003, Flutter 3.41.9,
Rust 1.95 stable with aarch64-apple-ios / aarch64-apple-ios-sim /
x86_64-apple-ios targets, CocoaPods 1.16+, FRB 2.12.0).
- macOS host options: owned hardware vs rental (MacStadium,
MacinCloud, Scaleway Apple silicon, AWS EC2 Mac) vs borrowed
Mac. Realistic cost ranges per option.
- Step-by-step Homebrew + Rust + Flutter + CocoaPods install.
- Pre-built libopus.a per arch via a CMake invocation that
targets the iOS SDK explicitly. Mirrors the Android build's
LIBOPUS_LIB_DIR wrap-dir trick.
- flutter create --platforms=ios to scaffold the ios/ folder
(the product Flutter app was created with only linux + android).
- Edits required to ios/Podfile and ios/Runner/Info.plist:
iOS 13 deployment target (DEC-003), NSMicrophoneUsageDescription
for the audio engine, UIBackgroundModes=audio for screen-locked
playback.
- Three cargo build --target invocations for device + both
simulator slices.
- lipo merge of the two simulator slices into one .a.
- xcodebuild -create-xcframework to produce
target/ChanoraBridge.xcframework with the right slices.
- flutter build ios --release --no-codesign or
flutter build ipa --release --export-method development for
a signed .ipa.
- Install paths: xcrun devicectl for wired install, altool for
TestFlight upload.
- Smoke-test instructions with the same hostname-resolution
caveat that affects the Android Beta (hickory-resolver does
not work on iOS; use the literal IP).
- Packaging into chanora-v0.2.0-beta.1-ios.ipa.
- Known-issue table covering: audiopus_sys cmake build failures
on iOS, microphone permission prompt prerequisites,
AVAudioSession category quirks for voice transmission, code-
signing failure modes, TestFlight rejection causes.
- Reproducibility note (build is not bit-reproducible).
tools/build-ios.sh:
- Parameter switches: --version, --no-codesign,
--regenerate-bindings, --export-method.
- Verifies xcodebuild, xcrun, cargo, rustc, flutter, pod, lipo
on PATH.
- Adds the three rustup iOS targets if missing.
- Verifies each pre-built libopus.a exists at the expected wrap
dir before starting.
- Optionally regenerates FRB bindings.
- Three cargo build runs (device + Apple-silicon sim + Intel
sim), each with LIBOPUS_LIB_DIR pointed at its arch's wrap dir
and the audiopus_sys build-cache wiped per target.
- lipo + xcodebuild -create-xcframework.
- flutter pub get + pod install + flutter build {ios,ipa}.
- Copies the .ipa to a versioned path under $HOME and prints
SHA-256.
This is documentation + helper only; no actual iOS binaries are
produced by this commit. The Linux development host cannot run
Xcode. To produce the binaries, follow §3-§14 of
docs/release/ios-build.md on a macOS host, or run
tools/build-ios.sh there.
DEC-011.1 iOS audio status remains Deferred; the doc notes that
cpal's iOS backend has not been empirically verified and the
AVAudioSession category likely needs configuration for voice
transmission. Both are Beta+ items.
23 KiB
Building Chanora v0.2.0-beta.1 on iOS via macOS host
Document type: Release / Platform Build Instructions
Version: 0.1.0
Status: Draft
Language: English
Product: Chanora
Repo path: docs/release/ios-build.md
1. Purpose
This document gives exact step-by-step instructions for building the
Chanora v0.2.0-beta.1 Internal Beta on an Apple macOS host. It
exists because the development host is Linux x86_64; the entire
iOS toolchain (Xcode, xcrun, codesign, xcodebuild, the
iPhoneOS SDK) is macOS-only under Apple licence and cannot be
cross-compiled from Linux. Even projects like xcbuild or
cctools-port cannot produce a signed .ipa that an iOS device's
installd will accept.
After following this doc you will have:
Runner.app— the iOS app bundle.libchanora_bridge.aand/or an XCFramework containing both device (arm64) and simulator (arm64+x86_64) slices of the Rust cdylib.- A
Runner.ipaarchive suitable for ad-hoc / TestFlight / App Store distribution depending on the provisioning profile you sign with.
The doc assumes you have:
- A macOS host (physical Mac or rental — see §3 for options).
- An Apple Developer account ($99/year). A free Apple ID can build for personal devices via Xcode automatic signing, but profiles expire in 7 days; the paid account is strongly recommended.
- Permission to enrol a test iPhone or iPad in the developer team.
- The Chanora source code (this repository), reachable from the Mac (git remote, scp, or AirDrop).
2. Toolchain versions pinned for this build
| Tool | Version | Why |
|---|---|---|
| macOS | 14 Sonoma or 15 Sequoia | Xcode 26 requires macOS 14+ at minimum |
| Xcode | 26 or newer (DEC-021) | Required to upload to App Store Connect on or after 2026-04-28. Beta builds for internal devices also pin to 26+ for parity. |
| iOS SDK | 26 or newer (DEC-021) | Bundled with Xcode 26+. |
| iOS deployment target | iOS 13 (DEC-003) | Matches Flutter 3.41.9's baseline support. The Podfile and Runner.xcodeproj must set this. |
| Command Line Tools | Latest stable (xcode-select --install or via Xcode) |
Provides xcrun, xcodebuild, codesign. |
| Flutter SDK | 3.41.9 stable | Same pin as Linux/Android/Windows builds. |
| Dart SDK | 3.11.5 | Bundled with Flutter. |
| Rust toolchain | stable 1.95 (or newer) | Workspace rust-version = "1.95". |
| Rust targets | aarch64-apple-ios, aarch64-apple-ios-sim, x86_64-apple-ios |
Device + Apple-silicon simulator + Intel-Mac simulator. |
cbindgen (optional) |
0.27+ | Only if you wire a C header for the bridge; FRB does not require it. |
cargo-lipo (legacy) |
not required | cargo-lipo was deprecated for multi-arch iOS in favor of cargo build --target <triple> + xcodebuild -create-xcframework. |
| CocoaPods | 1.16+ | Required by Flutter iOS builds (Podfile in apps/chanora_flutter/ios/). |
| Opus library | bundled via audiopus_sys build script with iOS-specific CMake toolchain |
Apple does not ship libopus; we cross-compile it per Rust target. |
| Apple Developer ID + certificate | one-time | Required to sign the .ipa. Free Apple ID OK for personal-device install (7-day profile). |
Why not Xcode 15 or 16? Apple's App Store Connect upload gate (DEC-021) requires the iOS 26 SDK on or after 2026-04-28. Even for internal Beta where you don't upload, we pin Xcode 26+ so the same archive can be re-signed for TestFlight without re-building.
3. macOS host options
3.1 Owned Apple hardware (cheapest long-term)
| Hardware | Min spec | Notes |
|---|---|---|
| Mac mini (M2, 2023+) | 16 GiB RAM, 256 GiB SSD | Best price/perf; ~USD $799 new |
| MacBook Air / Pro (M2+) | 16 GiB RAM | Portable; ~USD $1099+ new |
| Used Intel Mac (2018+) | 16 GiB RAM | Will need macOS 14+; check Apple's compatibility list |
Apple's licence forbids running macOS on non-Apple hardware ("Hackintoshes") for any commercial purpose. Don't.
3.2 macOS rental (no hardware purchase)
| Provider | Cost | Notes |
|---|---|---|
| MacStadium | USD ~$79+/month for an M-series Mac mini | Dedicated host; supports Xcode + signing |
| MacinCloud | USD ~$30+/month "Pay-As-You-Go" or "Dedicated Server" plans | Shared options exist; choose Dedicated for full Xcode access |
| Scaleway Apple silicon (EU) | EUR ~0.13/hour for Mac mini M2 | Hourly billing; cheap for short builds |
AWS EC2 Mac (mac1.metal, mac2.metal) |
USD ~$1.08/hour with a 24h minimum allocation | Most expensive; use only if you need AWS integration |
Apple's licence permits cloud hosts that comply with their terms. All of the above explicitly meet that requirement.
3.3 Borrowed Mac
Find a friend or coworker with a Mac that can run Xcode 26. Install your dev certificate to their Keychain locally for the build session only, then remove it. This is the cheapest option if you have access.
4. Install the toolchain (one-time per Mac)
All commands assume a normal user account; only the Xcode install needs an Administrator password.
4.1 Xcode + Command Line Tools
Either:
# Easiest: install Xcode from the Mac App Store.
# (Or download the .xip from https://developer.apple.com/download/all/)
open "https://apps.apple.com/app/xcode/id497799835"
# Then accept the licence and install Command Line Tools.
sudo xcodebuild -license accept
xcode-select --install # idempotent; skip if already done
# Verify.
xcodebuild -version # expect Xcode 26.x
xcrun --version
xcrun simctl list runtimes | head
If you already have Xcode 16 or 17 installed, add Xcode 26
side-by-side (don't replace) and switch via sudo xcode-select -s /Applications/Xcode-26.app.
4.2 Homebrew + Rust + Flutter + CocoaPods
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Rust (managed by rustup, not brew)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal
source "$HOME/.cargo/env"
# Add the three iOS targets.
rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios
# Flutter — pinned to 3.41.9 stable to match the Linux / Android
# builds.
brew install --cask flutter
flutter --version | head -3
flutter precache --ios
# CocoaPods.
brew install cocoapods
pod --version # expect 1.16 or newer
If your Mac is Apple-silicon and Homebrew installs to
/opt/homebrew, make sure that's onPATHfor new shells:eval "$(/opt/homebrew/bin/brew shellenv)".
4.3 Opus
audiopus_sys ships an Opus source tree and tries to build it via
CMake. On iOS the cmake-crate's generated build sometimes confuses
the iOS toolchain. We use the same pre-built libopus strategy that
the Android build uses:
# Get the audiopus_sys-bundled libopus source after a first cargo
# fetch (it gets unpacked into the registry).
cd ~/chanora
cargo fetch
OPUS_SRC=$(find ~/.cargo/registry/src -path '*audiopus_sys-*/opus' -type d | head -1)
mkdir -p /tmp/opus-ios
cp -R "$OPUS_SRC"/* /tmp/opus-ios/
# Helper: build libopus.a for one iOS arch.
build_opus_ios() {
local ARCH=$1 # arm64 | x86_64 | arm64-simulator
local SDK=$2 # iphoneos | iphonesimulator
local OUT=/tmp/opus-ios-${ARCH}
rm -rf /tmp/opus-ios/build && mkdir /tmp/opus-ios/build
cd /tmp/opus-ios/build
cmake .. \
-DCMAKE_SYSTEM_NAME=iOS \
-DCMAKE_OSX_SYSROOT=$(xcrun --sdk "$SDK" --show-sdk-path) \
-DCMAKE_OSX_ARCHITECTURES="${ARCH%-simulator}" \
-DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \
-DBUILD_SHARED_LIBS=OFF \
-DOPUS_BUILD_PROGRAMS=OFF \
-DOPUS_BUILD_TESTING=OFF \
-DCMAKE_INSTALL_PREFIX="$OUT" \
-G "Unix Makefiles"
make -j8 install
# Wrap so LIBOPUS_LIB_DIR appends /lib correctly.
mkdir -p "$OUT-wrap/lib"
cp "$OUT/lib/libopus.a" "$OUT-wrap/lib/"
cd /tmp/opus-ios
}
build_opus_ios arm64 iphoneos # device
build_opus_ios arm64-simulator iphonesimulator # Apple-silicon Mac sim
build_opus_ios x86_64 iphonesimulator # Intel Mac sim
You should end up with three wrap dirs each containing lib/libopus.a:
/tmp/opus-ios-arm64-wrap/lib/libopus.a
/tmp/opus-ios-arm64-simulator-wrap/lib/libopus.a
/tmp/opus-ios-x86_64-wrap/lib/libopus.a
4.4 Cargo target linker config
iOS targets need explicit linker config to find the SDK. Create
~/.cargo/config.toml:
[target.aarch64-apple-ios]
rustflags = ["-C", "link-arg=-fembed-bitcode"]
[target.aarch64-apple-ios-sim]
rustflags = ["-C", "link-arg=-fembed-bitcode"]
[target.x86_64-apple-ios]
rustflags = ["-C", "link-arg=-fembed-bitcode"]
Rust's built-in iOS targets already drive the right linker via
xcrun, so usually no further config is needed. If you hit a
"-platform_version not supported" error, ensure Xcode CLI tools are
fresh: sudo xcode-select --reset.
4.5 (Optional) flutter_rust_bridge_codegen
Only needed if you modify crates/chanora_bridge/src/api.rs on the
Mac. The bindings shipped in the repo are platform-agnostic Dart +
Rust source and do not need regenerating per platform.
cargo install flutter_rust_bridge_codegen --version "=2.12.0"
5. Get the source onto the Mac
Same two options as the Windows doc:
Option A — git remote
# On Linux dev host: push to a private remote.
cd /home/milkice/chanora
git remote add origin git@github.com:<your-user>/chanora.git
git push --all
git push --tags
# On the Mac:
cd ~
git clone https://github.com/<your-user>/chanora.git
cd chanora
git checkout v0.2.0-beta.1
Option B — zip / AirDrop
# On Linux:
git -C /home/milkice/chanora archive --format=zip \
--output=/tmp/chanora-v0.2.0-beta.1.zip v0.2.0-beta.1
# Transfer the zip to the Mac (AirDrop, scp, USB), then:
cd ~
unzip /Volumes/.../chanora-v0.2.0-beta.1.zip -d chanora
cd chanora
6. Add the iOS platform to the Flutter app
The product Flutter app was scaffolded with --platforms=linux,android,
so the ios/ folder does not yet exist. Add it once on the Mac:
cd ~/chanora/apps/chanora_flutter
flutter create --platforms=ios .
This creates apps/chanora_flutter/ios/ containing:
ios/
Runner/
AppDelegate.swift
Info.plist
Runner-Bridging-Header.h
Assets.xcassets/
Runner.xcodeproj/
Runner.xcworkspace/
Podfile
Flutter/
Important: Do not commit this
ios/folder back to the repo from the Mac unless you also do the corresponding edits the source-of-truth host uses (icons, bundle ID, signing config). For Beta it is fine to keep it Mac-only.
6.1 Set the iOS deployment target
Edit apps/chanora_flutter/ios/Podfile's first line to:
platform :ios, '13.0' # DEC-003
And in Xcode (open Runner.xcworkspace):
- Project → Runner → General → Minimum Deployments → iOS 13.0
- Project → Runner → Signing & Capabilities → set your Team
6.2 Add iOS-required entitlements + Info.plist keys
Chanora's audio engine needs the microphone. Edit
apps/chanora_flutter/ios/Runner/Info.plist to include:
<key>NSMicrophoneUsageDescription</key>
<string>Chanora uses the microphone for voice chat with TeamSpeak-compatible servers.</string>
<!-- Background-mode audio so playback continues when the screen
locks. Optional for Beta; required for a real release. -->
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<!-- Allow plain HTTP only for development. Remove before release.
The protocol layer itself uses UDP, not HTTP, but Flutter's hot
reload talks HTTP. -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
The bundle identifier should be set to a reverse-DNS string under
your developer team, e.g. app.chanora.chanora_flutter. Update it
in Xcode → Runner → General → Identity → Bundle Identifier.
7. Build the Rust libraries for iOS
We build three flavors and combine them into an XCFramework that Xcode can link against from both device and simulator slices.
cd ~/chanora
export LIBOPUS_STATIC=1
export LIBOPUS_NO_PKG=1
build_bridge_for_ios() {
local TARGET=$1 # aarch64-apple-ios | aarch64-apple-ios-sim | x86_64-apple-ios
local OPUS_DIR=$2 # /tmp/opus-ios-*-wrap
export LIBOPUS_LIB_DIR="$OPUS_DIR"
# Wipe audiopus_sys build cache so LIBOPUS_LIB_DIR is re-read.
rm -rf target/$TARGET/release/build/audiopus_sys-*
cargo build --release --target "$TARGET" -p chanora_bridge
}
build_bridge_for_ios aarch64-apple-ios /tmp/opus-ios-arm64-wrap
build_bridge_for_ios aarch64-apple-ios-sim /tmp/opus-ios-arm64-simulator-wrap
build_bridge_for_ios x86_64-apple-ios /tmp/opus-ios-x86_64-wrap
Expected static-lib outputs (the [lib] section in
crates/chanora_bridge/Cargo.toml already declares staticlib):
target/aarch64-apple-ios/release/libchanora_bridge.a (~25 MiB, device)
target/aarch64-apple-ios-sim/release/libchanora_bridge.a (~25 MiB, M-series simulator)
target/x86_64-apple-ios/release/libchanora_bridge.a (~25 MiB, Intel simulator)
7.1 Build the simulator universal binary with lipo
The simulator side needs both arches in one .a for Xcode to pick
the host architecture automatically:
mkdir -p target/universal-ios-sim
lipo -create \
target/aarch64-apple-ios-sim/release/libchanora_bridge.a \
target/x86_64-apple-ios/release/libchanora_bridge.a \
-output target/universal-ios-sim/libchanora_bridge.a
file target/universal-ios-sim/libchanora_bridge.a
7.2 Build the XCFramework
rm -rf target/ChanoraBridge.xcframework
xcodebuild -create-xcframework \
-library target/aarch64-apple-ios/release/libchanora_bridge.a \
-library target/universal-ios-sim/libchanora_bridge.a \
-output target/ChanoraBridge.xcframework
Result:
target/ChanoraBridge.xcframework/
Info.plist
ios-arm64/libchanora_bridge.a
ios-arm64_x86_64-simulator/libchanora_bridge.a
7.3 Link the XCFramework from the Flutter iOS app
Open apps/chanora_flutter/ios/Runner.xcworkspace in Xcode.
- Drag
target/ChanoraBridge.xcframeworkinto the Runner project (Embed: "Do Not Embed" — it's a static lib). - Project → Runner → Build Phases → Link Binary with Libraries →
ensure
ChanoraBridge.xcframeworkis listed. - The FRB Dart loader expects to find the symbols in the main app
binary on iOS (static linkage). The generated
apps/chanora_flutter/lib/src/rust/frb_generated.io.dartshould look up viaDynamicLibrary.process()on iOS — this is the default for FRB v2 when the Rust crate type isstaticlib.
If the dart-side loader expects a separate dylib, regenerate bindings with the iOS target hint:
flutter_rust_bridge_codegen generate
(The repo's bindings already target this style; no change usually needed.)
8. Regenerate the FRB bindings (optional)
Skip unless you've modified crates/chanora_bridge/src/api.rs on
the Mac. The bindings are platform-agnostic Dart + Rust source.
cd ~/chanora
flutter_rust_bridge_codegen generate
Takes ~5–10 minutes on first run (cargo expand).
9. Build the iOS app via Flutter
cd ~/chanora/apps/chanora_flutter
flutter pub get
cd ios && pod install && cd ..
flutter build ios --release --no-codesign
flutter build ios --no-codesign produces an unsigned Runner.app.
For signed .ipa for TestFlight or device install, use:
flutter build ipa --release --export-method development
# or: --export-method ad-hoc | enterprise | app-store
This requires:
- A configured Team and Bundle Identifier (set in Xcode in §6.1).
- A matching provisioning profile in your Keychain (Xcode's Automatic Signing usually handles this if you've signed into your Apple ID in Xcode → Settings → Accounts).
Expected output locations:
| Variant | Path |
|---|---|
| Unsigned app bundle | apps/chanora_flutter/build/ios/iphoneos/Runner.app/ |
Signed .ipa |
apps/chanora_flutter/build/ios/ipa/chanora_flutter.ipa |
10. Install on a physical iPhone/iPad
10.1 Wired install (USB or USB-C)
# List paired devices.
xcrun devicectl list devices
# Install the ipa.
xcrun devicectl device install app --device <UDID> \
apps/chanora_flutter/build/ios/ipa/chanora_flutter.ipa
# Launch it.
xcrun devicectl device process launch \
--device <UDID> --terminate-existing \
app.chanora.chanora_flutter
10.2 TestFlight (wireless to many testers)
xcrun altool --upload-app \
--type ios \
--file apps/chanora_flutter/build/ios/ipa/chanora_flutter.ipa \
--username "<your-apple-id>" \
--password "<app-specific-password>"
Then in App Store Connect:
- Wait for processing (10–30 minutes).
- Add the build to a TestFlight test group.
- Invite testers by Apple ID email.
Testers install via the TestFlight app on their device.
11. Smoke test on the iPhone/iPad
Open the installed Chanora app:
- Banner: "Beta build — voice in/out wired; not production ready."
- Form: pre-populated with
cn.teamspeak.app+ChanoraBeta. - Connect.
Known limitation (same as Android): the protocol layer's
hickory-resolvercannot resolve hostnames on iOS by default. If Connect times out, enter the literal IP. Forcn.teamspeak.appthat is currently175.178.125.23. A proper fix wires the iOS system resolver into hickory; Beta+ work.
- Once connected, the channel tree appears.
- Start audio → grant microphone permission when prompted.
- Hold to talk → frame counter increments under PTT.
If audio fails to start, check:
- Microphone permission granted in Settings → Chanora → Microphone.
- Device not in silent mode (output side only).
- iOS Audio Session category — by default Flutter uses
AVAudioSessionCategoryPlayback. Voice transmission needsPlayAndRecordwithvoiceChatmode. Beta+ work.
12. Package the release artefact
flutter build ipa already produces a single .ipa. Stamp it with
the build identifier:
cd ~/chanora/apps/chanora_flutter
cp build/ios/ipa/chanora_flutter.ipa \
"$HOME/chanora-v0.2.0-beta.1-ios.ipa"
shasum -a 256 "$HOME/chanora-v0.2.0-beta.1-ios.ipa"
Typical size: 60–90 MiB (includes Flutter engine + Dart AOT + Rust static lib + bundled libopus + iOS resources).
13. Download to your local host
If on a rental Mac:
# From the Mac, copy back to Linux dev host.
scp "$HOME/chanora-v0.2.0-beta.1-ios.ipa" \
user@your-linux-host:/home/milkice/
Or pull from the rental's web file manager. Keep the .ipa
re-installable indefinitely if the provisioning profile is
ad-hoc/enterprise; for development profiles it expires in 7 days.
14. Final artefact paths (what you actually produce)
On the Mac, after a successful build:
| Artefact | Path |
|---|---|
| Rust device static lib | ~/chanora/target/aarch64-apple-ios/release/libchanora_bridge.a |
| Rust Apple-silicon sim lib | ~/chanora/target/aarch64-apple-ios-sim/release/libchanora_bridge.a |
| Rust Intel sim lib | ~/chanora/target/x86_64-apple-ios/release/libchanora_bridge.a |
| Lipo'd simulator lib | ~/chanora/target/universal-ios-sim/libchanora_bridge.a |
| XCFramework | ~/chanora/target/ChanoraBridge.xcframework/ |
| Unsigned app bundle | ~/chanora/apps/chanora_flutter/build/ios/iphoneos/Runner.app/ |
Signed .ipa |
~/chanora/apps/chanora_flutter/build/ios/ipa/chanora_flutter.ipa |
Renamed release .ipa |
~/chanora-v0.2.0-beta.1-ios.ipa |
15. After the build — deallocate (rental hosts)
| Provider | Deallocate command |
|---|---|
| MacStadium | Suspend / shut down via control panel |
| MacinCloud | "Stop" the session in account dashboard |
| Scaleway | scw instance server stop <id> |
| AWS EC2 Mac | Stop the host; release the dedicated host if at end of 24h minimum |
Always verify the billing dashboard shows the instance as stopped, not just shut down at the OS level.
16. Known issues and caveats
| Issue | Workaround |
|---|---|
audiopus_sys cmake-build fails with "no member named '__int128_t'" |
Re-export LIBOPUS_LIB_DIR to a pre-built .a directory (§4.3); never let cargo's cmake-build attempt to compile Opus on iOS. |
hickory-resolver cannot resolve hostnames on iOS |
Use literal IP. Fix: wire dns-via-system resolver into chanora_protocol. Beta+ work. |
| Microphone permission prompt never appears | The NSMicrophoneUsageDescription is missing from Info.plist. See §6.2. |
| Audio plays but no transmission | iOS AVAudioSession category likely set to Playback not PlayAndRecord. Configure the session in Swift before Flutter loads, or via a small plugin. Beta+ work. |
| Code signing fails with "No signing certificate" | Open Xcode → Settings → Accounts → add your Apple ID, then close and reopen Runner.xcworkspace to refresh. |
flutter build ipa complains about missing entitlements |
Make sure the provisioning profile in Apple Developer console includes the App ID with microphone capability enabled. |
| TestFlight upload rejected: "Invalid bundle. Apple silicon support" | Build with flutter build ipa --release (not --debug); debug AOT is JIT-only and not allowed in TestFlight. |
App crashes on launch with dyld: Library not loaded: libchanora_bridge.dylib |
The bridge is built as staticlib, not cdylib, on iOS — FRB's loader uses DynamicLibrary.process(). If you accidentally built a cdylib, the loader path won't match. Stick to staticlib. |
First flutter build ios takes > 30 minutes |
Normal — first Pod install + CocoaPods cache. |
17. Reproducibility note
Like the Windows build, the iOS build is not bit-reproducible:
xcodebuildembeds a creation timestamp in the.ipa.audiopus_syspicks the cmake-discovered Xcode toolchain version.- Dart AOT compilation embeds a Dart kernel hash.
- Code-signing inherently makes every signed
.ipaunique.
If true reproducibility is required for a future External Beta or MVP Public release, that is Beta+ work; the build above is sufficient for an Internal Beta milestone per DEC-001.
18. Change History
| Version | Date | Description |
|---|---|---|
| 0.1.0 | 2026-05-14 | Initial iOS build instructions for Chanora v0.2.0-beta.1. Targets a macOS 14+ host running Xcode 26+ (DEC-021), Rust 1.95 stable with aarch64-apple-ios + aarch64-apple-ios-sim + x86_64-apple-ios targets, Flutter 3.41.9 stable, flutter_rust_bridge 2.12.0. Produces Runner.app + chanora_flutter.ipa via flutter build ipa --release. iOS deployment target pinned to 13.0 (DEC-003). Surface caveat: DEC-011.1 iOS audio remains Deferred — cpal's iOS backend has not been empirically verified, and AVAudioSession category configuration is required for voice transmission. |