.NET MAUI(.NET 9)iOS Release/IPAでZebraRFID SDKがリンクエラーになる原因と対処法(MtouchLink=SdkOnly)

Visual Studio 2022 から .NET MAUI(net9.0-ios)で ZebraRFID iOS SDK を組み込んだアプリを Release(IPA)で配布しようとすると、MtouchLink=SdkOnly を境にリンクエラーや TestFlight でのクラッシュが発生することがあります。ここでは、エラーログから原因を特定する考え方と、実務で効く回避策・切り分け手順をまとめます。

目次

Release(IPA)で MtouchLink=SdkOnly にするとリンクエラーになる症状

今回の現象は「C# のビルド」ではなく、IPA 作成の最終工程である ネイティブリンク(clang++)で止まるのが特徴です。現場でよく見る条件は次の通りです。

  • プロジェクト:.NET MAUI(TargetFramework は net9.0-ios
  • ビルド:Visual Studio 2022 から Release で IPA(Archive)生成
  • 環境:Xcode 16.2、実機向け arm64
  • SDK:ZebraRFID iOS SDK(ZebraRfidIntegratedSdk.dll
  • トリガー:<MtouchLink>SdkOnly</MtouchLink> にした時だけ失敗

ビルドログの典型例(抜粋)は以下です。

clang++ exited with code 1
Undefined symbols for architecture arm64:
  "_EAAccessoryDidConnectNotification", referenced from:
      ...
  "_EAAccessoryDidDisconnectNotification", referenced from:
      ...
  "_EAAccessoryKey", referenced from:
      ...
  "_OBJC_CLASS_$_EAAccessoryManager", referenced from:
      ...
  "_OBJC_CLASS_$_EASession", referenced from:
      ...
ld: symbol(s) not found for architecture arm64

一方で、リンカー設定を Don’t link(リンクしない)Link all(すべてリンク) にするとビルド自体は成功する、という挙動もポイントです。さらに、ビルドが通っても TestFlight 配布後に EXC_BAD_ACCESS / SIGABRT / KERN_INVALID_ADDRESS でクラッシュする、という追加症状が出るケースもあります。

まず押さえる:MtouchLink は「managed トリミング」だが、ネイティブリンクにも波及する

MtouchLink は iOS ビルドにおける「アセンブリのトリミング(不要コード削除)」の強さを決める設定です。ところが、バインディング SDK(.dll + .a)を使うと、SDK 側のメタデータ(必要フレームワークの宣言、リンカーフラグ、force_load 指定など)がビルドの途中で拾われたり拾われなかったりして、結果として clang++ のリンク引数が変わることがあります。SdkOnly でだけ壊れるのは、この“境界部分”が弱い SDK を踏んだ時に起きやすいパターンです。

設定何が起きるか向いている場面注意点
Don’t linkトリミングしないまず動かして切り分けたい/外部 SDK が多いIPA サイズ増・起動時ロード増になりがち
SdkOnlySDK アセンブリのみトリミング自作コードは安全に残しつつ最適化したいSDK/バインディングの依存宣言が不完全だと「必要なリンク情報が落ちる」ことがある
Link allすべてトリミングサイズ最小化を最優先したい反射やネイティブからのコールバックでクラッシュしやすい

結論として、外部ネイティブ SDK を含む iOS アプリは Don’t link → SdkOnly →(必要なら)Link all の順で段階的に攻めるのが、最短で安定点に到達しやすいです。

リンクエラーの本質:ExternalAccessory.framework が不足している

未解決になっているシンボルは EAAccessory / EASession など、Apple の ExternalAccessory フレームワーク(MFi アクセサリ)に属するものです。つまり、libintegratedsdk.a が ExternalAccessory を参照しているのに、最終リンクに ExternalAccessory.framework が入っていない状態です。

未解決シンボル(例)該当フレームワーク意味まず疑うべきこと
EAAccessoryManager / EASessionExternalAccessory.frameworkMFi アクセサリの管理・セッションExternalAccessory をリンクしていない/SDK が宣言していない
EAAccessoryDidConnectNotification などExternalAccessory.framework接続/切断の通知定数同上

ここから導ける整理はシンプルです。原因は MAUI というより「ZebraRFID 側のネイティブライブラリ/バインディングの依存宣言不足」である可能性が高い、ということです(特に、同じ SDK のデモでも再現するならなおさらです)。

まずはビルドを通す:ExternalAccessory を明示的に追加する

本来は SDK が自動的に ExternalAccessory.framework を要求すべきですが、現場では「アプリ側で明示」して先に前進するのが現実的です。以下は、再現しやすい順に並べた対処パターンです。

ビルドログを“見える化”してから作業する

リンク引数が変わっているかどうかを確認できないと、対処が当たりか外れか判断できません。Visual Studio 側で ビルド出力を詳細(Diagnostic) にして、clang++ のコマンドラインに何が渡っているか追える状態にします。

  • Visual Studio のオプションで「ビルド出力の詳細度」を上げる
  • iOS Release のビルドログから clang++ 行を探し、-framework ExternalAccessory があるか確認する

csproj で -framework ExternalAccessory を追加する(最も手軽)

まず試すならこの方法が速いです。iOS ターゲットだけに適用するため、TargetFramework 条件を付けます。

&lt;PropertyGroup Condition=&quot;'$(TargetFramework)' == 'net9.0-ios'&quot;&gt;
  &lt;MtouchLink&gt;SdkOnly&lt;/MtouchLink&gt;
  &lt;MtouchExtraArgs&gt;--framework ExternalAccessory&lt;/MtouchExtraArgs&gt;
&lt;/PropertyGroup&gt;

これで clang++ に ExternalAccessory が渡るようになれば、Undefined symbols は解消するのが通常です。解消しない場合は「引数が適用されていない」か「別のフレームワーク/ライブラリも不足している」可能性があるため、まずは clang++ の引数を確認してください。

NativeReference で依存を宣言する(ライブラリがプロジェクト内にある場合)

ネイティブの .a をプロジェクト内に持っている/参照できるなら、依存関係は NativeReference に寄せる方が保守性が上がります。

&lt;ItemGroup Condition=&quot;'$(TargetFramework)' == 'net9.0-ios'&quot;&gt;
  &lt;NativeReference Include=&quot;Platforms/iOS/libintegratedsdk.a&quot;
                   Kind=&quot;Static&quot;
                   ForceLoad=&quot;True&quot;
                   Frameworks=&quot;ExternalAccessory&quot; /&gt;
&lt;/ItemGroup&gt;

ForceLoad は静的リンク時の“取り込まれない事故”を避けるための保険です。SDK によっては不要ですが、アクセサリ系 SDK は初期化コードをカテゴリや自動実行ブロックに置くことがあるため、まずは付けて検証すると切り分けが速くなります。

それでもダメな場合に試す「リンカーフラグ」

Undefined symbols が ExternalAccessory 以外にも広がる場合や、ビルドは通るが実行時に SDK の初期化が動かない場合は、静的ライブラリ特有の取り込み問題を疑います。iOS では次のフラグが効くことがあります。

  • -ObjC:Objective-C のカテゴリ/拡張が落ちる問題の回避
  • -all_load / -force_load:静的ライブラリのオブジェクトを強制的に取り込む

ただし、これらは副作用(重複シンボルなど)もあるため、むやみに常用せず、まずは「どのフラグが必要か」を特定する使い方に留めるのが安全です。

SDK(ネイティブライブラリ/バインディング)側を疑うべきチェックポイント

ここまでで「ExternalAccessory を足せばビルドが通る」なら、SDK の依存宣言不足がほぼ確定です。通らない/別の不具合が出る場合は、次の観点で SDK の構成そのものを疑います。

観点なぜ重要か確認例
arm64 を含むか実機配布では arm64 が必須Mac のターミナルで lipo -info libintegratedsdk.a を実行
必要フレームワークが宣言されているか宣言されないと MAUI 側が自動でリンクできないバインディングの LinkWith/NativeReference に Frameworks 指定があるか
Xcode 16.2 / .NET 9 のサポート有無ツールチェーン更新でビルド/ランタイムが変わるベンダーのサポート表・リリースノートに明記されているか

この段階で「Zebra 提供のデモでも同様」「dll を差し替えても再現」という状況なら、ベンダーへの問い合わせが最短です。アプリ側で無理に“場当たり的なフラグ”を増やすより、SDK の更新版や正しいバインディング手順をもらった方が、将来の保守コストを大きく下げられます。

TestFlight 配布後のクラッシュを切り分ける

TestFlight でのみ落ちる問題は、ビルドエラーよりも原因が分散しやすいです。まずは「Release の何が Debug と違うのか」を押さえておくと、切り分けの精度が上がります。

Debug と違う点起きがちな症状対処の方向性
AOT/最適化が強いネイティブのメモリ破壊が顕在化(EXC_BAD_ACCESS 等)クラッシュログをシンボリケートし、SDK 内で落ちているならベンダーへ
トリミングの影響反射・イベント購読・コールバック登録が欠けて SIGABRT/例外Link all を避ける、保持設定を追加する
権限/Info.plist の差分が露呈特定 API 到達時に SIGABRTBluetooth/外部アクセサリ/ネットワーク等の必要キーを確認

最優先は「クラッシュログのシンボリケート」

EXC_BAD_ACCESS や KERN_INVALID_ADDRESS は、ログがシンボリケートされていないと“勘”でしか進められません。TestFlight のクラッシュログは、dSYM を揃えることで関数名まで追えるようになります。

  • IPA/Archive 生成時の dSYM を保管する
  • Xcode Organizer(または配布基盤)でクラッシュログを symbolicate する
  • スタックトレース上位が libintegratedsdk.a / Zebra の関数 なら、アプリ側の設定だけでの解決は難しい

Link all でだけ落ちる場合の“現実解”

サイズを削りたい気持ちは分かりますが、外部 SDK が反射や内部コールバックに依存していると、Link all はクラッシュの引き金になりがちです。現実解は次のどれかです。

  • 配布は SdkOnly で止める(安定優先)
  • Link all を使うなら、SDK が要求する保持設定(Preserve/linker 設定)をベンダーに要求する
  • どうしても自前で保持する場合は、SDK が反射で触る型/メソッドを特定して “残す”

保持する対象が分からないまま闇雲に残すと、最適化の意味が薄れます。ベンダーが「MAUI/.NET iOS 向けの保持設定(linker 設定)」を持っているか、まずは確認するのが安全です。

現場で使える切り分けチェックリスト

最後に、作業の抜け漏れを減らすためのチェックリストを置いておきます。順に潰すだけで「アプリ設定か、SDK か」がかなり早く見えます。

チェック見る場所OK の目安
clang++ の引数に ExternalAccessory が入っているRelease ビルドログ-framework ExternalAccessory が見える
ビルド成功パターンを固定できているDon’t link / SdkOnly / Link allどの設定で通り、どれで落ちるか説明できる
Info.plist の外部アクセサリ設定Platforms/iOS/Info.plistUISupportedExternalAccessoryProtocols が SDK 指定通り
TestFlight のクラッシュログがシンボリケートされているApp Store Connect / Xcode Organizer関数名が追える(objc_msgSend だけではない)
クラッシュ箇所が SDK 内か app 内か判定できているシンボリケート済みログlibintegratedsdk.a の関数名が出ているか、managed 例外か判別できる

ベンダー問い合わせを通すための情報セット

SDK 側の問題が濃厚なら、問い合わせの質がそのまま解決速度になります。次の情報を 1 セットにして送ると、往復回数を減らせます。

  • .NET MAUI / .NET 9 / TargetFramework(net9.0-ios)
  • Visual Studio 2022、Xcode 16.2、iOS バージョン、端末機種
  • ZebraRfidIntegratedSdk.dll とネイティブライブラリのバージョン
  • SdkOnly のときのリンクエラーログ全文(Undefined symbols の部分)
  • csproj 側で ExternalAccessory を明示リンクしたかどうか
  • TestFlight のクラッシュログ(可能ならシンボリケート済み)
  • 最小再現手順(デモでも再現するならその手順)

伝えるべき要点は「ExternalAccessory 依存があるのに、SDK から必要フレームワーク指定が伝播していない可能性がある」「.NET 9 + Xcode 16.2 の組み合わせで再現する」です。ここまで整理して渡すと、ベンダー側も調査ポイントを外しにくくなります。

まとめ

.NET MAUI(.NET 9)で ZebraRFID iOS SDK を組み込んだ際、Release(IPA)で MtouchLink=SdkOnly にすると Undefined symbols(EAAccessory 系)が出る場合は、ExternalAccessory.framework のリンク不足が第一容疑です。まずは csproj で ExternalAccessory を明示してビルドを通し、TestFlight のクラッシュはログをシンボリケートして “SDK 内で落ちているか” を確定させるのが、最短での解決につながります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次