Skip to content

Architecture

Engineering notes for chandram-dutta/sochi. Swift 6, macOS 14+, ArgumentParser CLI + SochiKit library.

Target Role
sochi CLI: Sochi.swift, Commands.swift, EditCommands.swift, Terminal.swift, Version.swift
SochiKit Domain logic
SochiKitTests Swift Testing; many suites need a real Xcode

Dependency: apple/swift-argument-parser ≥ 1.5.0. sochiVersion is "dev" from source; release builds stamp it from the tag.

  1. No hand-maintained Apple catalogs — capabilities, product types, and templates are loaded from the selected Xcode.
  2. Remembered values are choices only (scheme, team). Build settings are always queried fresh.
  3. Edits are planned, shown as diffs, applied atomically, verified, rolled back on failure.
  4. Projects with XcodeGen/Tuist markers are refused unless --force.
  5. CLI status goes to stderr so run can leave app stdout clean for pipes.

Used by run / build:

  1. XcodeProject.locate → load ProjectState
  2. Scheme: explicit → saved → Xcodebuild.pickScheme
  3. Destination: physical device by default; --sim or named simulator
  4. Team: --team / SOCHI_TEAM / saved / recovery on signing failure
  5. buildResolving: xcodebuild into sochi DerivedData; BuildLog events; one retry on recoverable scheme/team errors
  6. Persist successful choices; prepare (boot sim) + appProduct (-showBuildSettings)
  7. AppBundle.verify → installIfNeeded (device fingerprint skip unless --fresh) → launchAttached

add / remove / entitlements call ProjectEditor.plan*. Plans read resolved entitlements / Info.plist paths from xcodebuild settings (not guessed only from pbxproj). Apply: atomic file writes → re-parse / settings checks → rollback all files on failure.

  • CapabilityCatalog / CapabilityWriter — portal JSON from Xcode; local capabilities (e.g. Background Modes → Info.plist)
  • EntitlementSchema — allowed keys/types from Xcode schemas
  • PBXProjectFile / PBXTextWriter — project.pbxproj mutation
  • XcodeSpecs — product types, embed rules from .xcspec
  • TemplateIndex / TemplateEngine / TargetBuilder — .xctemplate instantiation
  • Migration / MigrationVerifier / Reorganizer — groups → synchronized folders; optional file moves

Target add assumes folder-based projects (objectVersion ≥ 77). Use migrate on classic group projects.

  • new: ProjectCreator → template instantiate → skeleton pbxproj → TargetBuilder → xcodebuild -list verify; delete tree on failure. Remembers --org.
  • migrate: Migration.plan → optional before/after BuildInventory compare → apply. --move-files runs Reorganizer then convert.
Data Path
DerivedData ~/Library/Caches/dev.sochi/projects/<name>-<fnv1a>/DerivedData
state.json (scheme, team, installs) ~/Library/Application Support/dev.sochi/projects/<id>/
CLI SochiKit
───────────────────── ────────────────────────────────
SessionOptions / Run → Session → Xcodebuild / BuildLog
Devices | Simulators | Signing | AppBundle
EditCommands.runPlan → ProjectEditor.plan* → apply
CapabilityCatalog | TemplateEngine | PBX*
New → ProjectCreator
Migrate → Migration | Reorganizer | MigrationVerifier
▲
selected Xcode (catalogs, specs, templates)
  • Domain changes go in SochiKit; wire flags in Commands.swift / EditCommands.swift.
  • Prefer extending Xcode data loaders over hardcoding Apple lists.
  • Tests/goldens: Fixtures/Golden/Xcode-27.0/, Fixtures/Classic for migration.
  • Local release dry-run: scripts/build-release.sh 0.1.0 → dist/. Script must leave Version.swift as "dev".
  • Published releases: GitHub tag → workflow builds universal tarball + checksum → updates Formula/sochi.rb.