xcode-cloud-deploy
Skill1 starsChanged 8 months agoUnlicenseView on GitHub
Xcode Cloud Deployment und TestFlight fuer brain-ios. Nutze diesen Skill bei Build-Fehlern, Deployment-Fragen oder wenn ein neuer TestFlight-Build gemacht werden soll.
- Commits and pushes
In this repository3 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- bug-triageSkill
- build-checkSkill
The file
Files
Xcode Cloud Deploy — brain-ios
Projekt-Setup
| Feld | Wert |
| Bundle ID | $(BRAIN_BUNDLE_ID_BASE) aus Config/Local.xcconfig (Platzhalter im Repo: com.example.brain-ios) |
| Team ID | $(BRAIN_TEAM_ID) aus Config/Local.xcconfig (Platzhalter: TEAM_ID_HERE) |
| Scheme | BrainApp |
| Branch | main |
| Min iOS | 17.0 |
| Geraete | iPhone + iPad |
Private Identifier (xcconfig)
Das oeffentliche Repo enthaelt nur Platzhalter. Alle deploy-relevanten IDs
(Bundle-ID-Basis, Team-ID, Google-Client-ID; daraus abgeleitet App Group,
iCloud-Container, BGTask-IDs, Extension-Bundle-IDs, IAP-Produkt-ID) kommen aus
Config/Base.xcconfig, das optional Config/Local.xcconfig (gitignored) inkludiert.
- Lokal:
Config/Local.xcconfig.examplenachConfig/Local.xcconfigkopieren und befuellen. Nie committen. - Xcode Cloud: Im Workflow die Environment-Variablen
BRAIN_BUNDLE_ID_BASE,BRAIN_TEAM_ID,BRAIN_GOOGLE_CLIENT_IDsetzen —ci_post_clone.shschreibt darausConfig/Local.xcconfigvor dem Build. - Zur Laufzeit liest
AppIdentifiers.swiftdie Werte aus Info.plist bzw. der Bundle-ID; App, Share Extension und Widgets teilen sich so dieselbe App Group. - Der Keychain-Service-Name bleibt bewusst fix (
com.example.brain-ios), damit gespeicherte Keys einen Identifier-Wechsel ueberleben.
Deployment ausloesen
Ein git push origin main triggert automatisch einen Xcode Cloud Build.
Der Build archiviert, signiert und laedt zu TestFlight hoch.
# Build-Nummer erhoehen vor Push:
# In BrainApp.xcodeproj/project.pbxproj: CURRENT_PROJECT_VERSION erhoehen
git add -A && git commit -m "..." && git push origin mainBuild-Status pruefen
Xcode Cloud Builds sind unter App Store Connect einsehbar: https://appstoreconnect.apple.com → Brain → Xcode Cloud → Builds
Bekannte Fehler und Loesungen
Exit Code 65 — Kompilierungsfehler
- Artifacts → Logs →
xcodebuild-archive.logherunterladen - Nach
error:suchen fuer die eigentlichen Fehlermeldungen - Xcode Cloud kompiliert STRENGER als
swift testauf Linux/VPS!
Swift 6 Strict Concurrency (haeufigster Fehler!)
static varin AppIntent: Mussstatic letsein- Betrifft:
title,description,openAppWhenRunin AppIntent Structs - Fehler: "static property is not concurrency-safe because it is nonisolated global shared mutable state"
- Betrifft:
[String: Any]in Sendable Struct:@unchecked Sendableverwenden- Fehler: "stored property of Sendable-conforming struct contains non-Sendable type"
- Closure in AsyncThrowingStream:
Task { @Sendable in ... }und Parameter@Sendablemarkieren- Fehler: "Passing closure as a 'sending' parameter risks causing data races"
- AVFoundation Captures:
nonisolated(unsafe) letfuer AVAudioEngine, SFSpeechAudioBufferRecognitionRequest- Fehler: "Capture of non-Sendable type in a '@Sendable' closure"
- Missing
await: GRDBpool.readist async in Xcode-Kontext- Fehler: "expression is 'async' but is not marked with 'await'"
.accentist kein ShapeStyle-Member:Color.accentColorstatt.accentverwenden- Fehler: "Type 'ShapeStyle' has no member 'accent'"
[weak self]in Task mit @MainActor:Task { @MainActor [weak self] in }funktioniert nicht mit Swift 6- Fehler: "sending 'self' risks causing data races"
- Loesung:
[weak self]entfernen, Klasse selbst@MainActormachen
- Captured var in async Closure mutiert: Variable wird in GRDB
pool.writeetc. mutiert- Fehler: "mutation of captured var in concurrently-executing code"
- Loesung: By-value capturen
[assistantMsg], lokale Kopie mutieren
Extension-Target Fehler
import GRDBfehlt: Widgets/Extensions die GRDB-Types nutzen (z.B.Row) brauchen explizitesimport GRDB- Property-Name-Kollision:
@State private var bodykollidiert mitvar body: some View→ umbenennen! - Unused
[weak self]: Wenn self nicht im Closure genutzt wird →[weak self]entfernen- Fehler: "Variable 'self' was written to, but never read"
#if canImport(FoundationModels) richtig verwenden
- Das
importStatement muss AUSSERHALB der Funktion stehen:#if canImport(FoundationModels) import FoundationModels #endif - Methoden die FoundationModels-Types nutzen muessen KOMPLETT in
#if canImporteingewickelt sein - NICHT nur den Body, sondern die ganze Methode einwickeln
LanguageModelSession()Init wirft nicht — kein do/catch noetig
pbxproj — Dateien werden nicht kompiliert (KRITISCH!)
Symptom: "Cannot find 'TypeName' in scope" obwohl die Datei existiert.
Eine Datei braucht DREI Eintraege im pbxproj um kompiliert zu werden:
- PBXFileReference — Datei existiert im Projekt
B1020019 /* OpenAIProvider.swift */ = {isa = PBXFileReference; ...}; - PBXBuildFile — Verknuepfung FileReference → Build-Phase
B1010019 /* OpenAIProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1020019; }; - PBXSourcesBuildPhase — Datei in der Compile-Liste des Targets
B1010019 /* OpenAIProvider.swift in Sources */,
Wenn EINES davon fehlt, wird die Datei STILL ignoriert! Xcode zeigt keinen Fehler — die Datei erscheint im Projekt, wird aber nie kompiliert. Alle Typen darin sind dann "not in scope" in allen anderen Dateien.
Checkliste bei "Cannot find in scope":
# Pruefe ob alle 3 Eintraege existieren:
grep "MyFile.swift" BrainApp.xcodeproj/project.pbxproj
# Erwartetes Ergebnis (3 Zeilen):
# XXXX /* MyFile.swift */ = {isa = PBXFileReference; ...} ← FileReference
# YYYY /* MyFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = XXXX; } ← BuildFile
# YYYY /* MyFile.swift in Sources */, ← In BuildPhaseHaeufige Fallen:
- Datei in PBXGroup + PBXFileReference, aber KEIN PBXBuildFile → wird nicht kompiliert
- Datei hat PBXBuildFile, ist aber nicht in PBXSourcesBuildPhase → wird nicht kompiliert
- Datei in Unterordner (z.B. Onboarding/) aber PBXGroup hat falschen
path→ Xcode findet Datei nicht
Unterordner im pbxproj (Onboarding/, LLMProviders/, Bridges/)
Dateien in Unterordnern brauchen eine eigene PBXGroup mit path:
OB030001 /* Onboarding */ = {
isa = PBXGroup;
children = (
2E2ECF4C /* OnboardingStaticPages.swift */,
);
path = Onboarding; ← Xcode sucht in Sources/BrainApp/Onboarding/
sourceTree = "<group>";
};Wenn die Dateien stattdessen direkt in der BrainApp-Gruppe liegen (ohne eigene
Untergruppe mit path), sucht Xcode in Sources/BrainApp/ und findet sie nicht.
Exit Code 70 (Export fehlgeschlagen)
- Paid Apps Agreement in App Store Connect → Business pruefen
- Status muss "Active" sein, nicht "New"
- Kann bis zu 30–60 Min dauern bis Agreement propagiert
- Alle Felder muessen ausgefuellt sein (auch fuer Privatpersonen)
- Auch pruefen: developer.apple.com/account → Agreements
Exit Code 75 — Timeout (30 Min kein Output)
Build hat 30 Min kein Output produziert. Zwei Hauptursachen:
Ursache 1: some View mit komplexen @ViewBuilder Switches
- Exponentieller Type-Checker-Aufwand bei verschachtelten
if/else/switchin@ViewBuilder - Compile-Zeit: 31 Min → unter 2 Min nach Fix!
- Loesung: AnyView Type Erasure
// FALSCH — exponentieller Type-Check: @ViewBuilder func render(_ node: ScreenNode) -> some View { switch node.type { case "text": Text(node.value) case "stack": VStack { ... } // 20+ cases → Compiler explodiert } } // RICHTIG — linearer Type-Check: func render(_ node: ScreenNode) -> AnyView { switch node.type { case "text": return AnyView(Text(node.value)) case "stack": return AnyView(VStack { ... }) } } - Kein Performance-Nachteil zur Laufzeit (AnyView-Overhead ist vernachlaessigbar)
@ViewBuilderentfernen, stattdessenreturn AnyView(...)
Ursache 2: ci_post_clone.sh ruft resolve auf
xcodebuild -resolvePackageDependenciesin ci_post_clone.sh kann Timeout verursachen- Xcode Cloud resolved selbst — das Script soll das NICHT nochmal machen
List(selection:content:) nicht auf iOS verfuegbar
// FALSCH — macOS-only API:
List(items, selection: $selected) { item in ... }
// RICHTIG — iOS-kompatibel:
List { ForEach(items) { item in ... } }"The archive contains nothing that can be signed"
XCLocalSwiftPackageReferencefehlt in project.pbxproj- Muss auf
relativePath = .zeigen (lokales Package.swift) - Symptom: Build dauert nur 3-5 Sek, kein Code wird kompiliert
"Could not resolve package dependencies" / "a resolved file is required"
- Package.resolved MUSS committed sein unter:
BrainApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved - NIEMALS in .gitignore packen! Xcode Cloud deaktiviert automatische Resolution
(
CLONED_SOURCE_PACKAGES_PATHgesetzt) und verlangt die Datei zwingend - Auch
xcodebuild -resolvePackageDependenciesin ci_post_clone.sh kann das NICHT umgehen - ci_post_clone.sh mit resolve als Sicherheitsnetz behalten, aber nie als Ersatz fuer committed Package.resolved
- Wenn Package.resolved auf VPS nicht generierbar (z.B. wegen Swift-Version-Mismatch): Die Datei manuell von einem erfolgreichen Xcode Cloud Build herunterladen und committen
"Container file path is invalid"
- Workflow wurde aus Package.swift statt BrainApp.xcodeproj erstellt
- Loesung: Projekt ueber .xcodeproj oeffnen, Workflow neu erstellen
"Create Workflow" ausgegraut
- GitHub-Account in Xcode → Settings → Source Control hinzufuegen
- Projekt ueber .xcodeproj oeffnen (nicht Package.swift)
- Signing muss funktionieren (Geraet registriert, Team gesetzt)
SWIFT_TREAT_WARNINGS_AS_ERRORS — Versteckte Fehler
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES in Release-Config bedeutet: Jede Warning wird
zum Error. Xcode Cloud baut in Release-Mode.
Problem: Wenn eine Datei (z.B. OpenAIProvider.swift) eine Warning-turned-Error hat, zeigt Xcode manchmal NUR die Folge-Fehler ("Cannot find 'OpenAIProvider' in scope") statt den eigentlichen Fehler in der Quelldatei.
Diagnose: Wenn "Cannot find X in scope" erscheint, aber die Datei korrekt registriert ist (alle 3 pbxproj-Eintraege vorhanden), dann hat die Datei vermutlich einen internen Kompilierfehler (oft eine Sendable-Warning).
Extension-Targets in pbxproj (Share Extension, Widgets)
Struktur pro Extension-Target
Jedes Extension-Target braucht diese Sektionen in project.pbxproj:
PBXNativeTarget(product type:com.apple.product-type.app-extension)PBXBuildFilefuer jede Source-DateiPBXFileReferencefuer jede Source-DateiPBXGroupfuer den OrdnerPBXSourcesBuildPhasemit allen Source-FilesPBXFrameworksBuildPhasemit BrainCoreXCBuildConfiguration(Debug + Release)XCConfigurationListPBXContainerItemProxy(Dependency auf Main-App)PBXTargetDependencyPBXCopyFilesBuildPhase("Embed Foundation Extensions")
Wichtige Build-Settings fuer Extensions
SKIP_INSTALL = YES
INFOPLIST_FILE = Sources/[Target]/Info.plist (plus GENERATE_INFOPLIST_FILE = YES)
CODE_SIGN_STYLE = Automatic
CODE_SIGN_ENTITLEMENTS = Sources/[Target]/[Target].entitlements (App Group!)
PRODUCT_BUNDLE_IDENTIFIER = "$(BRAIN_BUNDLE_ID_BASE).[extension-name]"
SWIFT_EMIT_LOC_STRINGS = YES
GENERATE_INFOPLIST_FILE = YESSharedContainer fuer App Group DB
- Alle Targets die DB-Zugriff brauchen muessen
SharedContainer.swiftUNDAppIdentifiers.swiftkompilieren - App Group ID:
$(BRAIN_APP_GROUP)(=group.$(BRAIN_BUNDLE_ID_BASE), aus Base.xcconfig) — in den Entitlements jedes Targets und als Info.plist-KeyBrainAppGroupID - SharedContainer.swift + AppIdentifiers.swift in PBXBuildFile jedes Targets eintragen
Kritische Dateien
BrainApp.xcodeproj/project.pbxproj ← Projekt-Config, Signing, Build-Nummer
Config/Base.xcconfig ← Identifier-Platzhalter (committed), inkludiert Local.xcconfig
Config/Local.xcconfig ← Echte IDs (gitignored; Vorlage: Local.xcconfig.example)
BrainApp.xcodeproj/project.xcworkspace/
xcshareddata/swiftpm/Package.resolved ← SPM Lock-File (MUSS committed sein, NICHT in .gitignore!)
BrainApp.xcodeproj/xcshareddata/
xcschemes/BrainApp.xcscheme ← Shared Scheme
ci_scripts/ci_post_clone.sh ← Post-Clone Hook: schreibt Local.xcconfig aus Env-Vars, Docs-only-SkipVoraussetzungen-Checkliste
- Apple Developer Program aktiv ($99/Jahr)
Config/Local.xcconfigbefuellt (lokal) bzw. Xcode-Cloud-Env-Vars gesetzt- App IDs
<base>,<base>.share-extension,<base>.widgetsregistriert; App Groupgroup.<base>und iCloud-ContaineriCloud.<base>zugewiesen (developer.apple.com → Identifiers) - Geraet UDID registriert (developer.apple.com → Devices)
- App in App Store Connect angelegt (Name: Brain, SKU: brain-ios)
- Paid Apps Agreement akzeptiert (App Store Connect → Business)
- GitHub in Xcode Source Control verbunden
- Package.resolved committed (NICHT in .gitignore!)
- ci_post_clone.sh vorhanden und executable (Sicherheitsnetz)
Wenn $ARGUMENTS "rebuild" ist
Erhoehe die Build-Nummer in project.pbxproj, committe und pushe.
Wenn $ARGUMENTS "status" ist
Weise den User auf App Store Connect → Brain → Xcode Cloud → Builds hin.
Wenn $ARGUMENTS "fix" ist
Lies die Build-Logs und vergleiche mit den bekannten Fehlern oben.
Pruefungsreihenfolge:
- pbxproj-Eintraege: Fehlen PBXBuildFile-Eintraege fuer neue Dateien?
- PBXSourcesBuildPhase: Sind alle PBXBuildFiles in der Build-Phase gelistet?
- Unterordner-Gruppen: Haben Dateien in Unterordnern eine PBXGroup mit korrektem
path? - Package.resolved: Committed und aktuell?
- Signing-Config: Automatic, Team ID gesetzt?
- Agreements: Paid Apps Agreement aktiv?
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

