All articles
4 min·

The missing tool in my iOS x Claude Code workflow

How I closed the last gap in my iOS pipeline by setting up Xcode Cloud to automate build, archive and TestFlight delivery on every push.

Par Carolane Lefebvre
iOSCI/CDSwiftLint
The missing tool in my iOS x Claude Code workflow

My iOS development pipeline was working well, up to a point. I was coding with Claude Code, testing, SwiftLint validating in pre-commit, and pushing to GitHub. Up to there, smooth. But after that? I was the one manually starting the build in Xcode, archiving the project, uploading to App Store Connect, then waiting for processing on TestFlight. Every single time, by hand.

In a previous article, I admitted my pipeline stopped at git push. Today, I am completing the picture. Here is how I set up Xcode Cloud on Keepio to automate everything that comes next.

All the manual steps after a push

When you push code to GitHub, nothing happens. It is up to the developer to:

  1. Open Xcode
  2. Run a build to check the project compiles
  3. Archive it
  4. Upload to App Store Connect
  5. Wait for processing
  6. Verify availability on TestFlight

It is repetitive, error-prone, and a real waste of time, especially as a solo developer. The fix: automate everything. You push, the rest happens on its own. If something breaks, you get an alert.

Creating the Xcode Cloud workflow

Setup is done directly in Xcode through Integrate > Xcode Cloud > Create Workflow. The IDE detects the project and the GitHub repo automatically. The wizard walks you through:

  1. Product selection: the target app (Keepio here), iOS platform
  2. Repository connection: Xcode Cloud asks for GitHub access, you authorise it, and you are linked
  3. App Store Connect connection: required for TestFlight delivery

Important note: Xcode Cloud uses the version of Xcode tied to the workflow. With multiple versions installed (say, 26.2 and 26.3), you must open the right one. The "Manage Workflows" menu only appears in the Xcode version linked to the workflow, a trap that cost me time.

Configuring the trigger

The trigger defines the "when". For Keepio, I picked a build on every change to main. As soon as a PR is merged or a push lands on main, Xcode Cloud kicks off automatically.

Other options are available: build on Pull Request (to validate before merge), on tag (for releases), scheduled, or manual.

Defining the actions

The actions define the "what". Initial setup includes:

  • Build: verify the project compiles
  • Archive: prepare the binary for distribution
  • Notify: receive an email on success or failure

Post Actions are particularly powerful: you can automatically send the build to TestFlight Internal or External Testing after a successful build. Testers receive the new version with no manual step.

Custom CI scripts

Xcode Cloud lets you run custom scripts at specific moments of the build. Just create a ci_scripts/ folder at the project root. Xcode Cloud detects it, and the script names dictate when they run.

The big upside: those scripts are versioned in the repo. No hidden config sitting on a server. The whole team (or future-you in six months) can read exactly what the CI does.

Branch protection on GitHub

Last piece of the puzzle: prevent merging code that breaks the build. On GitHub, in Settings > Branches > Add rule on the main branch, the key option is Require status checks to pass before merging, picking the Xcode Cloud check.

Concretely, if the build is red, the merge is blocked. The local pre-commit hook prevents you from committing non-compliant code, branch protection prevents merging code that does not compile. Two layers of safety.

Lessons learned

A few hiccups I ran into:

  • Open the right Xcode: with multiple versions installed, the "Manage Workflows" menu only appears in the version tied to the workflow
  • Terminal vs Xcode commits: commits made from the terminal do not always trigger Xcode Cloud right away, unlike commits made from Xcode
  • First builds failing: that is normal. SwiftLint in strict mode across the whole project will probably catch things. The system is doing its job.

The full pipeline

Isolated worktree
  → Plan mode (thinking)
    → Pair programming (code)
      → Tests (manual + auto)
        → Pre-commit SwiftLint (local)
          → Push to GitHub
            → Xcode Cloud (build + SwiftLint CI)
              → TestFlight (automatic)

From idea to deployment, every step is automated. Every link has its safety net. And if something breaks, it is caught before it reaches users.

Commentaires

Connecte-toi pour laisser un commentaire.

Aucun commentaire pour le moment. Sois le premier !

More articles

.task vs .onAppear vs .refreshable en SwiftUI

iOS 26 a livré 5 améliorations discrètes en SwiftUI

safeAreaInset vs safeAreaBar en SwiftUI