ios-app-build-automation
Complete iOS App from Source Code to Build fully automated pipeline. Uses XcodeGen + xcodebuild CLI, no Xcode GUI required.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Complete iOS App from Source Code to Build fully automated pipeline. Uses XcodeGen + xcodebuild CLI, no Xcode GUI required.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
Social media account setup guide for AI/developer platforms. Agent prepares all content (bio, avatar, posts) for user to copy-paste, since automation is blocked by Cloudflare/CAPTCHA/React shadow DOM. Supports X/Twitter, Medium, Dev.to, Reddit, LinkedIn.
Fix missing .dylib libraries when brew upgrades break Node.js browser tooling — create symlinks from old version numbers to new.
Auto-detect environment and record terminal/GUI demos for GitHub repos. Supports VHS, asciinema+agg, svg-term, and cua-driver modes. Single-command orchestration layer.
Use when releasing a new open-source project or skill pack on GitHub. Follows the proven exposure playbook from Hermes Core Skills release — covering README, community health, topics, social media, and verification. Ensures every release meets a minimum exposure bar so the project is discoverable, trustworthy, and ready for adoption.
Multi-angle GitHub project discovery using browser + GitHub API + trending, with practical clone/install/absorption workflow
Use when installing any new tool/package from GitHub, Brew, or Cask — includes post-install verification script, automated health check cron, and deliverable report so the user can independently confirm the tool is in active daily use.
SOC 職業分類に基づく
| name | ios-app-build-automation |
| description | Complete iOS App from Source Code to Build fully automated pipeline. Uses XcodeGen + xcodebuild CLI, no Xcode GUI required. |
| version | 2.0.0 |
| author | Hermes Agent (BuddhaTranslator case study — real-world battle-tested) |
| license | MIT |
| tags | ["ios","xcode","automation","build","xcodegen","swift","whisper","cicd"] |
Automated pipeline using XcodeGen + xcodebuild CLI to build iOS apps from source code. No Xcode GUI required. Supports macOS fallback build (when iOS Simulator is not installed).
brew install xcodegen # Xcode project generator (v2.45+)
xcode-select --install # Xcode command line tools
Project_Root/
├── project.yml # XcodeGen spec (only needs manual maintenance)
├── autobuild.sh # Automation pipeline script
├── YourApp/ # App source code directory
│ ├── YourAppApp.swift
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Models/
│ ├── Services/
│ ├── Views/
│ └── Assets.xcassets/
├── WhisperLocal/ # Embed whisper.cpp (without SPM)
│ ├── whisper.cpp
│ ├── ggml.c
│ ├── include/
│ └── ggml-metal.metal (excluded on macOS to avoid Metal toolchain dep)
└── Resources/
├── common_phrases/
└── tts_cache/
Source code changes (single edit point: Project_Root/YourApp/)
│
▼
Step 1: sync_source() — Ensure directory structure + copy source files
│
▼
Step 2: xcodegen generate — Generate complete .xcodeproj from project.yml
│ (Auto-generates all PBXBuildFile, PBXFileReference, PBXGroup)
▼
Step 3: xcodebuild build — CLI build (Simulator / device / macOS)
│
▼
Build success → .app bundle at $DERIVED_DATA/Build/Products/Debug/
name: YourAppName
options:
bundleIdPrefix: com.yourdomain
deploymentTarget:
iOS: "18.0"
macOS: "15.0"
xcodeVersion: "26.5"
createIntermediateGroups: true
settings:
base:
INFOPLIST_FILE: YourApp/Info.plist
DEVELOPMENT_TEAM: ""
MARKETING_VERSION: "1.0"
CURRENT_PROJECT_VERSION: "1"
SWIFT_VERSION: "6.0"
targets:
YourApp:
type: application
platform: iOS
sources:
- path: YourApp/Models
- path: YourApp/Services
- path: YourApp/Views
- path: YourApp/Resources
type: folder
- path: YourApp/YourAppApp.swift
- path: YourApp/ContentView.swift
- path: YourApp/Assets.xcassets
# Embed whisper.cpp directly into app target sources
- path: WhisperLocal/
excludes:
- "coreml/"
- "ggml-metal.metal"
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.yourdomain.YourApp
INFOPLIST_FILE: YourApp/Info.plist
TARGETED_DEVICE_FAMILY: "1"
HEADER_SEARCH_PATHS: "$(SRCROOT)/WhisperLocal/include"
OTHER_LDFLAGS: "-lz -lstdc++ -framework Accelerate -framework CoreML"
# macOS build target (for testing when iOS Simulator not installed)
YourAppMac:
type: application
platform: macOS
sources:
# Same sources as iOS target
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.yourdomain.YourApp.mac
SWIFT_ACTIVE_COMPILATION_CONDITIONS: $(inherited) MACOS_BUILD
HEADER_SEARCH_PATHS: "$(SRCROOT)/WhisperLocal/include"
OTHER_LDFLAGS: "-lz -lstdc++ -framework Accelerate -framework CoreML"
whisper.spm uses unsafeFlags for optimization.
Xcode policy: SPM packages with unsafeFlags cannot be used as dependencies.
The package product 'whisper' cannot be used as a dependency
because it uses unsafe build flags.
# First time setup (copy from cached SPM checkout into project)
WHISPER_SRC="$HOME/Library/Developer/Xcode/DerivedData/YourApp/SourcePackages/checkouts/whisper.spm/Sources/whisper"
mkdir -p ProjectRoot/WhisperLocal/include
cp -R "$WHISPER_SRC/"* ProjectRoot/WhisperLocal/
Don't add XcodeGen target dependency — XcodeGen 2.45.4 has a bug in validation of local static library targets (invalid dependency error). Embed source directly in the app target.
Apple plist parser does NOT accept // comments!
<!-- ❌ This will cause build failure -->
// Info.plist comment
<!-- ✅ Correct format -->
<?xml version="1.0" encoding="UTF-8"?>
Use plutil -lint Info.plist to validate first.
non-final class cannot conform to 'Sendable'// ✅ Fix: mark as final or @preconcurrency import
final class TTSEngine: NSObject, AVSpeechSynthesizerDelegate { ... }
sending 'self' risks causing data races// ✅ Fix: @MainActor on ObservableObject
@MainActor class TranslationPipeline: ObservableObject { ... }
missing CommonCrypto symbolsimport CommonCrypto
extension String {
var md5: String { ... }
}
// cacheKey 可能係 NSString,用時 cast:(cacheKey as String).md5
// AVAudioSession — macOS unavailable
#if !os(macOS)
let audioSession = AVAudioSession.sharedInstance()
#endif
// NavigationView + .navigationViewStyle(.stack) — iOS only
// Use NavigationStack (iOS 16+)
NavigationStack { ... }
./autobuild.sh # Sync → Gen → Build (macOS fallback)
./autobuild.sh device # Sync → Gen → Build (iPhone)
./autobuild.sh clean # Clean → Sync → Gen → Build
./autobuild.sh open # Sync → Gen → Open Xcode
XcodeGen 2.45.4 validation: invalid dependency error.
Solution: Add C/ObjC/C++ source files directly to the app target sources, don't use a separate target.
Symptom: Unable to find a device matching the provided destination specifier
Diagnosis: xcrun simctl list runtimes shows nothing
Solution:
-scheme YourAppMac -destination 'platform=macOS')Symptom: cannot execute tool 'metal' due to missing Metal Toolchain
Solution: xcodebuild -downloadComponent MetalToolchain (~688MB)
Solution: Embed source code directly (detailed above)
Solution: Validate with plutil -lint. Info.plist cannot have // comments.
If code changes produce stale build results:
Product → Clean Build FolderWhen using macOS target to build iOS app (for quick testing), these fixes are needed:
// Problem: non-final class cannot conform to Sendable
// Fix: mark final + @preconcurrency import
import Foundation
@preconcurrency import AVFoundation
import CommonCrypto
final class TTSEngine: NSObject, AVSpeechSynthesizerDelegate {
// Problem: AVAudioSession macOS unavailable
// Fix: #if !os(macOS)
#if !os(macOS)
private let audioSession = AVAudioSession.sharedInstance()
#endif
// Problem: sending 'self' risks causing data races (Swift 6)
// Fix: @MainActor
@MainActor
class TranslationPipeline: ObservableObject {
// 問題:.navigationViewStyle(.stack) macOS unavailable
// Fix: 用 NavigationStack (iOS 16+ / macOS 13+)
NavigationStack {
ConversationView()
}
// 問題:Color(.systemBackground) 唔 cross-platform
// Fix:用 explicit RGB
Color(.sRGB, red: 1, green: 1, blue: 1, opacity: 1)
如果 xcodebuild 出 Unable to find a device 但 SDK 已存在(ls /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/ check 到 .sdk),即係 Simulator runtime 未裝。
解決:
# Check runtime status
xcrun simctl list runtimes
# Download only iOS Simulator runtime
xcodebuild -downloadPlatform iOS
# Or download all (will do watchOS/tvOS too - slower):
xcodebuild -downloadAllPlatforms
Download 完之後 check:
xcrun simctl list devices # Should show iPhone 17 Pro etc.
whisper.cpp 嘅 ggml-metal.m 用咗 [release] message,但 Xcode project default 係 ARC。
症狀:
error: 'release' is unavailable: not available in automatic reference counting mode
解決:喺 project.yml 用獨立 path entry(XcodeGen map syntax compilerFlags: file: flag 唔 work):
sources:
# whisper.cpp source files (without ggml-metal.m — 獨立處理)
- path: WhisperLocal/
excludes:
- "coreml/"
- "ggml-metal.metal"
includes:
- "*.c"
- "*.cpp"
- "*.h"
# ggml-metal.m 獨立出嚟加 -fno-objc-arc(❗ map syntax 唔得,要用 array)
- path: WhisperLocal/ggml-metal.m
compilerFlags: ["-fno-objc-arc"]
WhisperLocal/include 如果加做 type: folder source,build 時會 copy 入 .app bundle,導致 simctl install fail。
症狀:.app bundle 入面有 include/ggml.h、include/whisper.h
解決:用 excludes: ["include/"] 排除,headers 只透過 HEADER_SEARCH_PATHS 使用:
sources:
- path: WhisperLocal/
excludes:
- "coreml/"
- "ggml-metal.metal"
- "include/" # ❗ 唔好 copy headers 入 .app
includes:
- "*.c"
- "*.cpp"
- "*.h"
settings:
base:
HEADER_SEARCH_PATHS: "$(SRCROOT)/WhisperLocal/include" # headers 只喺呢度用
症狀:simctl install 出 Missing bundle ID error,雖然 plutil -lint Info.plist 係 OK,defaults read Info CFBundleIdentifier 都正常傳回 bundle ID。
原因:Xcode 26.5 嘅 iOS Simulator runtime 用 cryptex volume 掛載(/Library/Developer/CoreSimulator/Volumes/iOS_23F77/),simctl 讀 Info.plist 時用到 runtime 嘅 plist parser 但 cryptex volume 有 bug。
Debug builds 多咗 __preview.dylib + BuddhaTranslator.debug.dylib split binary 都加劇問題。
解決方法 (by priority):
xcodebuild build-for-testing(build 成功但 launch 仍可能 fail)預防:project.yml 加 configs block 跳過 codesign(simulator builds 唔需要):
settings:
configs:
debug:
CODE_SIGNING_ALLOWED: "NO"
release:
CODE_SIGNING_ALLOWED: "NO"
| Phase | Method | Result | Problem |
|---|---|---|---|
| 1 | 手寫 .pbxproj | ❌ Build failed | productTypeIdentifier missing |
| 2 | Xcode GUI Add Files | ⚠️ Lost sync | 人為操作不可重複 |
| 3 | ContentView.swift overwrite | ❌ Cache 舊 code | Xcode cache issue |
| 4 | SPM whisper.spm | ❌ unsafeFlags | Xcode policy blocks it |
| 5 | XcodeGen + SPM | ❌ 同上 | 同一問題 |
| 6 | XcodeGen + embed source | ✅ XcodeGen ✅ Build compile | Metal toolchain missing |
| 7 | Full pipeline (final) | ✅ XcodeGen ✅ Code compile | 剩 iOS Simulator runtime 未裝 |
project.yml — ~92 linesautobuild.sh — ~419 lines bashWhisperLocal/ — 17 files (whisper.cpp + ggml source)Info.plist — 37 lines XMLSource code 修改點只有一個。唔好分散去多個目錄。所有改動喺 BuddhaTranslator/ folder 做,autobuild.sh 負責 sync + generate + build。
# ✅ Correct
xcrun devicectl device install app \
--device "iPhone Name" \
"/path/to/Release-iphoneos/App.app"
# ❌ Wrong: --device is positional arg syntax
xcrun devicectl device install app --device "name" --package path # Unknown option '--package'
xcrun devicectl install app --device name path # Unknown option '--device'
devicectl 限制:
launch subcommand — user 要自己㩒 app icondevice info apps 確認已安裝device process launch 可以 launch app 但冇 stdout capture第一次 Debug build 成功簽名後,第二次 build 會 errSecInternalComponent:
BuddhaTranslator.debug.dylib + __preview.dylib-configuration Release — Release build 冇 debug dylib從 source code 到真機安裝嘅全自動流程,唔使㩒 Xcode 任何掣。
# List connected devices (offline + online)
xcrun xctrace list devices 2>&1
# Check via devicectl (shows "available (paired)" status)
xcrun devicectl list devices 2>&1
# Check if device shows in xcodebuild destinations
xcodebuild -project YourApp.xcodeproj \
-scheme YourApp \
-showdestinations 2>&1 | grep "platform:iOS, arch:arm64"
Expected output:
{ platform:iOS, arch:arm64, id:00008140-XXXXXXXX, name:My iPhone }
當 security find-identity -v -p codesigning 出 "0 valid identities found" 時,代表未有任何 signing certificate。即使用戶已經喺 Xcode → Settings → Accounts 加咗 Apple ID,仍然需要搵返個 Team ID:
# Extract Team ID from Xcode preferences (no signing cert required)
defaults read com.apple.dt.Xcode 2>&1 | grep -A 5 "IDEProvisioningTeamByIdentifier"
Expected output:
{
isFreeProvisioningTeam = 1;
teamID = ABC123XYZ;
teamName = "User Name (Personal Team)";
teamType = "Personal Team";
Copy 個 teamID(如 ABC123XYZ)落 project.yml。
settings:
base:
DEVELOPMENT_TEAM: "ABC123XYZ" # 從 Xcode preferences 抽出
# 唔好留空,否則 xcodebuild 會出 "requires a development team" error
targets:
YourApp:
settings:
base:
CODE_SIGN_STYLE: Automatic # or leave out, set via CLI
唔使加 CODE_SIGN_STYLE=Automatic 落 project.yml — 可以由 CLI 參數傳入。
cd /path/to/project
# Generate xcodeproj (always do this first)
rm -rf YourApp.xcodeproj
xcodegen generate --project . --spec project.yml
# Build + sign for real device
xcodebuild build \
-project YourApp.xcodeproj \
-scheme YourApp \
-destination 'platform=iOS,id=DEVICE_ID' \
-allowProvisioningUpdates \
CODE_SIGN_STYLE=Automatic \
CODE_SIGNING_REQUIRED=YES \
2>&1 | tail -20
Key flags:
| Flag | Purpose |
|---|---|
-allowProvisioningUpdates | Allows xcodebuild to auto-create the development certificate + provisioning profile on the Apple Developer portal. Without this, no cert will be generated even with DEVELOPMENT_TEAM set. |
CODE_SIGN_STYLE=Automatic | Use automatic signing (matches the Personal Team setup) |
CODE_SIGNING_REQUIRED=YES | Enforce signing (default when building for real device) |
What happens during build:
Signing Identity: "Apple Development: user@email.com (XXXXXX)"
Provisioning Profile: "iOS Team Provisioning Profile: com.chrislam.MyApp" (UUID)
If this is the first time, Xcode will:
.app, __preview.dylib, .debug.dylib)BUILD_DIR="$HOME/Library/Developer/Xcode/DerivedData/YourApp-XXXXX/Build/Products/Debug-iphoneos"
# Find the device name from Step 0, then:
xcrun devicectl device install app \
--device "iPhone Name" \
"$BUILD_DIR/YourApp.app"
Correct syntax learned from trial-and-error:
devicectl device install app --device <name|uuid|hostname> <path-to-.app>
⚠️ NOT --package flag — the path is a positional argument, not a named option.
⚠️ NOT --device E0D1FB3E... with --package — app subcommand takes <path> as positional arg.
⚠️ The device subcommand structure is: devicectl device install app --device NAME APP_PATH
Expected output:
Acquired tunnel connection to device.
Enabling developer disk image services.
Acquired usage assertion.
App installed:
• bundleID: com.chrislam.MyApp
• installationURL: file:///private/var/containers/Bundle/Application/UUID/MyApp.app/
⚠️ devicectl has NO launch subcommand — unlike simctl launch, there is no CLI way to launch an app on a real device via devicectl (as of Xcode 26.5).
User must manually tap the app icon on their iPhone:
CFBundleDisplayName in Info.plist# Check app is installed
xcrun devicectl device info apps --device "iPhone Name" 2>&1 | grep "YourApp"
# Output: "AppName com.chrislam.MyApp 1.0.0 1"
#!/bin/bash
set -e
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
SCHEME="YourApp"
DEVICE_ID="00008140-XXXXXXXX" # From xcodebuild -showdestinations
DEVICE_NAME="My iPhone"
BUILD_DIR="$HOME/Library/Developer/Xcode/DerivedData"
TEAM_ID="ABC123XYZ" # From defaults read com.apple.dt.Xcode
# Regenerate xcodeproj
rm -rf "$PROJECT_DIR/YourApp.xcodeproj"
cd "$PROJECT_DIR"
xcodegen generate --project . --spec project.yml
# Check if device is connected
if xcrun devicectl list devices 2>&1 | grep -q "$DEVICE_NAME"; then
echo "📱 Device connected: $DEVICE_NAME"
else
echo "❌ Device not found. Run: xcrun devicectl list devices"
exit 1
fi
# Build for real device
xcodebuild build \
-project YourApp.xcodeproj \
-scheme "$SCHEME" \
-destination "platform=iOS,id=$DEVICE_ID" \
-allowProvisioningUpdates \
CODE_SIGN_STYLE=Automatic \
CODE_SIGNING_REQUIRED=YES \
-derivedDataPath "$BUILD_DIR" \
2>&1 | grep -E "BUILD SUCCEEDED|BUILD FAILED|error:|Signing Identity"
# Find the .app
APP_PATH=$(find "$BUILD_DIR" -name "*.app" -path "*/Debug-iphoneos/*" -type d 2>/dev/null | head -1)
if [ -z "$APP_PATH" ]; then
echo "❌ Can't find .app in DerivedData"
exit 1
fi
# Install on device
echo "📲 Installing..."
xcrun devicectl device install app \
--device "$DEVICE_NAME" \
"$APP_PATH" 2>&1
echo ""
echo "✅ App installed! Tap it on your iPhone to launch."
echo " Bundle: $(defaults read "$APP_PATH/Info.plist" CFBundleIdentifier 2>/dev/null)"
| Pitfall | Symptom | Solution |
|---|---|---|
security find-identity -v -p codesigning = 0 valid identities | Signing identity not created yet | Use defaults read com.apple.dt.Xcode to find IDEProvisioningTeamByIdentifier → extract teamID → use -allowProvisioningUpdates to auto-create cert during build |
requires a development team | BUILD FAILED | Set DEVELOPMENT_TEAM in project.yml or pass via CLI |
devicectl device install app --device ... --package ... | Unknown option '--package' | <path> is positional, not --package. Correct: devicectl device install app --device NAME PATH |
devicectl device launch app ... | Unknown subcommand | devicectl has no launch. User must tap manually |
| Free Apple ID signing | App expires after 7 days | Rebuild + reinstall weekly, or get $99/yr Apple Developer Program |
AllowProvisioningUpdates requires Xcode accounts setup | Error about no accounts | User must add Apple ID in Xcode → Settings → Accounts first (at least once) |
errSecInternalComponent signing failure | Debug build CodeSign fails with this error | Keychain is locked (no password = can't unlock via CLI). Fix: Use -configuration Release — Release builds skip __preview.dylib + BuddhaTranslator.debug.dylib split binary, avoiding the multi-binary signing chain that triggers the error in locked-keychain scenario |
| 第一次安裝後彈「不受信任的開發者」 | iOS blocks launch for free Apple ID signed apps | Go to Settings → General → VPN & Device Management → tap the developer profile → tap Trust → confirm |