DEV Community

ArshTechPro
ArshTechPro

Posted on

Understanding rules_xcodeproj: Generate Xcode Projects from Bazel

What is rules_xcodeproj?

rules_xcodeproj is a Bazel ruleset that automatically generates Xcode projects from your BUILD files. Its used by major companies including Slack, Spotify, Cash App, Lyft, Reddit and many others.

The tool translates your Bazel build definitions into native Xcode projects that provide full IDE functionality while still using Bazel for the actual building. This means you get Bazel's reproducible builds and caching combined with Xcode's excellent development experience.

Key Features

Full Xcode IDE Support:

  • Indexing (autocomplete, syntax highlighting, jump to definition)
  • Debugging with breakpoints and variable inspection
  • Runtime sanitizers (Address Sanitizer, Thread Sanitizer)
  • Inline compiler warnings and errors
  • Test selection and execution
  • SwiftUI Previews
  • Embedded targets (App Clips, Extensions, Watch Apps)

Focused Projects:
One standout feature is focused projects. For large codebases, you can include only a subset of targets in Xcode while Bazel builds the rest in the background. This dramatically improves Xcode performance.

Broad Compatibility:

  • Core Bazel C/C++/Objective-C rules
  • rules_swift
  • rules_apple
  • rules_ios
  • Most custom rules

Installation

The latest stable version is 3.5.1

Using Bzlmod (Modern)

Add to your MODULE.bazel:

bazel_dep(name = "rules_xcodeproj", version = "3.5.1")
Enter fullscreen mode Exit fullscreen mode

Using WORKSPACE (Legacy)

Add to your WORKSPACE:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "rules_xcodeproj",
    integrity = "sha256-3Dhy+1DRa7598DXqIurE950JVwNuIm1t+d5d44lDQ5M=",
    url = "https://github.com/MobileNativeFoundation/rules_xcodeproj/releases/download/3.5.1/release.tar.gz",
)

load(
    "@rules_xcodeproj//xcodeproj:repositories.bzl",
    "xcodeproj_rules_dependencies",
)

xcodeproj_rules_dependencies()

load("@bazel_features//:deps.bzl", "bazel_features_deps")

bazel_features_deps()

load(
    "@build_bazel_rules_apple//apple:repositories.bzl",
    "apple_rules_dependencies",
)

apple_rules_dependencies()

load(
    "@build_bazel_rules_swift//swift:repositories.bzl",
    "swift_rules_dependencies",
)

swift_rules_dependencies()

load(
    "@build_bazel_rules_swift//swift:extras.bzl",
    "swift_rules_extra_dependencies",
)

swift_rules_extra_dependencies()

load(
    "@build_bazel_apple_support//lib:repositories.bzl",
    "apple_support_dependencies",
)

apple_support_dependencies()
Enter fullscreen mode Exit fullscreen mode

Basic Usage Example

Here's a complete iOS app setup in a root-level BUILD file:

load(
    "@build_bazel_rules_apple//apple:ios.bzl",
    "ios_application",
    "ios_unit_test",
)
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
load(
    "@rules_xcodeproj//xcodeproj:defs.bzl",
    "top_level_target",
    "xcodeproj",
)

xcodeproj(
    name = "xcodeproj",
    project_name = "App",
    tags = ["manual"],
    top_level_targets = [
        top_level_target(":App", target_environments = ["device", "simulator"]),
        ":Tests",
    ],
)

ios_application(
    name = "App",
    bundle_id = "com.example.app",
    families = ["iphone", "ipad"],
    infoplists = [":Info.plist"],
    minimum_os_version = "15.0",
    visibility = ["//visibility:public"],
    deps = [":Lib"],
)

swift_library(
    name = "Lib",
    srcs = glob(["src/*.swift"]),
)

ios_unit_test(
    name = "Tests",
    bundle_id = "com.example.tests",
    minimum_os_version = "15.0",
    test_host = ":App",
    visibility = ["//visibility:public"],
    deps = [":TestLib"],
)

swift_library(
    name = "TestLib",
    srcs = glob(["test/*.swift"]),
)
Enter fullscreen mode Exit fullscreen mode

Generate the Project

bazel run //:xcodeproj
Enter fullscreen mode Exit fullscreen mode

This creates App.xcodeproj in your workspace. Open it:

open App.xcodeproj
Enter fullscreen mode Exit fullscreen mode

Understanding the Configuration

xcodeproj rule attributes:

  • name: Target name for generation command
  • project_name: Name of the generated .xcodeproj file
  • tags = ["manual"]: Prevents building during regular bazel commands
  • top_level_targets: Main targets to include in Xcode

top_level_target function:

  • Wraps targets with additional configuration
  • target_environments: Specify "device", "simulator", or both
  • Ensures targets build for appropriate platforms

Important Concepts

BUILD Files Are Source of Truth

Your BUILD files remain the source of truth. Xcode uses Bazel to build in the background, so changes to BUILD files require regenerating the project.

When to Regenerate

Regenerate the project when:

  • Adding new targets or dependencies
  • Changing build settings affecting project structure
  • Modifying the dependency graph

You don't need to regenerate for:

  • Editing source code
  • Minor changes within existing targets

Bazel Configuration

rules_xcodeproj uses special Bazel configurations isolated from your regular builds:

  • rules_xcodeproj: Base configuration
  • rules_xcodeproj_generator: Project generation
  • rules_xcodeproj_indexbuild: Background indexing
  • rules_xcodeproj_swiftuipreviews: SwiftUI previews

To run Bazel commands matching Xcode's environment:

bazel run //:xcodeproj -- build //path/to:target
Enter fullscreen mode Exit fullscreen mode

Working with Multiple Configurations

Define Debug and Release configurations:

xcodeproj(
    name = "xcodeproj",
    project_name = "App",
    xcode_configurations = {
        "Debug": {
            "//command_line_option:compilation_mode": "dbg",
        },
        "Release": {
            "//command_line_option:compilation_mode": "opt",
        },
    },
    top_level_targets = [
        top_level_target(":App", target_environments = ["device", "simulator"]),
    ],
)
Enter fullscreen mode Exit fullscreen mode

Troubleshooting

Xcode Version Issues

If you see "Xcode version must be specified":

sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
sudo xcodebuild -license
bazel sync --configure
Enter fullscreen mode Exit fullscreen mode

Changes Not Appearing

  1. Determine if you need to regenerate the project
  2. Clean Xcode's derived data (Product → Clean Build Folder)
  3. Verify Bazel is rebuilding the affected targets

Why rules_xcodeproj?

Before rules_xcodeproj, tools like Tulsi existed but didn't fully satisfy modern iOS development needs. rules_xcodeproj was designed from the ground up to:

  1. Provide complete Xcode feature support
  2. Require minimal configuration
  3. Generate natural-feeling Xcode projects
  4. Support focused projects for large codebases
  5. Work reliably with all major Apple platform rules

The tool successfully achieves all these goals, which is why it has been adopted by so many major companies.

Comparison with Other Tools

Unlike tools that proxy Xcode's build system or require extensive configuration:

  • rules_xcodeproj works directly with Xcode
  • Minimal configuration required (often just the example above)
  • All major Xcode features work out of the box
  • Active development with regular updates
  • Production-ready with strong community support

Resources

For issues or questions, check existing GitHub issues or file a new one. The community is active and responsive.

Top comments (0)