tuist

star 0

Manages Tuist configuration. Use when adding xcframeworks, configuring dependencies, or modifying Project.swift.

vjr2005 By vjr2005 schedule Updated 3/5/2026

name: tuist description: Manages Tuist configuration. Use when adding xcframeworks, configuring dependencies, or modifying Project.swift.

Tuist

Skill for managing Tuist configuration in the project.

MCP Server (Required)

IMPORTANT: This project has a Tuist MCP server installed. Always use the MCP tools for Tuist operations instead of running bash commands directly.

Available MCP Resources

The Tuist MCP server provides project graph information:

URI: tuist:///{project-path}
Resource: {AppName} graph
Type: application/json

Use ReadMcpResourceTool to read the project graph and understand:

  • Project structure and targets
  • Dependencies between modules
  • Build configurations

Usage

// Read project graph
ReadMcpResourceTool(
    server: "tuist",
    uri: "tuist:///{project-path}"
)

Why use MCP?

  • Provides structured JSON output of project configuration
  • Enables understanding of module dependencies
  • Integrates directly with Claude Code workflow

Note: Use ListMcpResourcesTool to see all available Tuist resources.


Project Options

The root Project.swift delegates to mainApp.project (defined in ModuleKit/App.swift). The project structure adapts based on the active module strategy set in ModuleFactory.swift.

// Project.swift
let project = mainApp.project

// App.swift — generates the project with all module targets and schemes
Project(
    name: name,
    options: .options(
        automaticSchemesOptions: .disabled,        // Manual scheme generation
        developmentRegion: "en",
        disableBundleAccessors: true,              // BundleAccessorGenerator handles this
        disableSynthesizedResourceAccessors: true   // No TuistAssets+*.swift
    ),
    packages: packages,                             // Aggregated from modules (empty for FrameworkModule)
    settings: .settings(
        base: baseSettings.merging([...]) { _, new in new },
        configurations: BuildConfiguration.all
    ),
    targets: [appTarget, uiTestsTarget] + moduleTargets,  // App + UI tests + module framework targets
    schemes: allSchemes + [uiTestsScheme] + moduleSchemes  // Environment schemes + tests + per-module
)
// Workspace.swift — coverage mode adapts to active strategy
let workspace = Workspace(
    name: mainApp.name,
    projects: ["."],
    generationOptions: .options(
        autogeneratedWorkspaceSchemes: .enabled(
            codeCoverageMode: mainApp.coverageMode,  // .targets() for Framework, .relevant for SPM
            testingOptions: []
        )
    )
)

Module Test Scheme

The Challenge (Dev) scheme aggregates all module test targets via ModuleAggregation, which dispatches on the active strategy:

  • Framework: Uses .targets(...) with aggregated testableTargets and codeCoverageTargets from each module — no test plan file is generated.
  • SPM: Generates a .xctestplan via TestPlanGenerator and uses .testPlans(...), which is the only mechanism to aggregate test targets across SPM local packages.

Focus mode: When TUIST_FOCUS_MODULES env var is set (via generate.sh --focus), ModuleAggregation filters testable and coverage targets to only include focused modules and their transitive dependents. Each FrameworkModule target is tagged with metadata: .metadata(tags: ["module:<ShortName>"]) for Tuist's tag-based generation filtering. The expansion is computed in generate.sh using tuist graph --format json to build the reverse dependency graph.

The test command is the same for both:

xcodebuild test \
  -workspace Challenge.xcworkspace \
  -scheme "Challenge (Dev)" \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro,OS=26.1'

Tests run via xcodebuild test (not tuist test) because Tuist can't resolve SPM package test targets when using .testPlans().

Why disable synthesizers?

Option Effect Reason
disableBundleAccessors No Tuist Bundle+*.swift BundleAccessorGenerator creates custom Bundle.module accessors
disableSynthesizedResourceAccessors No TuistAssets+*.swift Not using generated asset accessors

Localization Configuration

The app must declare supported languages in CFBundleLocalizations for iOS to load localizations from embedded frameworks:

// Project.swift
let appInfoPlist: [String: Plist.Value] = [
    "CFBundleLocalizations": ["en", "es"],
    // ... other settings ...
]
Key Value Description
CFBundleLocalizations ["en", "es"] Languages the app supports
developmentRegion "en" Default/fallback language (in project options)

Important: Without CFBundleLocalizations, iOS ignores localizations in embedded frameworks (like ChallengeResources) and always shows the development language.

See /resources skill for adding new languages and managing localized strings.


URL Schemes (Deep Links)

To enable the app to receive external URLs (from Safari, other apps, push notifications), configure CFBundleURLTypes in the app's Info.plist:

// Project.swift
let appInfoPlist: [String: Plist.Value] = [
    "UILaunchStoryboardName": "LaunchScreen",
    // ... other settings ...
    "CFBundleURLTypes": [
        [
            "CFBundleURLSchemes": ["challenge"],
            "CFBundleURLName": "com.app.Challenge",
        ],
    ],
]

Configuration:

Key Value Description
CFBundleURLSchemes ["challenge"] URL scheme the app responds to
CFBundleURLName "com.app.Challenge" Unique identifier for this URL type

Usage:

After configuration, the app can receive URLs like:

  • challenge://character/list
  • challenge://character/detail/42

Handle incoming URLs in RootContainerView with .onOpenURL:

.onOpenURL { url in
    appContainer.handle(url: url, navigator: navigationCoordinator)
}

See /navigator skill for deep link implementation details.


Derived Folder

Tuist generates files in the Derived/ folder:

Derived/
└── InfoPlists/
    ├── {AppName}-Info.plist
    └── {AppName}UITests-Info.plist

Contents: Only Info.plist files for the app and UI tests targets. Module Info.plists are managed by their respective strategy (Tuist for Framework, SPM for SPM packages).

Git: The Derived/ folder is in .gitignore.


Adding an XCFramework as a Dependency

1. XCFramework Location

Place the .xcframework file in the Tuist/Dependencies/ directory:

{AppName}/
├── Tuist/
│   ├── Dependencies/
│   │   └── FrameworkName.xcframework
│   └── ProjectDescriptionHelpers/
├── Libraries/
├── App/
└── Project.swift

Note: The Tuist/Dependencies/ directory is ignored by git. Do not commit xcframeworks to the repository.

2. Create the XCFrameworks Helper

If it doesn't exist, create the file Tuist/ProjectDescriptionHelpers/Dependencies.swift:

import ProjectDescription

public enum XCFrameworks {
  public static let frameworkName: TargetDependency = .xcframework(path: "Tuist/Dependencies/FrameworkName.xcframework")
}

3. Add the Dependency to a Target

In Project.swift, add the dependency to the target that needs it:

.target(
  name: appName,
  destinations: [.iPhone, .iPad],
  product: .app,
  bundleId: "com.app.\(appName)",
  deploymentTargets: developmentTarget,
  sources: ["App/Sources/**"],
  resources: ["App/Sources/Resources/**"],
  dependencies: [
    XCFrameworks.frameworkName,
  ]
)

4. For Internal Frameworks

If the xcframework is a dependency of an internal framework in Libraries/, update Target+Framework.swift:

public extension Target {
  static func createFramework(
    name: String,
    destinations: ProjectDescription.Destinations = [.iPhone, .iPad],
    dependencies: [TargetDependency] = []
  ) -> Self {
    let targetName = "\(appName)\(name)"
    return .target(
      name: targetName,
      destinations: destinations,
      product: .framework,
      bundleId: "${PRODUCT_BUNDLE_IDENTIFIER}.\(targetName)",
      sources: ["Libraries/\(name)/**"],
      dependencies: dependencies
    )
  }
}

Then in Project.swift:

.createFramework(
  name: "Features/UserFeature",
  dependencies: [
    XCFrameworks.frameworkName,
  ]
)

5. Regenerate the Project

tuist generate
Install via CLI
npx skills add https://github.com/vjr2005/Challenge --skill tuist
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator