PluginBench
Skill
Review
Audit score 70

developing-ios-apps

daymade/claude-code-skills

Build and deploy iOS/macOS apps with XcodeGen, SwiftUI, and SPM—fixing Xcode builds, signing, and CI/CD.

What is developing-ios-apps?

Handles iOS and macOS app development using XcodeGen for project configuration, Swift Package Manager for dependencies, and SwiftUI for UI. Use this when setting up XcodeGen projects, fixing library embedding crashes, resolving code-signing errors, managing device deployment, or configuring notarization pipelines.

  • Generate and configure Xcode projects from XcodeGen project.yml files
  • Embed SPM dynamic frameworks (RealmSwift, AudioKit) to fix @rpath crashes
  • Resolve code-signing errors (Error -25294, adhoc fallback) for iOS and macOS
  • Deploy apps to physical devices and simulators with command-line builds
  • Configure Apple Developer signing, notarization, and CI/CD pipelines
  • Handle iOS version compatibility and API migration (iOS 16–17+)

How to install developing-ios-apps

npx skills add https://github.com/daymade/claude-code-skills --skill developing-ios-apps
Prerequisites
  • Xcode installed (latest version recommended)
  • Apple Developer account (free for Xcode GUI builds; paid $99/year for CLI builds and App Store)
  • Git configured for SPM dependency resolution
  • Physical iOS device for camera/hardware testing (simulator limitations noted)
Claude Code
Cursor
Windsurf
Cline

How to use developing-ios-apps

  1. 1.Create or update project.yml with target, package, and signing configuration
  2. 2.Run `xcodegen generate` to create the Xcode project
  3. 3.For SPM dynamic frameworks: open in Xcode GUI, set Embed & Sign in General tab, then CLI builds work
  4. 4.Build simulator: `xcodebuild -destination 'platform=iOS Simulator,name=iPhone 17' build`
  5. 5.Build device (paid account): `xcodebuild -destination 'platform=iOS,name=DEVICE' -allowProvisioningUpdates build`
  6. 6.Fix code-signing: configure CODE_SIGN_STYLE and DEVELOPMENT_TEAM in project.yml target settings
  7. 7.For notarization: use `@electron/notarize` with API key auth (remove teamId parameter)

Use cases

Good for
  • Setting up a new iOS project with XcodeGen and SPM dependencies
  • Fixing "Library not loaded @rpath/Framework" crashes on device
  • Debugging code-signing failures in CI/CD pipelines (GitHub Actions, etc.)
  • Deploying an app to a physical device from the command line
  • Lowering iOS deployment target and updating deprecated APIs
Who it's for
  • iOS/macOS developers using XcodeGen and Swift Package Manager
  • DevOps engineers configuring CI/CD for Apple platforms
  • Teams migrating between iOS versions or API frameworks
  • Developers building command-line or automated deployment workflows

developing-ios-apps FAQ

Why does my app crash with "Library not loaded: @rpath/Framework"?

XcodeGen doesn't auto-embed SPM dynamic frameworks (RealmSwift, AudioKit). Fix: open the project in Xcode GUI, select target → General → Frameworks, change the framework from "Do Not Embed" to "Embed & Sign", then CLI builds will work.

Can I build iOS apps from the command line with a free Apple ID?

No. Free accounts only work in Xcode GUI. Command-line builds (xcodebuild) require a paid developer account ($99/year). Xcode GUI handles provisioning automatically; CLI cannot.

What's the difference between CODE_SIGN_STYLE: Automatic vs Manual?

Automatic: Xcode manages signing certificates and provisioning profiles (GUI only, free account OK). Manual: you provide certificate and profile paths (CLI capable, requires paid account and manual setup).

How do I find my Apple Team ID for code signing?

Run `security find-identity -v -p codesigning | head -3` or get it from Xcode → Settings → Accounts → select Apple ID → view Team ID in the details panel.

Why does notarization fail with "Cannot use password credentials, API key credentials..."?

You're passing `teamId` to `@electron/notarize` while using API key authentication. Remove the `teamId` parameter—notarytool infers the team from the API key automatically.

Full instructions (SKILL.md)

Source of truth, from daymade/claude-code-skills.


name: developing-ios-apps description: >- Builds and ships iOS/macOS apps with XcodeGen, SwiftUI and SPM: Xcode build fixes, device deployment, Apple Developer signing/notarization, CI/CD pipelines. Use for XcodeGen project.yml setup, "Library not loaded @rpath" crashes, code-signing errors (Error -25294, adhoc fallback), or Electron notarization failures. Not for TCC permission dialogs (use macos-permissions) or disk cleanup (use macos-cleaner).

iOS App Development

Build, configure, and deploy iOS applications using XcodeGen and Swift Package Manager.

Critical Warnings

IssueCauseSolution
"Library not loaded: @rpath/Framework"XcodeGen doesn't auto-embed SPM dynamic frameworksBuild in Xcode GUI first (not xcodebuild). See Troubleshooting
xcodegen generate loses signingOverwrites project settingsConfigure in project.yml target settings, not global
Command-line signing failsFree Apple ID limitationUse Xcode GUI or paid developer account ($99/yr)
"Cannot be set when automaticallyAdjustsVideoMirroring is YES"Setting isVideoMirrored without disabling automaticSet automaticallyAdjustsVideoMirroring = false first. See Camera
App signed as adhoc despite certificate@electron/packager defaults continueOnError: trueSet continueOnError: false in osxSign. See Code Signing
"Cannot use password credentials, API key credentials..."Passing teamId to @electron/notarize with API key authRemove teamId. notarytool infers team from API key. See Code Signing
EMFILE during signing (large embedded runtime)@electron/osx-sign traverses all files in .app bundleAdd ignore filter + ulimit -n 65536 in CI. See Code Signing

Quick Reference

TaskCommand
Generate projectxcodegen generate
Build simulatorxcodebuild -destination 'platform=iOS Simulator,name=iPhone 17' build
Build device (paid account)xcodebuild -destination 'platform=iOS,name=DEVICE' -allowProvisioningUpdates build
Clean DerivedDatarm -rf ~/Library/Developer/Xcode/DerivedData/PROJECT-*
Find device namexcrun xctrace list devices

XcodeGen Configuration

Minimal 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

Code Signing Configuration

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 Version Compatibility

API Changes by Version

iOS 17+ OnlyiOS 16 Compatible
.onChange { old, new in }.onChange { new in }
ContentUnavailableViewCustom VStack
AVAudioApplicationAVAudioSession
@Observable macro@ObservableObject
SwiftDataCoreData/Realm

Lowering Deployment Target

  1. Update project.yml:
deploymentTarget:
  iOS: "16.0"
  1. 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
  1. Regenerate: xcodegen generate

Device Deployment

First-time Setup

  1. Connect device via USB
  2. Trust computer on device
  3. In Xcode: Settings → Accounts → Add Apple ID
  4. Select device in scheme dropdown
  5. Run (Cmd + R)
  6. On device: Settings → General → VPN & Device Management → Trust

Command-line Build (requires paid account)

xcodebuild \
  -project App.xcodeproj \
  -scheme App \
  -destination 'platform=iOS,name=DeviceName' \
  -allowProvisioningUpdates \
  build

Common Issues

ErrorSolution
"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 listedReconnect USB, trust computer on device, restart Xcode
DerivedData won't deleteClose Xcode first: pkill -9 Xcode && rm -rf ~/Library/Developer/Xcode/DerivedData/PROJECT-*

Free vs Paid Developer Account

FeatureFree Apple IDPaid ($99/year)
Xcode GUI builds✅✅
Command-line builds❌✅
App validity7 days1 year
App Store❌✅
CI/CD❌✅

SPM Dependencies

SPM Dynamic Framework Not Embedded

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:

  • Static frameworks (most SPM packages) are linked into the binary - no embedding needed
  • Dynamic frameworks (RealmSwift, etc.) must be copied into the app bundle
  • XcodeGen generates link phase but NOT embed phase for SPM packages
  • embed: true in project.yml causes build errors (XcodeGen limitation)

The Fix (Manual, one-time per project):

  1. Open project in Xcode GUI
  2. Select target → General → Frameworks, Libraries
  3. Find the dynamic framework (RealmSwift)
  4. Change "Do Not Embed" → "Embed & Sign"
  5. Build and run from Xcode GUI first

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"

Adding Packages

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

Resolving Dependencies (China proxy)

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 / AVFoundation

Camera preview requires real device (simulator has no camera).

Quick Debugging Checklist

  1. Permission: Added NSCameraUsageDescription to Info.plist?
  2. Device: Running on real device, not simulator?
  3. Session running: session.startRunning() called on background thread?
  4. View size: UIViewRepresentable has non-zero bounds?
  5. Video mirroring: Disabled automaticallyAdjustsVideoMirroring before setting isVideoMirrored?

Video Mirroring (Front Camera)

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 Sizing Issue

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()
}

Debug Logging Pattern

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

macOS Code Signing & Notarization

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:

StepWhatCritical detail
1Create CSR in Keychain AccessCommon Name doesn't matter; choose "Saved to disk"
2Request Developer ID Application cert at developer.apple.comChoose G2 Sub-CA (not Previous Sub-CA)
3Install .cer → must choose login keychainiCloud/System → Error -25294 (private key mismatch)
4Export P12 from login keychain with passwordBase64: base64 -i cert.p12 | pbcopy
5Create App Store Connect API Key (Developer role)Download .p8 once only; record Key ID + Issuer ID

GitHub Secrets required (5 secrets):

SecretSource
MACOS_CERT_P12Step 4 base64
MACOS_CERT_PASSWORDStep 4 password
APPLE_API_KEYStep 5 .p8 base64
APPLE_API_KEY_IDStep 5 Key ID
APPLE_API_ISSUERStep 5 Issuer ID

APPLE_TEAM_ID is NOT needed. notarytool infers team from the API key. Passing teamId to @electron/notarize v2.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:

  1. @electron/osx-sign: continueOnError: false — signing error throws immediately
  2. postPackage hook: codesign --verify --deep --strict + adhoc detection
  3. Release trigger script: verify local HEAD matches remote before dispatch

Verify 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


Resources

Related skills

More from daymade/claude-code-skills and the wider catalog.

DOdocs-cleaner logo

docs-cleaner

daymade/claude-code-skills

Consolidates redundant documentation while preserving all valuable content. This skill should be used when users want to clean up documentation bloat, merge redundant docs, reduce documentation sprawl, or consolidate multiple files covering the same topic. Triggers include "clean up docs", "consolidate documentation", "too many doc files", "merge these docs", or when documentation exceeds 500 lines across multiple files covering similar topics.

650 installs
EXexcel-automation logo

excel-automation

daymade/claude-code-skills

Create, parse, and control Excel files on macOS with professional formatting and complex model support.

1.4k installs
FAfact-checker logo

fact-checker

daymade/claude-code-skills

Verify factual claims in documents using web search and official sources, then propose corrections.

1.4k installs
GIgithub-contributor logo

github-contributor

daymade/claude-code-skills

End-to-end playbook for shipping high-quality pull requests to open-source projects you don't maintain — discovery, CONTRIBUTING compliance, PR-size check, minimal-diff implementation, PR description with AI-assisted disclosure, conflict resolution, and post-submission maintainer interaction. Use whenever creating, editing, or pushing a PR to a third-party GitHub repo — "submit a PR", "open a PR", "fix this upstream", "rebase against main", "respond to the bot review", an `owner/repo` target, or 提 PR / 上游 PR / 贡献代码 / rebase 冲突 / 回应维护者.

616 installs
GIgithub-ops logo

github-ops

daymade/claude-code-skills

Operate GitHub state via gh CLI and REST/GraphQL—PRs, issues, Actions, repos, permissions, 2FA.

1.4k installs
I1i18n-expert logo

i18n-expert

daymade/claude-code-skills

Set up, audit, and enforce internationalization in React/Vue/Next.js codebases with i18next or similar frameworks.

2.5k installs