How to Run SwiftFormat Automatically: Pre-Commit Hook and Xcode Build Phase

Automate SwiftFormat so your Swift code is formatted without anyone remembering to do it, using a Git pre-commit hook or an Xcode build phase.

Why Automate Formatting

Running a formatter manually works until someone forgets, and then inconsistently formatted code sneaks into the repository. Automation removes the human step so formatting simply always happens.

The two most popular places to automate SwiftFormat for iOS work are a Git pre-commit hook and an Xcode build phase. Each catches formatting at a different moment, and you can use one or both.

A pre-commit hook acts before code is recorded in history, while a build phase acts as part of your normal development loop. They are not mutually exclusive, and many teams layer them.

This guide walks through both approaches, along with the trade-offs, so you can choose what fits your team.

The underlying tool is the same SwiftFormat command-line binary in every case. What changes is only when and how that binary gets invoked, so whatever you learn about running it applies everywhere you automate it.

Prerequisite: A Working CLI Install

Every automation approach here relies on the SwiftFormat command-line tool being installed and runnable. Before wiring anything up, confirm that `swiftformat --version` prints a version number in your terminal.

If it does not, resolve the installation first. Automating a tool that is not correctly installed just moves the failure into your commit flow or build, where it is more disruptive.

A broken hook that blocks every commit is far more painful than a formatter you occasionally forget to run, so get the basic install solid before adding automation on top.

It is also wise to have a committed .swiftformat configuration in place first. That way the automation applies the same rules everyone agreed on, rather than the raw defaults.

Without a shared config, each machine could format slightly differently depending on tool version, which reintroduces exactly the inconsistency you are trying to eliminate. Lock down the config, then automate.

Option A: A Git Pre-Commit Hook

A pre-commit hook runs automatically every time someone commits, formatting the code before it is recorded. This guarantees that what lands in history is already formatted.

Git supports hooks natively through scripts in your repository's hooks directory, but those are local and not shared automatically. Every developer would have to install the script by hand, which is fragile.

To distribute a hook across a team, many projects use the popular pre-commit framework, which manages hooks from a configuration file checked into the repo. Because the configuration is versioned, cloning the repo and running a one-time setup gives everyone the same hook.

Whichever mechanism you choose, the hook's job is simply to invoke SwiftFormat on the relevant files. Restricting it to staged files keeps each commit fast rather than formatting the entire project every time.

Test it by making a deliberately misformatted change and committing, then confirming the code comes out formatted. That quick end-to-end test proves the hook actually runs before you rely on it day to day.

Considerations for Commit Hooks

Pre-commit hooks are powerful but have subtleties. A hook that reformats files during the commit changes what you are committing, so make sure your workflow handles the newly modified files correctly.

If the hook edits a file after it was staged, you need to decide whether those edits are re-staged automatically or whether the commit should stop and ask you to review. Getting this wrong leads to commits that do not actually contain the formatted version.

Another approach is a check-only hook that fails the commit if code is not formatted, rather than reformatting it for you. This is less magical and can be easier to reason about, since the developer stays in control of the actual edit.

Because native Git hooks live outside the tracked tree, teams often prefer the pre-commit framework specifically so the hook definition is shared and versioned.

Document whichever choice you make so contributors know what to expect when they commit. A surprising hook that silently rewrites files can be just as frustrating as no automation at all if nobody knows it exists.

Option B: An Xcode Build Phase

You can also run SwiftFormat as part of building your app by adding a Run Script build phase to your Xcode target. Every time the project builds, the script runs SwiftFormat.

This keeps formatting tied to the normal development loop. Developers do not need to remember anything; the build itself handles it.

Because almost every meaningful action in iOS development involves a build, this catches formatting drift very frequently and very naturally. You essentially get formatting as a side effect of work you were already doing.

There is a design decision to make here. A build phase that rewrites source files while you build can feel surprising, and some teams prefer a build phase that only warns when code is not formatted rather than editing files mid-build.

Both are valid; choose based on how much automatic rewriting your team is comfortable with. A warning-only phase is often a gentler introduction, since it surfaces problems without changing files out from under the developer.

Considerations for Build Phases

A build phase adds a small amount of time to every build, so keep the script efficient, ideally formatting only what is necessary. On large projects this matters more, because a slow script taxes every single build across the team.

Build phases are also project-specific configuration stored in the Xcode project. Make sure the change is committed so every contributor's build behaves the same way.

If the build phase lives only on your machine, you get the benefit and nobody else does, which defeats the purpose of team-wide consistency. Commit the project change and confirm a teammate sees the same behaviour.

Finally, a build phase that hard-fails the build on unformatted code can be frustrating during rapid iteration. Many teams reserve the strict, blocking check for continuous integration and keep the local build phase gentler.

Decide deliberately rather than defaulting to the most aggressive setting. A build phase that blocks you mid-experiment trains people to disable it, which is the opposite of what you want.

Enforcing in Continuous Integration

The most robust place to enforce formatting is your CI pipeline. Add a step that runs SwiftFormat in a check-only mode and fails the build if any file would be reformatted.

This acts as a safety net. Even if a local hook is bypassed or someone commits from an unconfigured machine, CI catches unformatted code before it merges.

CI is authoritative in a way local tooling can never be, because it runs in a controlled environment that no individual developer can quietly skip. That makes it the natural place for the final, non-negotiable gate.

Combining a local convenience layer with a strict CI gate gives you the best of both. Developers get automatic formatting while they work, and the repository is protected by an authoritative check that cannot be skipped.

Because CI runs the same command-line tool, aligning its configuration with your local .swiftformat file ensures the check agrees with what developers see on their own machines and nobody is surprised by a failure they could not reproduce.

Choosing Your Setup

For most iOS teams, a pragmatic combination works well: a local hook or build phase for convenience, plus a strict check in CI for enforcement. Start simple and add layers as needed.

There is no need to adopt every mechanism on day one. Begin with the single approach that fits your workflow, confirm it is stable, and add the CI gate once the basics are solid.

Whatever you choose, keep the developer experience in mind. Automation should reduce friction, not create surprising failures that interrupt people mid-task.

If your automation becomes annoying, people will find ways around it, and unreliable enforcement is worse than none. Aim for automation that quietly does its job and rarely gets in the way.

As always, remember the limits. This automation guarantees consistently formatted code across your team, but it is purely about source style. It does not build a shippable product, sign anything, or interact with the App Store, which remain the responsibility of Xcode and your Apple Developer Program membership.

Frequently Asked Questions

How do I run SwiftFormat automatically before each commit?

Add a Git pre-commit hook that invokes SwiftFormat. Native Git hooks are local only, so many teams use the pre-commit framework to share and version the hook across the repository.

Can SwiftFormat run when I build in Xcode?

Yes. Add a Run Script build phase to your target that invokes SwiftFormat. You can have it reformat files or only warn when code is not formatted, depending on your team's preference.

Should the automation reformat files or just check them?

Both are valid. Reformatting is convenient but changes files during commit or build, while a check-only approach keeps developers in control. Many teams use gentle local automation plus a strict check in CI.

Where is the safest place to enforce formatting?

Continuous integration. A CI step that runs SwiftFormat in check-only mode and fails on unformatted code cannot be bypassed the way a local hook can.

Does automating SwiftFormat help me ship my app?

No. It only ensures consistent formatting. Building, signing, and submitting to the App Store still require Xcode and an Apple Developer Program membership.