Modern Python library and command-line tool for the new Xcode project.xcproj project format.
🚀 Project
Xcodefy reads, writes, validates, formats, and manipulates the new JSON5-based Xcode project format introduced with Xcode 27.
The project follows the model published in Apple's open-source xcode-project-format repository while providing a Python-native API and packaging. It intentionally supports only the new project.xcproj format. The legacy project.pbxproj format is outside the scope of the library.
The internal package layout mirrors the main areas of Apple's implementation:
apple/xcode-project-format xcodefy
Sources/Library/Schema -> src/xcodefy/library/schema
Sources/Library/Serialization -> src/xcodefy/library/serialization
Sources/Library/Utilities -> src/xcodefy/library/utilities
Sources/Tool -> src/xcodefy/tool
Every model class and enum lives in its own Python file. Every class has a mirrored unit test file, and the complete suite is required to maintain 100% statement and branch coverage.
✨ Features
- Read and write
project.xcproj - Accept an outer
.xcodeprojpackage or an innerproject.xcprojfile - JSON5 parser with comments, trailing commas, unquoted keys, single-quoted strings, hexadecimal numbers, and JSON5 escapes
- Canonical pretty printer with ordered output, omitted defaults, and the reference's compact and multi-line layout rules
- Complete Python model for projects, targets, references, build phases, build files, build rules, configurations, Swift packages, folder exception sets, imported products, and schema value types
- High-level project manipulation API
- Add and remove files and target membership
- Add and remove targets
- Add Swift package references
- Edit project-level and target-level build settings
- Validate project invariants and required capabilities
-
xcodefycommand-line formatter equivalent to Apple'sxcprojformatterworkflow - Zero runtime dependencies
-
uvdevelopment workflow - Wheel and source distribution ready for PyPI
- PyPI Trusted Publishing release workflow
- 100% statement and branch coverage gate
- Single-source version, managed through the Makefile
📦 Install
With pip:
pip install xcodefy
With uv:
uv add xcodefy
Xcodefy requires Python 3.11 or newer.
💡 Library usage
Load an Xcode project package:
from xcodefy.xcode_project import XcodeProject
project = XcodeProject.load("MyApp.xcodeproj")
project.set_build_setting("SWIFT_VERSION", "6.0")
project.set_build_setting("PRODUCT_BUNDLE_IDENTIFIER", "com.example.myapp", target="MyApp")
project.save()
Add a Swift source file to a target. The path is relative to the group it is added to, so this one
lands at Sources/Feature.swift:
from xcodefy.xcode_project import XcodeProject
project = XcodeProject.load("MyApp.xcodeproj")
project.add_group("Sources")
project.add_file("Feature.swift", target="MyApp", group="Sources")
project.save()
Add a local Swift package:
from xcodefy.library.schema.packages.local_swift_package import LocalSwiftPackage
from xcodefy.library.schema.packages.swift_package import SwiftPackage
from xcodefy.library.schema.packages.swift_package_location import SwiftPackageLocation
from xcodefy.xcode_project import XcodeProject
project = XcodeProject.load("MyApp.xcodeproj")
project.add_package(SwiftPackage(SwiftPackageLocation.of_local(LocalSwiftPackage("../Shared"))))
project.save()
Add a target and a file to it:
from xcodefy.library.schema.build_phases.build_phase import BuildPhase
from xcodefy.library.schema.build_phases.build_phase_kind import BuildPhaseKind
from xcodefy.library.schema.target.common_target_properties import CommonTargetProperties
from xcodefy.library.schema.target.target import Target
from xcodefy.library.schema.values.object_id import ObjectID
from xcodefy.xcode_project import XcodeProject
project = XcodeProject.load("MyApp.xcodeproj")
phases = [BuildPhase.of_kind(BuildPhaseKind.SOURCES)]
project.add_target(Target.native(CommonTargetProperties("Widget", ObjectID("W1"), build_phases=phases)))
project.add_group("Widget")
project.add_file("WidgetBundle.swift", target="Widget", group="Widget")
project.save()
The lower-level schema types are available directly from their modules. Package __init__.py files are deliberately empty, so imports always identify the concrete source module.
Reading a project and writing it back is idempotent: the output is the canonical rendering, so running Xcodefy over an already-canonical document changes nothing.
🛠️ Command-line tool
Xcodefy includes a formatter with the same input/output workflow as the tool in Apple's reference package:
xcodefy [[--input] input-project] [--output output-project]
xcodefy --update project
xcodefy --help
xcodefy --version
Update a project in place:
xcodefy --update MyApp.xcodeproj
Read from standard input and write canonical output to standard output:
cat project.xcproj | xcodefy
Format one project into another:
xcodefy --input Input.xcodeproj --output Output.xcodeproj
Both Input.xcodeproj and Input.xcodeproj/project.xcproj are valid paths.
🧪 Development
Install the locked development environment:
make install
Run the complete suite:
make test
Run the strict coverage gate:
make coverage
Run lint and formatting checks:
make lint
Build the wheel and source distribution:
make build
make check
📚 Documentation
- Getting started
- Schema overview
- Serialization
- Project manipulation
- Command-line tool
- File format goals
- Package layout
- Apple reference coverage
- Contribution
🏷️ Releasing a new version
A release is driven by a matching Git tag. The version lives only in pyproject.toml, and the
Makefile is the only thing that writes it:
make version # show the current version
make set-version VERSION=0.0.2 # set it
make bump PART=patch # or raise major, minor or patch
Commit the change, then push the matching tag:
git tag v0.0.2
git push origin v0.0.2
The release workflow runs lint and the full 100% coverage suite, verifies that the tag matches the declared package version, builds the wheel and source distribution with uv, validates both distributions with Twine, publishes to PyPI through Trusted Publishing, and creates the GitHub release.
Configure the PyPI Trusted Publisher for repository paulocoutinhox/xcodefy, workflow release.yml, and environment pypi before the first release.
☕ Buy me a coffee
Support the continuous development of this project.
📄 License
Copyright (c) 2026, Paulo Coutinho
Xcodefy is an independent project and is not affiliated with Apple Inc. Xcode, Swift, and Apple are trademarks of Apple Inc. See THIRD_PARTY_NOTICES.md.
Release files for xcodefy 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| xcodefy-0.0.1.tar.gz | 543.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xcodefy-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 651.8 kB
Release files / xcodefy-0.0.1.tar.gz
| Download URL | xcodefy-0.0.1.tar.gz |
|---|---|
| Size | 543.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e8a592b4407cdae8f103402d42304be5bbb5ca699a2f6a05e8ce9e1495173c53
|
|
BLAKE2b-256 checksum How to use checksums |
8e768b18363ff545c3143da4fe5f589165e52c3873f73c292fd848b4a4c73439
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.
Transparency logRelease files / xcodefy-0.0.1-py3-none-any.whl
| Download URL | xcodefy-0.0.1-py3-none-any.whl |
|---|---|
| Size | 107.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9edc5b5113e9bcefb561b4f1f299c69d5b6353820a1055e6c3007da2720fc3bb
|
|
BLAKE2b-256 checksum How to use checksums |
63e3b7f04c22a8c0798ccb0672f9370c0c8c01b267fcb3963a2bff36676e7002
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.
Transparency log