developing-ios-apps by daymade/claude-code-skills
npx skills add https://github.com/daymade/claude-code-skills --skill developing-ios-apps使用 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 而未禁用自动调整 | 先设置 。请参阅相机 |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
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 |
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 中有效。命令行构建需要付费账户。
# 在目标设置中
settings:
base:
CODE_SIGN_STYLE: Automatic
DEVELOPMENT_TEAM: TEAM_ID # 从 Xcode → Settings → Accounts 获取
获取团队 ID :
security find-identity -v -p codesigning | head -3
| 仅限 iOS 17+ | iOS 16 兼容 |
|---|---|
.onChange { old, new in } | .onChange { new in } |
ContentUnavailableView | 自定义 VStack |
AVAudioApplication | AVAudioSession |
@Observable 宏 | @ObservableObject |
| SwiftData | CoreData/Realm |
project.yml:deploymentTarget:
iOS: "16.0"
2. 修复不兼容的 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
3. 重新生成:xcodegen generate
Cmd + R)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 | ❌ | ✅ |
根本原因 :XcodeGen 不会为 SPM 动态框架(如 RealmSwift、Realm)生成"嵌入框架"构建阶段。应用构建成功,但在启动时崩溃,提示:
dyld: Library not loaded: @rpath/RealmSwift.framework/RealmSwift
Referenced from: /var/containers/Bundle/Application/.../App.app/App
Reason: image not found
为何会发生 :
embed: true 会导致构建错误(XcodeGen 限制)修复方法 (手动操作,每个项目仅需一次):
手动修复后 :命令行构建(xcodebuild)将正常工作,因为 Xcode 会将嵌入设置持久化到 project.pbxproj 中。
识别动态框架 :
# 检查框架是否为动态
file ~/Library/Developer/Xcode/DerivedData/PROJECT-*/Build/Products/Debug-iphoneos/FRAMEWORK.framework/FRAMEWORK
# 动态:"Mach-O 64-bit dynamically linked shared library"
# 静态:"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 # 当包有多个产品时,需明确指定产品名称
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)。重新下载速度很慢。
相机预览需要真实设备(模拟器没有相机)。
NSCameraUsageDescription 添加到 Info.plist?session.startRunning()?isVideoMirrored 之前是否禁用了 automaticallyAdjustsVideoMirroring?关键 :必须在设置手动镜像之前禁用自动调整:
// 错误 - 会崩溃并提示 "Cannot be set when automaticallyAdjustsVideoMirroring is YES"
connection.isVideoMirrored = true
// 正确 - 先禁用自动调整
connection.automaticallyAdjustsVideoMirroring = false
connection.isVideoMirrored = true
ZStack 中的 UIViewRepresentable 可能具有零边界。使用显式框架修复:
// 错误:UIViewRepresentable 在 ZStack 中可能获得零尺寸
ZStack {
CameraPreviewView(session: session) // 可能不可见!
OtherContent()
}
// 正确:显式指定尺寸
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)")
// ... 设置代码 ...
logger.info("session.startRunning() completed")
}
// 对于 CGRect(不符合 CustomStringConvertible 协议)
logger.info("bounds=\(NSCoder.string(for: self.bounds))")
在 Console.app 中按子系统过滤。
有关详细的相机实现 :请参阅 references/camera-avfoundation.md
对于在 App Store 之外分发 macOS 应用(Electron 或原生应用),签名 + 公证是必需的。否则用户会看到"Apple 无法检查此应用是否包含恶意软件。"
5 步清单:
| 步骤 | 内容 | 关键细节 |
|---|---|---|
| 1 | 在 Keychain Access 中创建 CSR | 通用名无关紧要;选择"Saved to disk" |
| 2 | 在 developer.apple.com 请求 Developer ID Application 证书 | 选择 G2 Sub-CA(而非 Previous Sub-CA) |
| 3 | 安装 .cer → 必须选择 login 钥匙串 | iCloud/System → 错误 -25294(私钥不匹配) |
| 4 | 从 login 钥匙串导出带密码的 P12 | Base64:`base64 -i cert.p12 |
| 5 | 创建 App 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/notarizev2.5.0 传递teamId会导致凭证冲突错误。
Electron Forge osxSign 关键设置:
osxSign: {
identity: 'Developer ID Application',
hardenedRuntime: true,
entitlements: 'entitlements.mac.plist',
entitlementsInherit: 'entitlements.mac.plist',
continueOnError: false, // 关键:默认为 true,会静默回退到 adhoc
// 跳过大型嵌入式运行时中的非二进制文件(防止 EMFILE)
ignore: (filePath: string) => {
if (!filePath.includes('python-runtime')) return false;
if (/\.(so|dylib|node)$/.test(filePath)) return false;
return true;
},
// CI:显式指定钥匙串(apple-actions/import-codesign-certs 使用 signing_temp.keychain)
...(process.env.MACOS_SIGNING_KEYCHAIN
? { keychain: process.env.MACOS_SIGNING_KEYCHAIN }
: {}),
},
快速失败的三层防御:
@electron/osx-sign:continueOnError: false — 签名错误立即抛出postPackage 钩子:codesign --verify --deep --strict + adhoc 检测验证签名:
security find-identity -v -p codesigning | grep "Developer ID Application"
有关完整的逐步指南、权利文件、工作流示例和完整的故障排除(7 个真实错误及其根本原因):references/apple-codesign-notarize.md
每周安装量
197
仓库
GitHub 星标数
713
首次出现
2026年1月21日
安全审计
安装于
opencode171
claude-code168
gemini-cli166
codex166
cursor157
github-copilot151
Build, configure, and deploy iOS applications using XcodeGen and Swift Package Manager.
| Issue | Cause | Solution |
|---|---|---|
| "Library not loaded: @rpath/Framework" | XcodeGen doesn't auto-embed SPM dynamic frameworks | Build in Xcode GUI first (not xcodebuild). See Troubleshooting |
xcodegen generate loses signing | Overwrites project settings | Configure in project.yml target settings, not global |
| Command-line signing fails | Free Apple ID limitation | Use Xcode GUI or paid developer account ($99/yr) |
| "Cannot be set when automaticallyAdjustsVideoMirroring is YES" | Setting isVideoMirrored without disabling automatic | Set automaticallyAdjustsVideoMirroring = false first. See Camera |
| App signed as adhoc despite certificate | @electron/packager defaults continueOnError: true | Set continueOnError: false in osxSign. See Code Signing |
| "Cannot use password credentials, API key credentials..." | Passing teamId to @electron/notarize with API key auth | RemoveteamId. notarytool infers team from API key. See Code Signing |
| EMFILE during signing (large embedded runtime) | @electron/osx-sign traverses all files in .app bundle | Add ignore filter + ulimit -n 65536 in CI. See Code Signing |
| Task | Command |
|---|---|
| Generate project | xcodegen generate |
| Build simulator | xcodebuild -destination 'platform=iOS Simulator,name=iPhone 17' build |
| Build device (paid account) | xcodebuild -destination 'platform=iOS,name=DEVICE' -allowProvisioningUpdates build |
| Clean DerivedData | rm -rf ~/Library/Developer/Xcode/DerivedData/PROJECT-* |
| Find device name | xcrun xctrace list devices |
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
Personal (free) account : Works in Xcode GUI only. Command-line builds require paid account.
# In target settings
settings:
base:
CODE_SIGN_STYLE: Automatic
DEVELOPMENT_TEAM: TEAM_ID # Get from Xcode → Settings → Accounts
Get Team ID :
security find-identity -v -p codesigning | head -3
| iOS 17+ Only | iOS 16 Compatible |
|---|---|
.onChange { old, new in } | .onChange { new in } |
ContentUnavailableView | Custom VStack |
AVAudioApplication | AVAudioSession |
@Observable macro | @ObservableObject |
| SwiftData |
project.yml:deploymentTarget:
iOS: "16.0"
2. Fix incompatible APIs:
// 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
3. Regenerate: xcodegen generate
Cmd + R)xcodebuild \
-project App.xcodeproj \
-scheme App \
-destination 'platform=iOS,name=DeviceName' \
-allowProvisioningUpdates \
build
| Error | Solution |
|---|---|
| "Library not loaded: @rpath/Framework" | SPM dynamic framework not embedded. Build in Xcode GUI first, then CLI works |
| "No Account for Team" | Add Apple ID in Xcode Settings → Accounts |
| "Provisioning profile not found" | Free account limitation. Use Xcode GUI or get paid account |
| Device not listed | Reconnect USB, trust computer on device, restart Xcode |
| DerivedData won't delete | Close Xcode first: pkill -9 Xcode && rm -rf ~/Library/Developer/Xcode/DerivedData/PROJECT-* |
| Feature | Free Apple ID | Paid ($99/year) |
|---|---|---|
| Xcode GUI builds | ✅ | ✅ |
| Command-line builds | ❌ | ✅ |
| App validity | 7 days | 1 year |
| App Store | ❌ | ✅ |
| CI/CD | ❌ | ✅ |
Root Cause : XcodeGen doesn't generate the "Embed Frameworks" build phase for SPM dynamic frameworks (like RealmSwift, Realm). The app builds successfully but crashes on launch with:
dyld: Library not loaded: @rpath/RealmSwift.framework/RealmSwift
Referenced from: /var/containers/Bundle/Application/.../App.app/App
Reason: image not found
Why This Happens :
embed: true in project.yml causes build errors (XcodeGen limitation)The Fix (Manual, one-time per project):
After Manual Fix : Command-line builds (xcodebuild) will work because Xcode persists the embed setting in project.pbxproj.
Identifying Dynamic Frameworks :
# 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
Never clear global SPM cache (~/Library/Caches/org.swift.swiftpm). Re-downloading is slow.
Camera preview requires real device (simulator has no camera).
NSCameraUsageDescription to Info.plist?session.startRunning() called on background thread?automaticallyAdjustsVideoMirroring before setting isVideoMirrored?CRITICAL : Must disable automatic adjustment before setting manual mirroring:
// WRONG - crashes with "Cannot be set when automaticallyAdjustsVideoMirroring is YES"
connection.isVideoMirrored = true
// CORRECT - disable automatic first
connection.automaticallyAdjustsVideoMirroring = false
connection.isVideoMirrored = true
UIViewRepresentable in ZStack may have zero bounds. Fix with explicit frame:
// 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()
}
Add logging to trace camera flow:
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))")
Filter in Console.app by subsystem.
For detailed camera implementation : See references/camera-avfoundation.md
For distributing macOS apps (Electron or native) outside the App Store, signing + notarization is required. Without it users see "Apple cannot check this app for malicious software."
5-step checklist:
| Step | What | Critical detail |
|---|---|---|
| 1 | Create CSR in Keychain Access | Common Name doesn't matter; choose "Saved to disk" |
| 2 | Request Developer ID Application cert at developer.apple.com | Choose G2 Sub-CA (not Previous Sub-CA) |
| 3 | Install .cer → must choose login keychain | iCloud/System → Error -25294 (private key mismatch) |
| 4 | Export P12 from login keychain with password | Base64: `base64 -i cert.p12 |
| 5 | Create App Store Connect API Key (Developer role) | Download .p8 once only; record Key ID + Issuer ID |
GitHub Secrets required (5 secrets):
| Secret | Source |
|---|---|
MACOS_CERT_P12 | Step 4 base64 |
MACOS_CERT_PASSWORD | Step 4 password |
APPLE_API_KEY | Step 5 .p8 base64 |
APPLE_API_KEY_ID | Step 5 Key ID |
APPLE_API_ISSUER | Step 5 Issuer ID |
APPLE_TEAM_IDis NOT needed.notarytoolinfers team from the API key. PassingteamIdto@electron/notarizev2.5.0 causes a credential conflict error.
Electron Forge osxSign critical settings:
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 }
: {}),
},
Fail-fast three-layer defense:
@electron/osx-sign: continueOnError: false — signing error throws immediatelypostPackage hook: codesign --verify --deep --strict + adhoc detectionVerify signing:
security find-identity -v -p codesigning | grep "Developer ID Application"
For complete step-by-step guide, entitlements, workflow examples, and full troubleshooting (7 real-world errors with root causes): references/apple-codesign-notarize.md
Weekly Installs
197
Repository
GitHub Stars
713
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykPass
Installed on
opencode171
claude-code168
gemini-cli166
codex166
cursor157
github-copilot151
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
113,700 周安装
Oracle IDCS组织配置技能:解决登录后租户、角色与成员资格生效问题
2 周安装
Fastify Better Auth Bridge:集成Better Auth与Fastify 5的认证桥接解决方案
2 周安装
your-skill-name 技能详解:AI 助手功能、使用流程与最佳实践指南
2 周安装
AI求职申请优化器 - 智能定制简历、求职信,提升面试成功率 | 简历优化工具
154 周安装
OpenSpec 变更提案工具:一步生成提案、设计与任务文档
2 周安装
fibx CLI 配置管理 - 自定义区块链 RPC URL,解决速率限制与连接问题
2 周安装
| CoreData/Realm |