Move to synchronized folders
Convert a group-based project to Xcode 16's folder format, verified by building it before and after.
Xcode 16 introduced synchronized folders. Instead of listing every file in the project, a target points at a folder and picks up whatever is inside it. That gives you fewer merge conflicts in project.pbxproj, and new files show up without being added by hand.
Older projects use groups. sochi migrate converts them, and sochi needs the new format before it can add targets.
Preview the conversion
Section titled “Preview the conversion”sochi migrate --dry-runsochi lists each folder it would convert and which targets use it. It also lists groups it would leave alone, with the reason.
Convert
Section titled “Convert”sochi migrateEach group that matches a real folder on disk becomes a synchronized folder. sochi keeps every target building exactly what it built before:
- Files a target didn’t use are listed as exceptions for that target. A file that sits in the folder but wasn’t in the project stays out of the app.
- Per-file compiler flags carry over.
.xcconfigfiles inside a converted folder stay attached to their configurations.
Before writing anything, sochi builds the project twice, once as it is and once converted. Both builds use the device SDK without signing. If the compiled sources or the files bundled into any product differ, sochi stops, lists the differences, and changes nothing.
✓ builds identically (148 compiled sources, 37 bundled files across 3 products)✓ converted 6 folders · the project now needs Xcode 16 or laterRunning migrate again changes nothing.
Groups that stay groups
Section titled “Groups that stay groups”sochi leaves a group as it is when a folder can’t express it exactly. The reasons include:
- It isn’t backed by a folder (a virtual group).
- Its folder doesn’t exist.
- A file is in a Copy Files or other build phase besides Sources and Resources.
- A file has per-file attributes, such as a public header setting.
- A subgroup doesn’t match a folder inside it.
- A file lives outside the folder, is missing on disk, or is referenced twice.
- It contains a folder reference.
Groups made only of SDK frameworks or build products aren’t reported. The Products group is never touched.
Give virtual groups real folders
Section titled “Give virtual groups real folders”sochi migrate --move-files --dry-runsochi migrate --move-files--move-files first gives each virtual or mismatched group a real folder, named after the group, inside its parent’s folder. It moves the group’s files there and updates build settings that point at them. Then it converts as usual and verifies the build. If anything differs, it moves every file back.
sochi won’t move a group when:
- The destination folder already exists.
- A file is missing, outside the project, or referenced twice.
- A file’s path is used in an
.xcconfig, a scheme or a build-phase script, which sochi won’t rewrite. - The group’s name can’t be used as a folder name.
Review the moves with git status afterwards.
Options
Section titled “Options”| Option | Effect |
|---|---|
--project <path> |
A folder or .xcodeproj. Defaults to the current folder. |
--dry-run |
Show what would change without writing. With --move-files, lists the files per folder. |
--no-verify |
Skip the two builds. The checks that don’t need a build still run. |
--force |
Convert a project that looks generated by XcodeGen or Tuist. |
--move-files |
Give virtual groups real folders by moving their files. |
If the project doesn’t build as it is, sochi can’t compare the builds. Fix the build first, or pass --no-verify to rely on the other checks only.