Visual Studio 2019 でビルドした Xamarin.iOS アプリを App Store に提出しようとしたとき、「altool exited with code 1」だけが出て止まる――そんな“情報が見えない失敗”を手早く解体・修復するための実践ガイドです。Xcode Organizer/Transporter でエラーを可視化し、App Icon のアルファ除去、Linker の調整、バイナリ肥大化の抑制、ランチ画像消失の副作用対策、ビルド番号の衝突回避までを、再現性のある手順とチェックリストでまとめます。
Xamarin.iOS 申請トラブルの全体像と最短ルート
本記事で扱うのは、Visual Studio 2019(Windows または Mac)で作成した Xamarin.iOS アプリを App Store Connect へアップロードする際に遭遇しやすい以下の事象です。
- アップロード時に “altool exited with code 1” だけが表示され、詳細が何も分からない。
- Xcode の Organizer → Archives で再検証すると、次の具体エラーが出る。
- App Icon にアルファチャンネルが含まれている
- armv7 用バイナリサイズが 60 MB 超
- arm64 用バイナリサイズが 60 MB 超
- さらに Linker 設定(Link All 推奨)を変えた結果、シミュレータでランチスクリーン画像が消える 副作用が発生。
結論から言えば、エラーの可視化 → アイコンの修正 → リンク最適化 → リソース確認 → バージョン更新 の順で対処すると、短時間で解決できます。以下で一つずつ深掘りします。
なぜ「altool exited with code 1」しか出ないのか(可視化の第一歩)
altool は検証やアップロードの詳細を標準エラー出力へ返すため、Visual Studio から直接叩いた場合は 要点が見えない ことがよくあります。最初にやるべきは エラーの可視化 です。
- Xcode → Window → Organizer → Archives を開き、対象アーカイブを選んで Validate App または Distribute App を実行し、Xcode の UI で 具体的なメッセージを確認します。
- もしくは Transporter.app(App Store 公式のアップロードツール)に
.ipaをドラッグ&ドロップして検証・送信します。UI で失敗理由が明示され、ログも残ります。 - CLI が必要な場合は次のようにバリデーションします(XML で詳細取得)。
xcrun altool --validate-app -f MyApp.ipa -u <Apple ID> -p <アプリ用パスワード> --output-format xml
この「見える化」を通すと、多くの場合は アイコンの透明度 や バイナリ肥大 といった具体エラーに分解できます。
エラー別:原因・確認ポイント・解決策
| 発生事象 | 原因・確認ポイント | 解決策 |
|---|---|---|
| 「altool exited with code 1」だけ | 詳細は標準エラー出力へ。VS の UI からは省略されがち | Transporter または Xcode → Organizer → Archives で再検証し、実際のエラー内容 を取得する |
| App Icon にアルファチャンネル | iOS の App Icon は 完全不透明(α=1.0)が必須。透過 PNG は不可 | 画像編集ツールで アルファチャンネルを削除して再保存。Asset Catalog の AppIcon セットを作り直し、全サイズを不透明に統一 |
| バイナリサイズ > 60 MB(armv7/arm64) | 不要コード/リソースがリンクされ、アプリ Slice が肥大 | Linker Behavior = Link All に設定。Enable LLVM、Strip native debugging symbols、Optimize PNG files も有効化。不要アーキテクチャ削減(例:ARM64 のみに統一) |
| Link All でランチ画像が消える | Assets 内の Images セット の欠落、または Storyboard の参照名不一致 | Asset Catalog で該当 Images セットを復元 し、LaunchScreen.storyboard の「Image Name」と名称を一致させる。ビルドアクションも確認 |
| Build Version(CFBundleVersion)衝突 | 同一バージョンのビルドが App Store Connect に既存 | info.plist の Build(CFBundleVersion)をインクリメント。例:1 → 2。Short Version と区別する |
App Icon を完全不透明にする:作業の要点
App Icon の透明度エラーは最も多い落とし穴です。ポイントは「全サイズを不透明 PNG でそろえる」こと。1枚でも透明を含むと失敗します。
- 画像の用意:1024×1024 をマスターにし、縮小して各サイズ(20〜1024)を作成。透明レイヤーは削除。
- Asset Catalog:AppIcon の各スロットをすべて埋め、欠けを残さない。
- 保存形式:PNG で OK。ただし 透明=不可。必要なら白や黒の背景合成で不透明化。
- 自動生成の落とし穴:一括エクスポート時に一部が透明のまま残ることがあります。ランダムに 2〜3 枚をサンプリング確認を。
コマンドラインで不透明化を自動処理したい場合は、画像処理ツールで「アルファ削除+白合成」を一括化するとヒューマンエラーを減らせます。
バイナリ肥大:Linker とアーキテクチャの最適化
armv7/arm64 の Slice サイズが 60 MB を超える場合、Linker と アーキテクチャ の見直しが最短です。
Release ビルド設定(推奨の基本形)
- Linker Behavior:Link All(= Full)。SDK だけでなく自分のアセンブリもリンク対象にし、未参照コードを除去。
- Supported architectures:ARM64 のみ(古い端末を切り捨て可であれば)。armv7 を外すと Slice が 1 つ減り、トータルが大幅に縮む。
- Enable LLVM:有効。ネイティブ最適化で数%〜十数%の削減が見込めます。
- Strip native debugging symbols:有効。シンボルをストリップして IPA を軽量化。
- Optimize PNG files:有効。アセットの圧縮率を上げる。
- registrar: static:動的レジストラを避けてフットプリントを縮小(Extra Args で指定)。
- Bitcode:古いテンプレートで残っていれば無効化を検討(現在の iOS では不要)。
Linker の副作用を安全に抑える(必須の知恵)
Link All は強力ですが、リフレクションや XAML/Storyboard 経由で暗黙参照している型・メンバーを「未使用」と誤判定しがちです。対策は次の 3 本柱です。
- [Preserve] 属性で保護
using Foundation; [Preserve(AllMembers = true)] public class MyViewModel { // … } - LinkerPleaseInclude.cs を用意(アクセスだけするダミーコードで参照を生やす)
public class LinkerPleaseInclude { public void Include(UIKit.UIImageView v) { v.Image = v.Image; } } - –linkskip で特定アセンブリをリンク対象から外す(最終手段)
// iOS Build → Additional mtouch arguments --linkskip=Newtonsoft.Json
グローバリゼーション(i18n)を絞る
.NET のグローバリゼーション関連アセンブリはサイズ増の一因です。Extra Args の --i18n で必要なセットだけに絞ります。
- 日本語を含むなら
--i18n=cjkを選択(west だけにすると日本語処理が欠けます)。 - 複数をカンマで併記可能(例:
--i18n=cjk,mid)。
不要アーキテクチャを外す判断軸
- サポート OS バージョン を iOS 11 以降に設定できるなら、ARM64 のみで十分なケースが多い。
- 古い端末をサポートする場合は armv7 を残しますが、容量制約にかかるなら、機能削減か配布対象の見直しを検討。
Link All にしたらランチスクリーンが消えた:原因の切り分け
Linker が画像を「未参照」と誤って除外したというよりも、Asset Catalog の Images セット欠落や名称不一致が原因であることがほとんどです。次の順で確認します。
- Assets.xcassets 内に、LaunchScreen.storyboard が参照する Image Set(例:
LaunchImage)が存在するか。 - Storyboard の Image View → Image に設定された名称が、Image Set と完全一致しているか(大文字小文字も含む)。
- Image Set の各スロット(1x/2x/3x)が埋まり、ビルドアクションが BundleResource になっているか。
- 「Optimize PNG files」有効時にも問題なく表示されるか(最終確認として実機でチェック)。
上記を満たせば、Link All のままでもランチ画像は正常に表示されます。Assets から画像セットを誤って削除していた場合は、該当セットを復元すれば解決です。
ビルド番号の衝突(CFBundleVersion)を防ぐ運用
App Store Connect には同一 Short Version に対して、ビルド番号(CFBundleVersion)が単調増加 という制約があります。解決は単純で、info.plist のビルド番号をインクリメントするだけです。衝突を恒久的に避けるなら、CI で自動採番するのが有効です。
// 例:日付+通番
CFBundleShortVersionString = 1.4.0
CFBundleVersion = 2024110201
チームで手動更新する場合は、ビルド前フックで PlistBuddy を使って書き換えるとミスが減ります。
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion 2024110201" path/to/Info.plist
具体手順:Visual Studio 2019(Windows)+ Mac ビルドホスト
- Release|iPhone 構成に切り替え。
- iOS Bundle Signing:Distribution 証明書と App Store 用プロビジョニングを選択。
- iOS Build:
- Linker Behavior:Link All
- Supported architectures:ARM64(可能なら単独)
- Enable LLVM:On
- Strip native debugging symbols:On
- Optimize PNG files:On
- Additional mtouch arguments:
--registrar:static --i18n=cjk
- ビルド番号 を更新(
info.plist)。 - Archive を作成(メニューの アーカイブ)。
- .ipa のエクスポート(Distribute → App Store → Upload または Export)。
- Xcode → Organizer → Archives で対象アーカイブを選び Validate。問題なければ Distribute。
- あるいは Transporter に .ipa をドラッグし、検証 → 送信。
csproj による再現性ある設定(サンプル)
GUI 設定は便利ですが、差分が見えづらいのが難点です。再現性と運用性を上げるため、Release|iPhone の条件付きで csproj に明示することを推奨します。
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|iPhone'">
<MtouchLink>Full</MtouchLink> <!-- Link All -->
<MtouchArch>ARM64</MtouchArch> <!-- armv7 を外し容量削減 -->
<MtouchUseLlvm>true</MtouchUseLlvm>
<MtouchExtraArgs>--registrar:static --i18n=cjk</MtouchExtraArgs>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
</PropertyGroup>
一部のライブラリでリンク除外が必要なら、MtouchExtraArgs へ --linkskip=AssemblyName を追加し、最小限で運用します。
サイズ最適化をもう一段攻めるテクニック
- 画像アセットの見直し:未使用の @3x/@2x を削除。SVG→PDF(ベクタ)対応のアセットは PDF ベースに切り替え。
- リソースの遅延ロード:初回起動サイズの圧迫を避け、後段でダウンロードする(規約・審査観点と UX のバランスに配慮)。
- 文字フォント:巨大なカスタムフォントは可変フォントやサブセット化を検討。
- 静的レジストラ:
--registrar:staticを基本とし、動的レジストラ分のオーバーヘッドを削減。 - AOT 最適化オプション:LLVM と組み合わせ、Release だけに限定適用。
検証ログを読むコツ(Xcode Organizer/Transporter)
エラーの表現はしばしば冗長です。読み解きのポイントを押さえると、原因特定が速くなります。
- App Icon:文末に with alpha channel とあれば 100% 透明が原因。どのサイズかも記載されることが多い。
- Binary Size:armv7 slice > 60 MB のように Slice 別の閾値に触れる。armv7 を外す/Link All 化で改善する。
- Version:CFBundleVersion must be higher と明示。ビルド番号のみ上げればよい。
よくある質問(実務でつまずくポイント)
Q. Link All にすると起動時クラッシュします。
[Preserve] と LinkerPleaseInclude を追加し、反射解決している ViewModel/カスタムレンダラ/JSON バインディング対象などを保護してください。改善しない場合、問題のライブラリだけ --linkskip=LibName でリンク除外し、影響範囲を最小化します。
Q. 画像は消えないのに、特定の XAML(または Storyboard)の要素が出ません。
動的に Type.GetType() などで解決している場合、型がリンクで落ちている可能性があります。該当クラスに [Preserve(AllMembers=true)] を付けるか、LinkerPleaseInclude で明示的に参照を生やしてください。
Q. どうしても 60 MB の閾値を割れません。
- armv7 を外せるか検討(サポート OS と端末要件を見直す)。
- 大容量のネイティブ SDK(例:ML/AR 系)を使っている場合、静的リンクの代替や必要機能の最小セット化を検討。
- リソースの外部配信(初回は軽量、必要に応じてダウンロード)へ設計変更。
Q. Visual Studio 側では成功するのに、審査で弾かれます。
最終検証は Xcode Organizer での Validate を正とし、Transporter のログまで確認する運用を推奨します。VS の出力はサマリであり、詳細は省略されがちです。
チェックリスト:提出直前の 60 秒点検
- App Icon:全サイズが不透明。透明を含む PNG は 0 件。
- Asset Catalog:Launch 用 Image Set が存在し、Storyboard の Image 名称 と一致。
- iOS Build:Link All/ARM64/Enable LLVM/Strip Symbols/Optimize PNG を有効。
- mtouch:
--registrar:static、必要に応じて--i18n=cjk。 - ビルド番号:CFBundleVersion をインクリメント。Short Version は据え置きで OK。
- Xcode Organizer:Validate → Distribute の順で問題なし。
トラブル再現〜解消の“手順書”テンプレート
チームで共有できるよう、以下のテンプレートをそのまま社内 Wiki に貼り付けて使えます。
【前提】
- VS2019 / Xamarin.iOS
- iOS Provisioning: Distribution
- Build: Release|iPhone
【手順】
1. info.plist の CFBundleVersion を +1
2. iOS Build 設定を確認
* Linker Behavior = Link All
* Supported architectures = ARM64
* Enable LLVM = On
* Strip native debugging symbols = On
* Optimize PNG files = On
* Additional mtouch args = "--registrar:static --i18n=cjk"
3. Assets.xcassets を開き、AppIcon 全サイズが不透明であることを目視確認
4. LaunchScreen.storyboard の Image 名と Asset の Image Set 名を一致
5. Archive → Export .ipa
6. Xcode → Organizer → Archives → Validate → Distribute
7. エラーがあればメッセージを基に該当項目を修正
トラブルの背景(仕組みから理解する)
なぜ App Icon の透明がダメなのか? iOS のホーム画面での描画最適化・統一感確保のため、角丸や影などの装飾は OS が担い、アイコン画像側は「完全不透明の四角」で提供する前提になっています。透明ピクセルが混じるとアップロード段階で検証に失敗します。
なぜ Link All がサイズ削減に効くのか? Xamarin.iOS は AOT で .NET アセンブリをネイティブ化しますが、未参照コードも含めたままだとネイティブ側に変換され、バイナリを押し上げます。Link All は「リフレクションでの暗黙参照」などを除き、未使用の IL を大胆に間引くため、Slice サイズが大きく下がります。副作用は [Preserve] や LinkerPleaseInclude でコントロール可能です。
デバッグに効く小ネタ集
- IPA を解凍して中を確認:
Payload/<AppName>.app配下に巨大な Framework や不要リソースがないかを直接チェック。 - シミュレータと実機を分けて確認:シミュレータは x64/ARM64 Mac 向け Slice で動作条件が異なります。App Store 申請は 実機用 Release を正として検証。
- サイズ増加の回帰検知:CI で IPA サイズの閾値チェックを入れ、PR 時に差分をレビュー。
最終まとめ:解決への導線
「altool exited with code 1」で詰まったら、まずは Organizer/Transporter で可視化。エラーが「App Icon のアルファ」「Slice サイズ超過」「ビルド番号の衝突」であれば、
- App Icon を完全不透明で作り直し、Asset Catalog を更新。
- iOS Build を Link All+ARM64+LLVM+Strip Symbols+Optimize PNG へ最適化。必要なら
--i18n調整。 - ランチ画像は Assets の Image Set 復元 と名称一致で解消。
- CFBundleVersion を +1 して再アーカイブ。
- Xcode Organizer で Validate → Distribute を通す。
この順番で実施すれば、再現性高くエラーを潰し込み、App Store への提出を完了できます。チームでは csproj へ設定を明記し、テンプレート手順とチェックリストを運用することで、次回以降の工数を最小化できます。
付録:実際に使える比較表(再掲・拡張)
| 項目 | 推奨設定 | 補足・副作用 |
|---|---|---|
| Linker Behavior | Link All | 未参照コードを徹底削減。反射利用は [Preserve] で保護 |
| Supported architectures | ARM64(可能なら単独) | 古い端末切り捨てとトレードオフ。容量インパクト大 |
| Enable LLVM | On | サイズ・性能が改善。ビルド時間はやや増 |
| Strip native debugging symbols | On | デバッグ時は dSYM を別途収集 |
| Optimize PNG files | On | 見た目は変えず容量を圧縮 |
| registrar | static | 動的レジストラ分のオーバーヘッド削減 |
| i18n | cjk | 日本語環境を想定。west 単独は避ける |
| CFBundleVersion | 単調増加 | Short Version と混同しない |
| App Icon | 完全不透明 PNG | 透明ピクセルが 1 つでもあれば失敗 |
付録:よく使う mtouch 引数メモ
// 反射保護は [Preserve] と LinkerPleaseInclude が基本。必要時のみ:
--linkskip=AssemblyName // 特定アセンブリをリンク除外
--registrar:static // 静的レジストラを使用
--i18n=cjk // 日本語を含むグローバリゼーションをロード
--optimize=all // 追加最適化(環境により効果差あり)
これで提出できる:最終フローのひな形
- 設定見直し(Linker/Arch/i18n/PNG など)→ Archive。
- Xcode Organizer で Validate → NG なら該当箇所をピンポイント修正。
- Transporter または Organizer で Distribute。
- App Store Connect 側でビルド選択 → 審査提出。
同じミスを繰り返さないために、この記事のチェックリストと csproj スニペットをそのままプロジェクトに取り込み、“ビルドすれば通る” 再現性を担保してください。

コメント