TestFlightに配布したiOSアプリが「起動直後にクラッシュ」しているのに、App Store Connectから取得したクラッシュログがメモリアドレスばかりで読めない――この症状の多くは、クラッシュしたビルドと一致するdSYMがXcode側で見つからない(または一致していない)ことが原因です。.NET MAUI / Visual Studio(Windows)でも、押さえるポイントは同じです。
この問題の正体:クラッシュログが「アドレスのまま」になる条件
TestFlight(App Store Connect)から落とした crashlog.crash を Xcode の Devices and Simulators などで開いたのに、スタックトレースが 0x0000000100xxxxxx のようなアドレス表示のまま――これは「Xcodeがシンボル情報(dSYM)を当てられていない」状態です。
よくある誤解として、次の2つが混ざりがちです。
- クラッシュ原因の解析(なぜ落ちたのかを直す)
- シンボリケート(シンボル化)(アドレスを関数名に戻して「読める状態」にする)
まずは後者を成功させないと、前者の調査効率が極端に落ちます。
| 用語 | 意味 | 今回の論点 |
|---|---|---|
dSYM(MyAppName.app.dSYM) | デバッグシンボル(関数名・行情報など)を保持するファイル群 | クラッシュしたビルドと一致するdSYMが必須 |
| シンボリケート | アドレスを関数名(可能なら行番号)に解決すること | XcodeがdSYMを見つけられると自動で進むことが多い |
| UUID(Build UUID) | ビルドごとに付く識別子 | クラッシュログのUUIDとdSYMのUUIDが一致しないと失敗 |
| .xcarchive | Xcodeのアーカイブ(ビルド成果物の箱) | dSYMsフォルダからdSYMを取り出せる |
結論:シンボリケートはXcode側で行い、MAUI側は「dSYMが残る設定」と「一致する成果物の保管」が要
質問で挙がっている「WindowsのVisual Studioで何か必要?」「MacのXcode側?」「dSYMはどう使う?」に対する答えを先にまとめると、実務ではこう考えるのが最短です。
| やる場所 | やること | 目的 |
|---|---|---|
| Visual Studio / .NET MAUI(ビルド側) | dSYMが生成され、不要に消えない設定にする/アーカイブを作る | 「該当ビルドと一致するdSYM」を確実に残す |
| Xcode(解析側) | クラッシュログにdSYMを当ててシンボリケートする | スタックトレースを関数名に戻して読めるようにする |
最初に確認:クラッシュログの「Version / Build」と、手元の成果物が一致しているか
シンボリケートができない原因で一番多いのは、設定の問題よりもシンプルに「別ビルドのdSYMを当てようとしている」ケースです。TestFlightの運用では、同日に何回もアップロードしていることが珍しくないため、特に起こりがちです。
- App Store Connect(TestFlight)側で、該当クラッシュのビルド番号(例:1.2.3 (45))を確認する
- クラッシュログ(.crash)でも、ヘッダ付近や末尾のBinary Imagesセクションで対象バイナリを確認する
- 手元の
MyAppName.app.dSYMが、そのビルドのものかを後述のUUIDで確認する
.NET MAUI側でやること:シンボルが落ちない(調査に必要な情報が残る)ビルドに寄せる
NoSymbolStrip を設定して、ビルド時のシンボル削除を抑止する
.NET(iOS)では、MSBuildプロパティ NoSymbolStrip で「ビルド時にデバッグシンボルを削らない」指定ができます。クラッシュ調査の初動を速くしたい場合、TestFlight向け(少なくとも社内/検証用)では有効にしておくと、後から困りにくいです。
プロジェクト全体に影響させたくない場合は、iOSのReleaseだけに限定するのが安全です。
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0-ios' And '$(Configuration)' == 'Release'">
<NoSymbolStrip>true</NoSymbolStrip>
</PropertyGroup>
マルチターゲット(net8.0-android など)構成で条件を書きにくいときは、TargetFrameworkに -ios が含まれるかで分岐しても構いません。
<PropertyGroup Condition="$([System.String]::Copy('$(TargetFramework)').Contains('-ios')) And '$(Configuration)' == 'Release'">
<NoSymbolStrip>true</NoSymbolStrip>
</PropertyGroup>
注意点として、NoSymbolStrip=true はアプリサイズ増の要因になります。最終的に本番配信でサイズを最適化したい場合は、「本番はstripするが、dSYMは必ず保管する」運用に切り替えるのが一般的です(後述)。
「dSYMが生成されない」系の設定も念のため把握しておく
シンボリケートに必要なのは基本的にdSYMなので、「そもそもdSYM生成が無効になっていないか」も確認ポイントです。.NET(iOS)には NoDSymUtil というプロパティがあり、これが有効だとdSYM生成が抑止されます。デフォルト挙動も条件によって変わるため、特殊なビルド設定を入れている場合は要注意です。
ArchiveOnBuild=true を使って「.xcarchive」を確実に残す
調査で一番強いのは、該当ビルドの .xcarchive を残すことです。なぜなら、アーカイブの中に dSYMs がまとまっており、後から「このビルドのdSYMどれ?」で迷いにくいからです。
Microsoftの手順でも、iOS配布用の dotnet publish で -p:ArchiveOnBuild=true を指定する例が紹介されています。Mac上で実行する場合だけでなく、Windowsからリモートビルド(Pair to Mac相当)する場合も同様のパラメータを渡せます。
dotnet publish -f net8.0-ios -c Release -p:ArchiveOnBuild=true -p:RuntimeIdentifier=ios-arm64
Visual StudioのGUI操作でIPAを作っている場合でも、裏側ではMac側にアーカイブ相当が残ることがあります。大事なのは「IPAだけを成果物として扱わず、アーカイブ(またはdSYM一式)をビルド番号付きで保管する」運用にすることです。
| 成果物 | 用途 | 紛失すると困る度 | おすすめの保管単位 |
|---|---|---|---|
| .ipa | 配布(TestFlight/AdHocなど) | 中 | ビルド番号つきで保管(再配布用) |
| .xcarchive | 調査(dSYMの供給源) | 最重要 | ビルドごとに丸ごと保管(推奨) |
| MyAppName.app.dSYM | シンボリケート | 最重要 | 最低限これだけはZIPで保管 |
dSYMの入手:XcodeのArchive(.xcarchive)から取り出す手順
すでに MyAppName.app.dSYM を入手できている場合でも、今後のために「どこにあるのか」を押さえておくと安心です。
- MacでXcodeを起動
- Window → Organizer を開く
- Archives を選択し、該当アプリの該当ビルド(Version/Build)を選ぶ
- Show in Finder(Finderで表示) などでアーカイブの場所へ移動
xxxx.xcarchiveを右クリック → パッケージの内容を表示dSYMs配下にMyAppName.app.dSYMがある
重要なのは、クラッシュを起こしたビルドと同一のアーカイブ由来のdSYMを使うことです。ビルドが1つ違うだけで、シンボリケートは失敗します。
シンボリケートの前にやるべき最重要チェック:UUIDが一致しているか
「Xcodeで開いてもアドレスのまま」のとき、経験上ほぼこれです。
- クラッシュログが要求しているUUID
- dSYMが持っているUUID
この2つが一致しない限り、どれだけ手順をやっても“それっぽく”シンボル化されません。
dSYM側のUUIDを確認する(dwarfdump)
Macのターミナルで、dSYMのUUIDを確認します。Crashlyticsのドキュメントでも、dSYMのUUID確認に dwarfdump を使う例が案内されています。
dwarfdump --uuid MyAppName.app.dSYM
出力例(イメージ):
UUID: 01234567-89AB-CDEF-0123-456789ABCDEF (arm64) MyAppName.app.dSYM
クラッシュログ側のUUIDを確認する(Binary Images)
.crashファイルの末尾付近にある Binary Images セクションで、MyAppName(またはBundle名)に該当する行を探します。そこにUUIDが載っています(表示形式はログの種類やXcodeのバージョンで多少異なります)。
| 見るもの | どこを見る | 一致させるもの |
|---|---|---|
| クラッシュログ | Binary Imagesの MyAppName 行 | UUID |
| dSYM | dwarfdump --uuid の出力 | UUID |
一致していれば、Xcodeが正しく参照できる状態に置くことでシンボリケートできる可能性が一気に上がります。逆に一致していないなら、今持っているdSYMはそのクラッシュに使えません。この場合は「該当ビルドのアーカイブから取り直す」「App Store Connect/Xcodeから該当ビルドのdSYMを入手する」へ進みます。
シンボリケートの実行:Xcodeで自動、ダメなら手動
方法A:Xcode Organizerの「Crashes」で見る(自動シンボリケート)
XcodeはOrganizer経由でクラッシュを閲覧すると、自動でシンボリケートされることが多いです。特に「該当ビルドのアーカイブがMac内に残っている」場合、ここが最短ルートになりやすいです。
- Xcode → Window → Organizer
- Crashes(またはそれに相当するクラッシュ表示)を開く
- 対象アプリを選択
- 対象のクラッシュを開き、関数名が出るか確認
ここでシンボル化されない場合は、ほぼ「一致するdSYMが見つからない」か「見つけられる場所にない」です。
方法B:クラッシュログ(.crash)をXcodeで開き、dSYMを“見つけられる場所”に置く
クラッシュログをXcodeで開いたとき、Xcodeが参照できる場所にdSYMが存在し、かつUUIDが一致していれば、勝手にメソッド名が出ることがあります。ポイントは「Xcodeが普段見る場所(Archiveや既知のdSYMキャッシュ)に置く」です。
- 該当ビルドの
.xcarchiveを Xcode Organizer の Archives に残す(最優先) - 取り出した
MyAppName.app.dSYMを、アーカイブのdSYMs配下に戻しておく(構造を崩さない)
最近のXcodeでは「dSYMが足りない」ことを警告として出すこともあり、逆に言えば“足りていない”ことが判定しやすくなっています。
方法C:ターミナルで手動シンボリケート(最後の手段として覚えておく)
自動でうまくいかないときでも、UUIDが合っているなら手動で前に進めます。代表的なのは symbolicatecrash を使うやり方です(Xcodeの内部に含まれるスクリプトを利用します)。
環境によってパスが変わるため、まずは見つけます。
xcrun --find symbolicatecrash
見つかったパスを使って実行します(例)。
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
SYMBOLICATE=$(xcrun --find symbolicatecrash)
# 例:同じフォルダに crashlog.crash と MyAppName.app.dSYM を置いて実行
"$SYMBOLICATE" crashlog.crash > symbolicated.crash
この方法は、Xcodeのバージョン差や環境変数で躓きやすいので、まずは「Archives/Crashesで自動」を優先し、どうしても必要なときだけ使うのがおすすめです。
App Store ConnectからdSYMを取るべきケースと、取れないときの代替
運用次第では「ローカルのアーカイブが残っていない」「ビルドマシンが入れ替わって古いdSYMが消えた」ということが起こります。その場合、App Store ConnectやXcode経由でdSYMを取得できるかが鍵になります。
| 状況 | 第一候補 | ダメなとき |
|---|---|---|
| ビルドマシンに該当アーカイブが残っている | Xcode Organizer → Archives → .xcarchive から取り出す | アーカイブ保管運用に切り替える |
| ローカルに何も残っていない | App Store Connectのビルド詳細からdSYMをダウンロード(表示される場合) | Xcode Organizerの「Download dSYMs」相当の導線を試す |
| dSYMダウンロード導線が見当たらない | (環境/時期/ビルド形式によって見え方が変わる) | ローカルにアーカイブを残す運用が結局確実 |
App Store Connectで「Build Metadata → Download dSYM」のような導線が案内されている例もありますが、時期やBitcode事情などで画面や提供範囲が変わることがあります。見えない場合は、Xcode Organizer側で取得できるかを当たる、または最初からアーカイブを保管する運用に寄せるのが現実的です。
「MoveNext」「AsyncMethodBuilderCore_Start」など見慣れない名前が出ても慌てない
シンボリケートが成功すると、...MoveNext のような見慣れないメソッド名が出ることがあります。これは多くの場合、C#の async/await がコンパイル時に生成する「状態マシン(state machine)」由来のメソッドで、プロジェクト内で MoveNext を検索しても出てこないのは正常です。
ここでやるべきことは「名前に惑わされる」よりも、次の観点でログを読むことです。
- クラッシュしたスレッド(Crashed Thread)はどれか
- 自分のアセンブリ(あなたの名前空間)に入っているフレームはどこか
- クラッシュ直前に呼ばれているOS API / フレームワークは何か
よくある詰まりポイント:症状→原因→対処(チェック表)
| 症状 | よくある原因 | 対処 |
|---|---|---|
| ずっとアドレス表示のまま | dSYMが別ビルド(UUID不一致) | dwarfdump --uuid でUUIDを突き合わせ、該当ビルドのアーカイブから取り直す |
| 一部だけシンボル化され、他は「???」 | アプリ本体はOKだが、組み込みフレームワークのdSYMが不足 | アーカイブ内の dSYMs を丸ごと扱う/依存関係のdSYMも揃える |
| Xcode Organizerでクラッシュは見えるが詳細が薄い | 対応するアーカイブがMacにない/dSYM未取得 | 該当ビルドの .xcarchive を復元するか、dSYMを取得してXcodeが参照できる場所に置く |
| Visual StudioでIPAはあるが、dSYMが見つからない | 成果物としてIPAしか保管していない | 今後は .xcarchive(またはdSYM一式)をビルド番号付きで保管する |
| TestFlightでだけ落ちる(DebugはOK) | Release特有のリンク/最適化、権限、構成差 | まずシンボリケートして原因箇所を特定。別問題として切り分けて調査する |
運用の話:dSYMを「探す」のではなく「必ず残す」に変える
シンボリケート問題は、技術的にはUUID一致の話ですが、実務での勝敗は運用で決まります。おすすめは「ビルドごとにdSYM(できればxcarchive)を必ず保管する」ことです。
| おすすめ運用 | やること | メリット |
|---|---|---|
| ビルドごとにxcarchive保管 | MyApp_1.2.3(45).xcarchive を丸ごと保存 | dSYMだけでなく依存フレームワークのdSYMも揃いやすい |
| dSYM ZIP保管 | dSYMs をZIP化して保存 | 容量を抑えつつ最重要情報を保持できる |
| 命名規則を固定 | 「アプリ名_バージョン(ビルド番号)」で統一 | 後から照合が速い(人間のミスを減らす) |
例えば、アーカイブからdSYMsだけ取り出してZIP化するなら次のような形です。
cd /path/to/YourApp.xcarchive
zip -r YourApp_1.2.3_45_dSYMs.zip dSYMs
シンボリケート後の「読み方」だけは押さえる(起動直後クラッシュ編)
シンボリケートに成功しても、クラッシュログは情報量が多く、見る場所を間違えると迷子になります。起動直後クラッシュでは、まずここだけ確認すると効率が上がります。
| 見る項目 | よく書かれている内容 | 読み取りのコツ |
|---|---|---|
| Exception Type / Exception Codes | EXC_BAD_ACCESS、SIGABRT など | メモリアクセス違反か、abort系かで調査の方向が変わる |
| Termination Reason / Termination Signal | OS側で殺された理由が出ることも | Watchdog(起動時間)や権限/署名系の可能性も疑う |
| Crashed Thread | 落ちたスレッド番号 | まずそのスレッドのバックトレースを最優先で読む |
| Backtrace(スタックトレース) | 関数が並ぶ | 自分の名前空間に入った瞬間を起点に、前後のOS呼び出しも見る |
| Binary Images | ロードされたバイナリとUUID | シンボリケート不調時はここが“正解合わせ”の場所 |
なお、シンボリケート後に「push通知許可ダイアログの直後に落ちる」など、原因の輪郭が見えたら、それはもうシンボル化の問題ではなく別の不具合解析です。ログから得た手掛かりを元に、再現条件(端末種別・iOSバージョン・初回起動か・権限状態など)を整理して切り分けるのが定石です。
まとめ:迷ったら「UUID一致」「アーカイブ保管」「Xcode側でシンボリケート」の順に戻る
TestFlightのクラッシュログがシンボリケートされないときは、技術的には複雑に見えても、実際は次の3点に収束します。
- dSYMはクラッシュしたビルドと一致しているか(UUID一致)
- そのビルドのdSYM/xcarchiveを手元に残しているか
- シンボリケート作業はXcode側の流儀で進めているか
この土台が整うと、スタックトレースが一気に読めるようになり、起動直後クラッシュの原因特定までの時間が大幅に短縮できます。

コメント