Open-source project
MobileNativeFoundation/rules_xcodeproj avatar
MobileNativeFoundation/rules_xcodeproj

rules_xcodeproj: an Xcode project generated from a Bazel workspace

Bazel rules for generating Xcode projects.

628 stars127 forksSwiftMIT

At a glance

What is it?
rules_xcodeproj is a set of Bazel rules that generate a real Xcode project from your workspace targets, so Xcode keeps its indexing, debugging and previews while Bazel stays the build system. The payoff is a working editor on a codebase that Bazel owns, and the price is a compatibility matrix you have to keep checking against your Xcode and rules_apple versions.
Who is it for?
rules_xcodeproj suits a team whose iOS or macOS codebase is already built with Bazel and whose engineers want a usable Xcode experience instead of fighting a generated build setup by hand. It is a poor fit for a project that does not use Bazel at all, and a poor fit if you are on an Xcode version outside the tested range, because the support matrix is explicit and versioned.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Swift, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A generated Xcode project that behaves like a real one

rules_xcodeproj contains rules for Bazel that can be used to generate Xcode projects from targets in your workspace. The problem it solves is the usual one: Bazel gives you reproducible builds and a consistent dependency graph, and Xcode gives you an editor that understands your code. Without a bridge, the Xcode project you hand maintain drifts from the Bazel build, and every developer ends up with a slightly different setup.

The feature list shows what the bridge aims to preserve. Full support for Xcode features is claimed, and the list is specific: indexing, meaning autocomplete, syntax highlighting and jump to definition, debugging, runtime sanitizers, inline warnings and errors, test selection and running, embedded targets such as App Clips, App Extensions and Watch Apps, dynamic frameworks, and Xcode Previews.

That list is the real product. A generated project that opens but cannot index or run tests is an inconvenience; one that does all of that is something engineers stop fighting. The design goals for the ruleset are documented separately in the design goals document in the docs directory, and the project maintains an FAQ document at `docs/faq.md` that it asks you to check before opening an issue.

The repository also records who uses it, and the list of projects is a decent argument for the approach: amo, BazelPods, Cash App, Envoy Mobile, Ergatta, Faire Wholesale, Gojek, Lyft, Mercari, Reddit, Robinhood, Slack, Snap, Spotify, Square, SwiftLint, Ten Ten, Tinder and Tokopedia. The project invites you to open a pull request to add your own.

The xcodeproj rule names the targets you want to see

Usage is a BUILD file at the root of your repository. The example loads the Apple rules, the Swift rules and the xcodeproj rules, then declares the project itself:

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

The `top_level_targets` list is what determines the shape of the generated project, and `top_level_target` lets you pass options per target, such as which target environments to include. A plain string entry such as `":Tests"` is the shorthand for a target with no extra options. The `tags = ["manual"]` attribute keeps the target out of wildcard builds, which is what you want for something that writes files into your workspace.

Your application target is an ordinary rules_apple target:

python
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"],
)

And a Swift library is a rules_swift target:

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

Once the file is in place you generate the project with:

shell
bazel run //:xcodeproj

The generated project lands in the workspace next to the BUILD file, as `App.xcodeproj`, named after the `project_name` attribute. The examples directory holds a simple iOS example, an integration example and a sanitizers example, and the docs directory has the full reference.

Focused projects keep the generated project small

The idea of focused projects is the one that decides whether this is pleasant to use day to day.

A focused project includes only a subset of your targets in Xcode. The unfocused targets are built with Bazel. In other words, the Xcode project you open is not a mirror of the entire workspace, it is a window onto the part you are working on, and the rest of the build still happens through Bazel.

Without that idea, a generated project for a large workspace is enormous: every target appears, the project navigator is unusable, and indexing has to cover the whole dependency graph before the editor is useful. The ruleset docs a separate concept of minimal configuration, pointing at the usage section, and the whole design is aimed at the stated goal that it just works.

The practical consequence for a team is that you have to decide what goes in the top level targets list and how that list changes as work moves around. A focused project is also a natural place to put a feature flag: keep a small set of top level targets for your current feature branch, and let Bazel handle the rest of the graph behind it. The `docs/faq.md` document is the place the project points people with problems, which is also where the guidance on this kind of workflow is most likely to live.

The support matrix is versioned and explicit

The compatibility table is the most useful thing in the readme, because it tells you exactly which combinations have been tested.

The current line, rules_xcodeproj 2.13.0 and later, works with Bazel 7.0 through 9.x, rules_apple 4.0.0 and later, rules_swift 3.0.2 and later, Xcode 13.3 through 16.x and macOS 13 through 15.x. Older rows walk backwards through the same fields: 2.10.0 and later with Bazel 7.0 to 9.x, rules_apple 3.16.1 and later, rules_swift 1.18.0 and later; 1.17.0 and later with Bazel 6.3 to 7.x and Xcode 13.3 through 15.x; and rows going back to 1.0 through 1.3.3 with Bazel 5.3 through 6.x, Xcode 13.3 through 14.2 and macOS 12 through 13.x.

Two details are worth pulling out. The table has a column for the supporting branch, and for the current line that is `main`, which tells you that the newest row is the one being developed rather than a frozen legacy branch. And the project says plainly that more versions of these tools and rulesets might be supported, but that the listed ones are the ones officially tested with. That is an honest statement of the difference between what works and what has been verified.

The 4.0.0 release title tells you what changed at the top end: Bazel 9 support, and dropping WORKSPACE support. If you are still on WORKSPACE, that is the version boundary you will hit.

The rules wrap the wider Bazel Apple ecosystem

The Bazel side of the project is not a single rule but a bridge over an ecosystem.

Core Bazel C, C++ and Objective-C rules are supported, as are rules_swift and rules_apple, and the readme expects your own custom rules to work as well. The repository shows that in its own structure. There is a `distribution` directory, which is where the release archive is assembled, a `xcodeproj` directory holding the rules themselves, a `shared.bazelrc` and an `xcodeproj.bazelrc` for repository level Bazel configuration, and a `bazel_9.bazelrc` for the Bazel 9 path that the 4.0.0 release added. There is a `MODULE.bazel`, which is the Bzlmod module definition, and a `.bazelversion` pinning the Bazel version the project develops against. A `.bcr` directory points to its presence in the Bazel Central Registry, and `third_party` and `tools` directories cover the vendored dependencies and the auxiliary tooling.

The project is also tidy about the Apple toolchain details that usually cause friction. There is a `.swiftformat` file and a `swiftformat` directory, a `.swiftlint.yml` and a `swiftlint` directory, and a `CODEOWNERS` file, so the Swift code in the project is held to the same formatting and linting rules it imposes on the code it processes.

The `test` directory holds the test suite, and the `examples` directory holds the simple iOS example, an integration example and a sanitizers example.

Where the code came from, and how a release is cut

The provenance section of the readme is short and gives a good sense of the project's lineage. The rules were inspired by Tulsi, the Bazel to Xcode project generator, and by the custom project generators at Target and Lyft. The work was made possible by XcodeProj, the Swift library for reading and writing Xcode project files. Initial design and development was by brentleyjones, and the logo was by pennig. The project was donated to the Mobile Native Foundation by BuildBuddy.

That last detail explains the repository layout a little. The organisation is a foundation, so the licence is MIT and the code sits in the open with a `CONTRIBUTING.md`, a `CODE_OF_CONDUCT.md` and a `MAINTENANCE.md`.

Releases are cut from the GitHub releases page, and installation is a matter of copying the Bzlmod snippet from the release you want into your own repository. If you would rather build the archive yourself, the command is `bazel build //distribution:release`, which is what the `distribution` directory is for. The release history shows 4.0.0 on 2026-03-25, 4.0.1 on 2026-04-09 and 4.1.0 on 2026-05-07, and the last push was on 2026-10-15, so the project is still being worked on even though the newest tag is several months old. There is also a `CHANGELOG.md` at the root, which is where the detail of what changed in a given version will be.

Editorial conclusion

rules_xcodeproj suits a team whose iOS or macOS codebase is already built with Bazel and whose engineers want a usable Xcode experience instead of fighting a generated build setup by hand. It is a poor fit for a project that does not use Bazel at all, and a poor fit if you are on an Xcode version outside the tested range, because the support matrix is explicit and versioned. Before you adopt it, check the compatibility table for your Bazel, rules_apple, rules_swift, Xcode and macOS versions, decide how you will use focused projects to keep the generated project small, and read the FAQ in `docs/faq.md` before filing an issue, since the common problems are already documented there.

Frequently asked questions

What does rules_xcodeproj do?

It provides Bazel rules that generate Xcode projects from targets in your workspace, so you can open the generated project in Xcode and keep indexing, debugging, sanitizers, previews and test running while Bazel remains the build system.

How do I generate the Xcode project?

Declare an `xcodeproj` rule in a root level BUILD file with `top_level_targets` listing the targets you want in the project, then run `bazel run //:xcodeproj`. The generated project appears in the workspace next to the BUILD file, named after the `project_name` attribute, for example `App.xcodeproj`.

What is a focused project in rules_xcodeproj?

It is a generated Xcode project that includes only a subset of your targets, with the unfocused targets built by Bazel instead. That keeps the project navigator and the indexing work small instead of mirroring the entire workspace.

Which Xcode and Bazel versions does rules_xcodeproj support?

The current line, 2.13.0 and later, works with Bazel 7.0 to 9.x, rules_apple 4.0.0 and later, rules_swift 3.0.2 and later, Xcode 13.3 to 16.x and macOS 13 to 15.x. Older combinations are listed in the same table, and the project states that the listed versions are the ones officially tested with.

How is rules_xcodeproj installed and released?

You copy the Bzlmod snippet from the release you want into your own repository, using the release assets on the GitHub releases page. A release archive can also be built locally with `bazel build //distribution:release`, and the project keeps a CHANGELOG.md plus a MAINTENANCE.md at the root.

Official sources

  1. Issues
  2. License: MIT
  3. MobileNativeFoundation/rules_xcodeproj on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mobilenativefoundation-rules-xcodeproj.svg)](https://hysenlabs.com/projects/mobilenativefoundation-rules-xcodeproj)

Was this useful?