mcpField
D
developing-ios-apps

developing-ios-apps

github.com/daymade開発1.5k1.5k 回インストール今日更新
リポジトリ

developing-ios-apps について

developing-ios-appsは、XcodeGenとSwift Package Managerを使ったiOSアプリの開発、設定、デプロイを案内するスキルです。プロジェクト設定、バージョン互換性、実機ビルド、カメラ実装、Apple署名の問題に関するトラブルシューティングを扱います。

主な機能

  1. 01XcodeGenによるプロジェクト生成
  2. 02Swift Package Manager依存関係の管理
  3. 03iOS API互換性への対応
  4. 04実機へのビルドとデプロイ
  5. 05カメラと署名の問題の調査

活用シーン

  1. 01iOSプロジェクトと依存関係の設定
  2. 02フレームワーク埋め込みや署名の失敗解決
  3. 03実機でのカメラプレビュー確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/daymade/claude-code-skills --skill developing-ios-apps

インストール後、Claude Code で/developing-ios-appsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
apple-codesign-notarize.md
camera-avfoundation.md
swiftui-compatibility.md
testing-mainactor.md
xcodegen-full.md

詳細

iOS アプリ開発

XcodeGen と Swift Package Manager を使用して、iOS アプリケーションをビルド、設定、デプロイします。

重要な警告

問題原因解決策
「Library not loaded: @rpath/Framework」XcodeGen は SPM の動的フレームワークを自動で埋め込みませんまず Xcode GUI でビルドしてください(xcodebuild ではありません)。トラブルシューティングを参照してください
xcodegen generate を実行すると署名設定が失われるプロジェクト設定が上書きされるグローバル設定ではなく、project.yml のターゲット設定で構成してください
コマンドラインでの署名に失敗する無料の Apple ID の制限Xcode GUI または有料の開発者アカウント(年額 99 ドル)を使用してください
「Cannot be set when automaticallyAdjustsVideoMirroring is YES」自動調整を無効にせず isVideoMirrored を設定している先に automaticallyAdjustsVideoMirroring = false を設定してください。カメラを参照してください
証明書があるのにアプリが adhoc 署名される@electron/packager の continueOnError: true がデフォルトosxSign で continueOnError: false を設定してください。コード署名を参照してください
「Cannot use password credentials, API key credentials...」API キー認証で @electron/notarize に teamId を渡しているteamId を削除してください。notarytool が API キーからチームを推測します。コード署名を参照してください
署名中に EMFILE が発生する(埋め込みランタイムが大きい場合)@electron/osx-sign が .app バンドル内のすべてのファイルを走査するCI で ignore フィルターと ulimit -n 65536 を追加してください。コード署名を参照してください

クイックリファレンス

タスクコマンド
プロジェクトを生成xcodegen generate
シミュレーター向けにビルドxcodebuild -destination 'platform=iOS Simulator,name=iPhone 17' build
デバイス向けにビルド(有料アカウント)xcodebuild -destination 'platform=iOS,name=DEVICE' -allowProvisioningUpdates build
DerivedData をクリーンアップrm -rf ~/Library/Developer/Xcode/DerivedData/PROJECT-*
デバイス名を確認xcrun xctrace list devices

XcodeGen の設定

最小限の project.yml
name: AppName
options:
  bundleIdPrefix: com.company
  deploymentTarget:
    iOS: "16.0"

settings:
  base:
    SWIFT_VERSION: "6.0"

packages:
  SomePackage:
    url: https://github.com/org/repo
    from: "1.0.0"

targets:
  AppName:
    type: application
    platform: iOS
    sources:
      - path: AppName
    settings:
      base:
        INFOPLIST_FILE: AppName/Info.plist
        PRODUCT_BUNDLE_IDENTIFIER: com.company.appname
        CODE_SIGN_STYLE: Automatic
        DEVELOPMENT_TEAM: TEAM_ID_HERE
    dependencies:
      - package: SomePackage
コード署名の設定

個人(無料)アカウント:Xcode GUI でのみ動作します。コマンドラインでのビルドには有料アカウントが必要です。

# In target settings
settings:
  base:
    CODE_SIGN_STYLE: Automatic
    DEVELOPMENT_TEAM: TEAM_ID  # Get from Xcode → Settings → Accounts

Team ID の確認:

security find-identity -v -p codesigning | head -3

iOS バージョンの互換性

バージョンごとの API の変更
iOS 17 以降のみiOS 16 互換
.onChange { old, new in }.onChange { new in }
ContentUnavailableViewカスタム VStack
AVAudioApplicationAVAudioSession
@Observable マクロ@ObservableObject
SwiftDataCoreData/Realm
デプロイメントターゲットを下げる
  1. project.yml を更新します:
deploymentTarget:
  iOS: "16.0"
  1. 互換性のない API を修正します:
// iOS 17
.onChange(of: value) { oldValue, newValue in }
// iOS 16
.onChange(of: value) { newValue in }

// iOS 17
ContentUnavailableView("Title", systemImage: "icon")
// iOS 16
VStack {
    Image(systemName: "icon").font(.system(size: 48))
    Text("Title").font(.title2.bold())
}

// iOS 17
AVAudioApplication.shared.recordPermission
// iOS 16
AVAudioSession.sharedInstance().recordPermission
  1. 再生成します:xcodegen generate

デバイスへのデプロイ

初回セットアップ
  1. USB でデバイスを接続します
  2. デバイスでコンピューターを信頼します
  3. Xcode で「Settings → Accounts → Add Apple ID」を選択します
  4. スキームのドロップダウンでデバイスを選択します
  5. 実行します(Cmd + R)
  6. デバイスで「Settings → General → VPN & Device Management → Trust」を選択します
コマンドラインでのビルド(有料アカウントが必要)
xcodebuild \
  -project App.xcodeproj \
  -scheme App \
  -destination 'platform=iOS,name=DeviceName' \
  -allowProvisioningUpdates \
  build
よくある問題
エラー解決策
「Library not loaded: @rpath/Framework」SPM の動的フレームワークが埋め込まれていません。まず Xcode GUI でビルドすると、その後は CLI でも動作します
「No Account for Team」Xcode の「Settings → Accounts」で Apple ID を追加します
「Provisioning profile not found」無料アカウントの制限です。Xcode GUI を使用するか、有料アカウントを取得してください
デバイスが一覧に表示されないUSB を再接続し、デバイスでコンピューターを信頼してから、Xcode を再起動します
DerivedData を削除できない先に Xcode を終了します:pkill -9 Xcode && rm -rf ~/Library/Developer/Xcode/DerivedData/PROJECT-*
無料と有料の開発者アカウント
機能無料の Apple ID有料(年額 99 ドル)
Xcode GUI でのビルド✅✅
コマンドラインでのビルド❌✅
アプリの有効期間7 日間1 年間
App Store❌✅
CI/CD❌✅

SPM の依存関係

SPM Dynamic Framework Not Embedded

根本原因:XcodeGen は、SPM の動的フレームワーク(RealmSwift、Realm など)の「Embed Frameworks」ビルドフェーズを生成しません。アプリは正常にビルドされますが、起動時に次のエラーでクラッシュします:

dyld: Library not loaded: @rpath/RealmSwift.framework/RealmSwift
  Referenced from: /var/containers/Bundle/Application/.../App.app/App
  Reason: image not found

発生する理由:

  • 静的フレームワーク(ほとんどの SPM パッケージ)はバイナリにリンクされるため、埋め込みは不要です
  • 動的フレームワーク(RealmSwift など)はアプリバンドルにコピーする必要があります
  • XcodeGen は、SPM パッケージのリンクフェーズは生成しますが、埋め込みフェーズは生成しません
  • embed: true を project.yml に指定すると、ビルドエラーになります(XcodeGen の制限)

修正方法(プロジェクトごとに手動で一度だけ実施):

  1. Xcode GUI でプロジェクトを開きます
  2. ターゲットを選択 → General → Frameworks, Libraries
  3. 動的フレームワーク(RealmSwift)を探します
  4. 「Do Not Embed」から「Embed & Sign」に変更します
  5. まず Xcode GUI からビルドして実行します

手動修正後:Xcode が project.pbxproj に埋め込み設定を保存するため、コマンドラインからのビルド(xcodebuild)も動作します。

動的フレームワークの識別方法:

# Check if a framework is dynamic
file ~/Library/Developer/Xcode/DerivedData/PROJECT-*/Build/Products/Debug-iphoneos/FRAMEWORK.framework/FRAMEWORK
# Dynamic: "Mach-O 64-bit dynamically linked shared library"
# Static: "current ar archive"
パッケージの追加
packages:
  AudioKit:
    url: https://github.com/AudioKit/AudioKit
    from: "5.6.5"
  RealmSwift:
    url: https://github.com/realm/realm-swift
    from: "10.54.6"

targets:
  App:
    dependencies:
      - package: AudioKit
      - package: RealmSwift
        product: RealmSwift  # Explicit product name when package has multiple
依存関係の解決(中国向けプロキシ)
git config --global http.proxy http://127.0.0.1:1082
git config --global https.proxy http://127.0.0.1:1082
xcodebuild -scmProvider system -resolvePackageDependencies

グローバル SPM キャッシュ(~/Library/Caches/org.swift.swiftpm)は絶対に削除しないでください。再ダウンロードには時間がかかります。

カメラ / AVFoundation

カメラプレビューには実機が必要です(シミュレーターにはカメラがありません)。

すぐに確認できるデバッグ項目
  1. 権限:Info.plist に NSCameraUsageDescription を追加しましたか?
  2. デバイス:シミュレーターではなく、実機で実行していますか?
  3. セッションの実行:session.startRunning() はバックグラウンドスレッドで呼び出されていますか?
  4. ビューサイズ:UIViewRepresentable の境界にゼロ以外の値がありますか?
  5. ビデオのミラーリング:isVideoMirrored を設定する前に automaticallyAdjustsVideoMirroring を無効にしましたか?
ビデオのミラーリング(フロントカメラ)

重要:手動でミラーリングを設定する前に、自動調整を無効にする必要があります:

// WRONG - crashes with "Cannot be set when automaticallyAdjustsVideoMirroring is YES"
connection.isVideoMirrored = true

// CORRECT - disable automatic first
connection.automaticallyAdjustsVideoMirroring = false
connection.isVideoMirrored = true
UIViewRepresentable のサイズに関する問題

ZStack 内の UIViewRepresentable の境界がゼロになる場合があります。明示的なフレームを指定して修正します:

// BAD: UIViewRepresentable may get zero size in ZStack
ZStack {
    CameraPreviewView(session: session)  // May be invisible!
    OtherContent()
}

// GOOD: Explicit sizing
ZStack {
    GeometryReader { geo in
        CameraPreviewView(session: session)
            .frame(width: geo.size.width, height: geo.size.height)
    }
    .ignoresSafeArea()
    OtherContent()
}
デバッグログのパターン

カメラの処理を追跡するためにログを追加します:

import os
private let logger = Logger(subsystem: "com.app", category: "Camera")

func start() async {
    logger.info("start() called, isRunning=\(self.isRunning)")
    // ... setup code ...
    logger.info("session.startRunning() completed")
}

// For CGRect (doesn't conform to CustomStringConvertible)
logger.info("bounds=\(NSCoder.string(for: self.bounds))")

Console.app でサブシステムを指定して絞り込みます。

カメラの実装について詳しくは:references/camera-avfoundation.md を参照してください。

macOS のコード署名と公証

App Store 以外で macOS アプリ(Electron またはネイティブ)を配布するには、署名と公証が必要です。これらを行わないと、ユーザーには「Apple はこのアプリに悪意のあるソフトウェアが含まれていないことを確認できません」と表示されます。

5 ステップのチェックリスト:

手順内容重要な詳細
1Keychain Access で CSR を作成する通称は何でも構いません。「Saved to disk」を選択します
2developer.apple.com で Developer ID Application 証明書を申請するG2 Sub-CA を選択します(Previous Sub-CA ではありません)
3.cer をインストールする → login キーチェーンを選択する必要がありますiCloud/System を選ぶとエラー -25294(秘密鍵の不一致)が発生します
4login キーチェーンからパスワード付きの P12 を書き出すBase64:base64 -i cert.p12 | pbcopy
5App Store Connect API キーを作成する(Developer ロール).p8 は一度しかダウンロードできません。Key ID と Issuer ID を記録してください

必要な GitHub Secrets(5 つ):

Secret入手元
MACOS_CERT_P12手順 4 の Base64
MACOS_CERT_PASSWORD手順 4 のパスワード
APPLE_API_KEY手順 5 の .p8 の Base64
APPLE_API_KEY_ID手順 5 の Key ID
APPLE_API_ISSUER手順 5 の Issuer ID

APPLE_TEAM_ID は不要です。 notarytool は API キーからチームを推定します。@electron/notarize v2.5.0 に teamId を渡すと、認証情報の競合エラーが発生します。

Electron Forge の osxSign に関する重要な設定:

osxSign: {
  identity: 'Developer ID Application',
  hardenedRuntime: true,
  entitlements: 'entitlements.mac.plist',
  entitlementsInherit: 'entitlements.mac.plist',
  continueOnError: false,  // CRITICAL: default is true, silently falls back to adhoc
  // Skip non-binary files in large embedded runtimes (prevents EMFILE)
  ignore: (filePath: string) => {
    if (!filePath.includes('python-runtime')) return false;
    if (/\.(so|dylib|node)$/.test(filePath)) return false;
    return true;
  },
  // CI: explicitly specify keychain (apple-actions/import-codesign-certs uses signing_temp.keychain)
  ...(process.env.MACOS_SIGNING_KEYCHAIN
    ? { keychain: process.env.MACOS_SIGNING_KEYCHAIN }
    : {}),
},

失敗を即座に検出する 3 層の防御:

  1. @electron/osx-sign:continueOnError: false — 署名エラーが直ちにスローされます
  2. postPackage フック:codesign --verify --deep --strict とアドホック署名の検出
  3. リリーストリガースクリプト:ディスパッチ前にローカル HEAD がリモートと一致することを確認します

署名を確認する:

security find-identity -v -p codesigning | grep "Developer ID Application"

手順の詳細、エンタイトルメント、ワークフローの例、およびトラブルシューティング全体(実際に発生した 7 件のエラーと根本原因)については、references/apple-codesign-notarize.md を参照してください。


リソース

コメント