# xcodeproj-creator

Generate Xcode `.xcodeproj` bundles from scratch — no macOS required.

Build valid Xcode projects on Linux (or any platform) for iOS/macOS apps,
then hand them off to Xcode or CI services like Codemagic for building.
Zero dependencies — stdlib only.

## Installation

```bash
pip install xcodeproj-creator
```

## CLI

```bash
# Minimal — creates MyApp.xcodeproj in current directory
xcodeproj-create MyApp --bundle-id com.example.myapp

# Full options
xcodeproj-create MyApp \
    --bundle-id com.example.myapp \
    --output MyApp.xcodeproj \
    --deployment-target 18.0 \
    --swift-version 5.0 \
    --team-id ABCDEF1234 \
    --sources MyApp/ \
    --resources MyApp/Resources/ \
    --asset-catalogs MyApp/Assets.xcassets

# Modern Xcode 16+ project (objectVersion 77, auto-syncs files from disk)
xcodeproj-create MyApp \
    --bundle-id com.example.myapp \
    --object-version 77
```

### CLI Options

| Option | Description | Default |
|---|---|---|
| `name` | Project and target name | (required) |
| `--bundle-id` | Bundle identifier | (required) |
| `--output`, `-o` | Output path | `<name>.xcodeproj` |
| `--deployment-target` | iOS deployment target | `18.0` |
| `--swift-version` | Swift language version | `5.0` |
| `--team-id` | Development team ID | (none) |
| `--sources` | Directories to scan for `**/*.swift` | (none) |
| `--resources` | Directories to scan for resources | (none) |
| `--asset-catalogs` | Asset catalog paths (`.xcassets`) | (none) |
| `--product-type` | Xcode product type | `com.apple.product-type.application` |
| `--object-version` | pbxproj format: `56` (classic) or `77` (modern file-sync) | `56` |

## Python API

```python
from xproject_creator import ProjectBuilder

# Classic project (objectVersion 56) — list files explicitly
project = ProjectBuilder("MyApp", bundle_id="com.example.myapp")
project.set_deployment_target("18.0")
project.set_swift_version("5.0")
project.set_development_team("ABCDEF1234")
project.add_swift_sources("MyApp/", glob="**/*.swift")
project.add_resources("MyApp/Resources/")
project.add_asset_catalog("MyApp/Assets.xcassets")
project.add_shell_script_phase("Lint", "swiftlint")
project.set_build_setting("ENABLE_BITCODE", "NO")
project.save("MyApp.xcodeproj")

# Modern project (objectVersion 77) — files auto-sync from disk
project = ProjectBuilder("MyApp", bundle_id="com.example.myapp", object_version="77")
project.set_development_team("ABCDEF1234")
project.save("MyApp.xcodeproj")
```

### Builder Methods

| Method | Description |
|---|---|
| `set_deployment_target(version)` | Set minimum iOS version |
| `set_swift_version(version)` | Set Swift language version |
| `set_development_team(team_id)` | Set code signing team |
| `set_build_setting(key, value, config=)` | Set a build setting (optionally per-config) |
| `add_swift_source(path)` | Add a single Swift file |
| `add_swift_sources(dir, glob=)` | Add Swift files matching a glob |
| `add_resource(path)` | Add a single resource file |
| `add_resources(dir, glob=)` | Add resource files from a directory |
| `add_asset_catalog(path)` | Add an `.xcassets` catalog |
| `add_shell_script_phase(name, script)` | Add a shell script build phase |
| `add_file_sync_group(path)` | Add a file-sync root group (v77 only) |
| `save(output_path)` | Write the `.xcodeproj` bundle to disk |

All methods return `self` for chaining (except `save`).

### What Gets Generated

```
MyApp.xcodeproj/
  project.pbxproj                          # Full ASCII plist
  xcshareddata/
    xcschemes/
      MyApp.xcscheme                       # Build + Run scheme
```

The generated project includes:
- Debug and Release build configurations with sensible defaults
- Sources, Resources, and Frameworks build phases
- Auto-generated Info.plist (via `GENERATE_INFOPLIST_FILE = YES`)
- Proper group hierarchy mirroring the filesystem (v56) or file-system-synchronized groups (v77)

### objectVersion 56 vs 77

| | v56 (classic) | v77 (modern, Xcode 16+) |
|---|---|---|
| File tracking | Each file listed individually in the project | Auto-synced from disk via `PBXFileSystemSynchronizedRootGroup` |
| Adding files | Use `add_swift_source()`, `add_resources()`, etc. | Just `save()` — Xcode discovers files automatically |
| Compatibility | All Xcode versions | Xcode 16+ only |

### Supported Product Types

| Type | Identifier |
|---|---|
| iOS App (default) | `com.apple.product-type.application` |
| Framework | `com.apple.product-type.framework` |
| Static Library | `com.apple.product-type.library.static` |
| Unit Test Bundle | `com.apple.product-type.bundle.unit-test` |
| UI Test Bundle | `com.apple.product-type.bundle.ui-testing` |
| App Extension | `com.apple.product-type.app-extension` |

## Development

```bash
make install        # Create venv and install with dev deps
make check          # Format + lint + typecheck + test
make test           # pytest with coverage
make lint           # ruff check
make typecheck      # mypy strict
make format         # ruff format + autofix
```

## License

MIT
