Skip to main content

Overview

Migrating an existing Xcode project to Tuist allows you to gain better control over your project structure, enable powerful features like binary caching, and simplify dependency management. This guide walks you through the migration process step by step.
Manual migration is an excellent opportunity to clean up accumulated complexity in your Xcode projects. Your team and Xcode will benefit from a simpler, more consistent project structure.

Before You Begin

Prerequisites

  • Xcode installed on your Mac
  • Tuist CLI installed (curl -Ls https://install.tuist.io | bash)
  • Basic familiarity with your current project structure
  • Backup of your existing project (commit all changes to git)

Understanding the Benefits

Migrating to Tuist provides:
  • Consistency: Projects are defined declaratively and remain simple over time
  • Faster builds: Enable binary caching and selective testing
  • Better collaboration: Fewer merge conflicts in project files
  • Dependency management: Unified approach to external dependencies
  • Graph validation: Automatic detection of dependency cycles and issues

Migration Process

1
Step 1: Create Project Scaffold
2
First, create the basic Tuist structure in your project root:
3
Tuist.swift
import ProjectDescription

let tuist = Tuist()
Project.swift
import ProjectDescription

let project = Project(
    name: "MyApp-Tuist",
    targets: [
        // Targets will go here
    ]
)
Tuist/Package.swift
// swift-tools-version: 5.9
import PackageDescription

#if TUIST
    import ProjectDescription

    let packageSettings = PackageSettings(
        productTypes: [:]
    )
#endif

let package = Package(
    name: "MyApp",
    dependencies: [
        // Add your dependencies here
    ]
)
4
Add the -Tuist suffix to your project name to prevent conflicts with the existing Xcode project. You can remove it after completing the migration.
5
Step 2: Set Up CI Validation
6
Extend your continuous integration pipeline to build and test the Tuist-generated project. This ensures each migration step is valid:
7
name: Tuist Validation

on: [pull_request]

jobs:
  validate:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Tuist
        run: curl -Ls https://install.tuist.io | bash
      - name: Install dependencies
        run: tuist install
      - name: Generate project
        run: tuist generate
      - name: Build project
        run: xcodebuild build -workspace MyApp-Tuist.xcworkspace -scheme MyApp-Tuist
8
Step 3: Extract Build Settings to XCConfig Files
9
Extract project-level build settings to make the project leaner:
10
mkdir -p xcconfigs/
tuist migration settings-to-xcconfig -p MyApp.xcodeproj -x xcconfigs/MyApp-Project.xcconfig
11
Update your Project.swift to reference the xcconfig file:
12
import ProjectDescription

let project = Project(
    name: "MyApp-Tuist",
    settings: .settings(configurations: [
        .debug(name: "Debug", xcconfig: "./xcconfigs/MyApp-Project.xcconfig"),
        .release(name: "Release", xcconfig: "./xcconfigs/MyApp-Project.xcconfig"),
    ]),
    targets: [
        // Targets will go here
    ]
)
13
Add a CI check to ensure build settings remain in xcconfig files:
14
tuist migration check-empty-settings -p MyApp-Tuist.xcodeproj
15
Step 4: Extract Package Dependencies
16
Move all Swift Package Manager dependencies to Tuist/Package.swift:
17
let package = Package(
    name: "MyApp",
    dependencies: [
        .package(url: "https://github.com/Alamofire/Alamofire", from: "5.0.0"),
        .package(url: "https://github.com/onevcat/Kingfisher", .upToNextMajor(from: "7.12.0")),
    ]
)
18
Override the product type for specific packages using productTypes in PackageSettings. By default, Tuist treats all packages as static frameworks.
19
Step 5: Determine Migration Order
20
List targets sorted by their number of dependencies:
21
tuist migration list-targets -p MyApp.xcodeproj
22
Migrate from the top of the list (most depended upon) to the bottom. This ensures dependencies are available as you migrate dependent targets.
23
Step 6: Migrate Targets
24
For each target:
25
Extract Target Build Settings
26
tuist migration settings-to-xcconfig -p MyApp.xcodeproj -t TargetName -x xcconfigs/TargetName.xcconfig
27
Define the Target
28
Add the target definition to Project.swift:
29
.target(
    name: "TargetName",
    destinations: .iOS,
    product: .framework,
    bundleId: "com.example.targetname",
    sources: ["Sources/TargetName/**"],
    resources: ["Resources/TargetName/**"],
    dependencies: [
        .external(name: "Alamofire"),
        .target(name: "OtherTarget")
    ],
    settings: .settings(configurations: [
        .debug(name: "Debug", xcconfig: "./xcconfigs/TargetName.xcconfig"),
        .release(name: "Release", xcconfig: "./xcconfigs/TargetName.xcconfig"),
    ])
)
30
Validate the Target
31
After defining each target:
32
tuist generate
xcodebuild build -workspace MyApp-Tuist.xcworkspace -scheme TargetName
33
Use xcdiff to compare the generated project with the original to verify correctness.
34
Create Pull Request
35
Create a separate PR for each target migration to facilitate code review and testing.
36
Step 7: Migrate Test Targets
37
For each test target, follow the same process as regular targets:
38
.target(
    name: "TargetNameTests",
    destinations: .iOS,
    product: .unitTests,
    bundleId: "com.example.targetname.tests",
    sources: ["Tests/TargetNameTests/**"],
    dependencies: [
        .target(name: "TargetName"),
        .xctest
    ],
    settings: .settings(configurations: [
        .debug(name: "Debug", xcconfig: "./xcconfigs/TargetNameTests.xcconfig"),
        .release(name: "Release", xcconfig: "./xcconfigs/TargetNameTests.xcconfig"),
    ])
)
39
Validate tests run correctly:
40
tuist test TargetNameTests
41
Step 8: Complete the Migration
42
Once all targets are migrated:
43
  • Remove the -Tuist suffix from the project name
  • Update CI/CD pipelines to use Tuist commands
  • Delete the old .xcodeproj file
  • Update team documentation
  • Troubleshooting

    Compilation Errors Due to Missing Files

    If files aren’t contained in directories matching the target structure:
    Ensure the list of files after generation matches the original Xcode project. Use this opportunity to align file structure with target structure.
    Compare source lists:
    # List files in original target
    xcodebuild -project MyApp.xcodeproj -target TargetName -showBuildSettings | grep SOURCES
    
    # List files in generated project
    tuist generate
    xcodebuild -project MyApp-Tuist.xcodeproj -target TargetName -showBuildSettings | grep SOURCES
    

    Build Settings Conflicts

    If you encounter conflicts between project and target settings:
    1. Review the extracted .xcconfig files
    2. Ensure inheritance is correct using $(inherited)
    3. Remove duplicate settings between project and target levels

    Dependency Graph Cycles

    Tuist validates the dependency graph and will error on cycles:
    tuist graph
    
    Review the graph visualization to identify and break circular dependencies.

    External Dependencies Not Found

    If external dependencies aren’t resolving:
    # Force reinstall dependencies
    rm -rf Tuist/Dependencies
    tuist install
    tuist generate
    

    Test Target Configuration

    If tests fail after migration:
    1. Verify the test target has .xctest dependency
    2. Check that test host configuration matches the original
    3. Ensure test resources are properly included

    Migration Checklist

    Use this checklist to track your migration progress:
    • Create Tuist scaffold files
    • Set up CI validation pipeline
    • Extract project build settings to xcconfig
    • Extract package dependencies
    • Determine target migration order
    • Migrate each target (create separate PRs)
      • Extract target build settings
      • Define target in Project.swift
      • Validate builds successfully
      • Migrate associated test target
      • Validate tests pass
    • Update CI/CD pipelines
    • Remove old .xcodeproj file
    • Update team documentation

    Next Steps

    After completing the migration:

    Enable Binary Caching

    Speed up builds with Tuist’s binary caching feature

    Optimize Project Structure

    Learn best practices for organizing your Tuist projects

    Manage Dependencies

    Master dependency management with Tuist

    Set Up CI/CD

    Configure continuous integration for your Tuist project