CocoaPods から Swift Package Manager への移行を、壁紙アプリの一つで終えた日のことです。ビルドは緑、警告も想定の範囲でした。ほっとしてシミュレータで実行ボタンを押すと、起動画面が一瞬だけ映り、そのままホーム画面に戻されました。
クラッシュのダイアログも、赤い行も出ません。Xcode のコンソールを開くと、ようやく一行だけ、見慣れない文言が残っていました。
dyld[1234]: Library not loaded: @rpath/AppLovinSDK.framework/AppLovinSDK
Referenced from: <...> /MyApp.app/MyApp
Reason: tried: '/usr/lib/system/introspection/AppLovinSDK.framework/AppLovinSDK' (no such file), ...
コンパイルは通っているのに、起動の前段階で止められていたのです。その一行をどう読み、どこを直したのかを、ここに書き残します。同じ移行をしている方が、半日を溶かさずに済めばと思っております。
症状の見分け方
最初にお伝えしたいのは、これが「コードのバグ」ではない、ということです。次の三つが揃っていたら、このタイプを疑ってください。
- ビルドとリンクは成功しています
- 起動した直後、
AppDelegateの最初の行に届く前に落ちます - 原因はコンソールの
dyldの一行にしか残りません
私の環境では、Crashlytics には何も届きませんでした。初期化より前に止まるのですから、当然といえば当然です。ダッシュボードが静かだったので、最初は「落ちていない」と錯覚しかけました。
何が起きていたのか
移行前の CocoaPods では、広告 SDK の取り込み方を Podfile が決めていました。SPM に移すと、その取り決めは「パッケージの提供形態」と「ターゲットの設定」に分かれます。今回の SDK は動的フレームワークとして配布されています。動的フレームワークは、次の二つが両方そろって初めて、起動時に見つけてもらえます。
- アプリの
Frameworksフォルダに実体が埋め込まれていること - 実行時の探索パスに、その場所が含まれていること
私のプロジェクトでは、広告のアダプタ経由でしか SDK を参照しておらず、アプリのターゲットから直接リンクしていませんでした。この「間接参照のまま」が曲者で、ビルドは通るのに、埋め込みの対象から漏れていたのです。
Claude Code に渡したもの、渡さなかったもの
project.pbxproj は数千行あります。丸ごと読ませるのは、文脈の無駄遣いです。私は次の二つだけを渡しました。
# 解決された依存の一覧(バージョンと、どのパッケージか)
grep -n "productName" MyApp.xcodeproj/project.pbxproj
# 実行時の探索パスの設定
grep -n "LD_RUNPATH_SEARCH_PATHS" -A3 MyApp.xcodeproj/project.pbxprojそして、dyld の一行と一緒に「ビルドは成功。起動直前で落ちる。動的フレームワークの埋め込みか探索パスを疑いたい」と添えました。返ってきた見立ては二点です。SDK がアプリのターゲットから直接リンクされていないこと。LD_RUNPATH_SEARCH_PATHS に @executable_path/Frameworks が入っていないこと。
私の仮説と同じ方向でしたが、確認の順番を決めてくれたことで、手が止まらずに済みました。なお、Claude Code に任せたのは「どこを見るか」の整理までです。ビルドと実機での確認は、手元の Xcode で私が行っています。
頼み方も、最初は雑でした。
# Before: 範囲が広すぎて、答えも広くなる
SPM に移したら起動しない。原因を調べて直して。
# After: 症状・済んだこと・疑う範囲を先に渡す
ビルドは成功。起動直前に dyld の Library not loaded で落ちる(ログ一行を貼付)。
動的フレームワークの埋め込みか Runpath を疑う。まず確認コマンドだけ出して。直すのはその後で。「直して」だけを渡すと、広い範囲を一度に書き換えようとします。確認コマンドを先に出してもらい、結果を見てから直すようにしました。いま思えば、この往復のほうが、私の場合はやり直しが少なく済んだのだと思います。
直し方(Before / After)
直すのは三か所でした。
# 1. アプリのターゲットが SDK を直接リンクするように追加
- (アダプタ経由でのみ間接参照)
+ Package Dependencies に AppLovin-MAX-Swift-Package を追加し、
+ ターゲットの Frameworks, Libraries に AppLovinSDK を追加(Embed & Sign)
# 2. Build Settings の Runpath Search Paths
- LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks" ← 欠けていた
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );先に直接依存を足し、続けて探索パスを確認しました。この順番にしたのは、探索パスだけ直しても、埋め込まれていないものは見つからないからです。
直ったかを、目で確かめる
「起動したから直った」で終わらせず、ビルド成果物の中身を確認しておくことをおすすめします。次の二行で足ります。
APP="$HOME/Library/Developer/Xcode/DerivedData/<プロジェクト名-ハッシュ>/Build/Products/Debug-iphonesimulator/MyApp.app"
ls "$APP/Frameworks" # AppLovinSDK.framework が並んでいるか
otool -l "$APP/MyApp" | grep -A2 LC_RPATH # @executable_path/Frameworks が出るかFrameworks の中に実体があり、LC_RPATH に探索パスが出ていれば、起動時に見つけてもらえる状態です。私はここまで確認して、ようやく安心しました。
直らないときの次の一手
| 見えた症状 | まず見る場所 |
|---|---|
Library not loaded が別のフレームワーク名になった | そのフレームワークも同じ「間接参照」になっていないか |
Frameworks に実体はあるのに落ちる | LC_RPATH が空か、別の値で上書きされていないか |
| シミュレータでは動き、実機で落ちる | 署名と Embed の設定(Embed & Sign か Do Not Embed か) |
| 依存の解決で止まる | xcodebuild -resolvePackageDependencies を先に通す |
私の場合、この移行は一度で終わりませんでした。ビルドを回しては直す往復を七回ほど重ねています。一回で通る移行は、まず無いと思っておくほうが気が楽です。
線引きとして残したこと
今回の失敗は、「ビルドが通る」ことと「起動できる」ことを、同じものだと思い込んでいた点にありました。
緑のビルドは入口で、起動して初めて完了です。
この移行では、ビルドの成功ではなく、成果物の Frameworks と LC_RPATH を見て完了とすることにしました。
次に同じ移行をされる方は、最初の実行の前に、先ほどの二行のコマンドだけ試してみていただければと思います。そこに実体と探索パスが見えていれば、半日ぶんの迷いは、ほとんど消えるはずです。