Using AI for CI & Automation in iOS Projects

Nov 11 2026 · Swift 5, iOS 27, Xcode 27

Lesson 03: Interpreting Feedback, Deciding Value, and Understanding the Limits

Demo

Episode complete

Play next episode

Next

Written by the author, AI-editedAI-generated, author-verified How this module was made

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Welcome back. In the previous lessons we configured three agentic review agents and compared their comments on the same pull request. Today we turn those experiments into a sustainable production practice. We begin from the same sample iOS repository. The Starter folder remains the root of the repository and is committed as the main branch so that GitHub Actions discovers every workflow file we add or modify.

name: AI Code Review
on:
  pull_request:
    types: [opened, synchronize, reopened]
    paths:
      - '**/*.swift'
      - '**/*.{m,h,mm}'
      - '**/*.xcodeproj/**'
      - '**/*.xcworkspace/**'
      - 'CODE_REVIEW.md'
      - 'CI_GUIDANCE.md'
      - '.github/scripts/**'
      - '.github/workflows/ai-code-review.yml'
env:
  CI_GUIDANCE: CI_GUIDANCE.md
concurrency:
  group: ai-review-${{ github.event.pull_request.number }}
  cancel-in-progress: true
jobs:
  review:
    if: github.event.pull_request.draft == false
    runs-on: ubuntu-latest
    timeout-minutes: 10
    permissions:
      contents: read
      pull-requests: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7
        with:
          fetch-depth: 0
      - name: Set up Node.js
        uses: actions/setup-node@v7
        with:
          node-version: '24'
      - name: Install GitHub Copilot CLI
        run: npm install -g @github/copilot
      - name: Run Copilot review
        env:
          COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          COPILOT_OTEL_ENABLED: true
          COPILOT_OTEL_EXPORTER_TYPE: file
        run: |
          ./.github/scripts/ai-code-review.sh "${{ github.base_ref }}" \
            --review-file review.md \
            --otel-file "${{ runner.temp }}/copilot-otel.jsonl" \
            --guidance "${{ env.CI_GUIDANCE }}"
      - name: Upload token log
        uses: actions/upload-artifact@v7
        if: always()
        with:
          name: copilot-token-log
          path: ${{ runner.temp }}/copilot-otel.jsonl
          if-no-files-found: ignore
      - name: Post review comment
        if: success()
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
        run: ./.github/scripts/post-review-comment.sh review.md "AI Code Review"
name: Claude Code Review
on:
  pull_request:
    types: [opened, synchronize, reopened]
    paths:
      - '**/*.swift'
      - '**/*.{m,h,mm}'
      - '**/*.xcodeproj/**'
      - '**/*.xcworkspace/**'
      - 'CODE_REVIEW.md'
      - 'CI_GUIDANCE.md'
      - '.github/scripts/**'
      - '.github/workflows/claude-code-review.yml'
env:
  CI_GUIDANCE: CI_GUIDANCE.md
concurrency:
  group: claude-review-${{ github.event.pull_request.number }}
  cancel-in-progress: true
jobs:
  claude-review:
    if: github.event.pull_request.draft == false
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      contents: read
      pull-requests: write
      # Federated authentication for this agent only.
      id-token: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7
        with:
          fetch-depth: 0
      - name: Build review prompt
        id: prompt
        run: |
          {
            echo 'text<<PROMPT_EOF'
            ./.github/scripts/build-review-prompt.sh claude \
              "${{ github.base_ref }}" \
              "${{ github.repository }}" \
              "${{ github.event.pull_request.number }}" \
              --guidance "${{ env.CI_GUIDANCE }}"
            echo 'PROMPT_EOF'
          } >> "$GITHUB_OUTPUT"
      - name: Run Claude Code review
        id: claude
        uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          github_token: ${{ secrets.GITHUB_TOKEN }}
          prompt: ${{ steps.prompt.outputs.text }}
          claude_args: |
            --model claude-sonnet-5
            --max-turns 20
            --allowedTools "Read,Glob,Grep,Bash(git *)"
      - name: Print token usage
        if: always() && steps.claude.outputs.execution_file != ''
        run: ./.github/scripts/claude-token-usage.sh "${{ steps.claude.outputs.execution_file }}"
      - name: Extract review text
        if: success() && steps.claude.outputs.execution_file != ''
        run: |
          jq -r '
            [.[] | select(.type == "result" and .subtype == "success")]
            | last
            | .result // empty
          ' "${{ steps.claude.outputs.execution_file }}" > claude-review.md
      - name: Post review comment
        if: success()
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
        run: ./.github/scripts/post-review-comment.sh claude-review.md "Claude Code Review"
name: Codex Review
on:
  pull_request:
    types: [opened, synchronize, reopened]
    paths:
      - '**/*.swift'
      - '**/*.{m,h,mm}'
      - '**/*.xcodeproj/**'
      - '**/*.xcworkspace/**'
      - 'CODE_REVIEW.md'
      - 'CI_GUIDANCE.md'
      - '.github/scripts/**'
      - '.github/workflows/codex-review.yml'
env:
  CI_GUIDANCE: CI_GUIDANCE.md
concurrency:
  group: codex-review-${{ github.event.pull_request.number }}
  cancel-in-progress: true
jobs:
  codex-review:
    if: github.event.pull_request.draft == false
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      contents: read
      pull-requests: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7
        with:
          fetch-depth: 0
      - name: Build review prompt
        run: ./.github/scripts/build-review-prompt.sh codex "${{ github.base_ref }}" --guidance "${{ env.CI_GUIDANCE }}" > review-prompt.txt
      - name: Run Codex review
        id: codex
        uses: openai/codex-action@v1
        with:
          openai-api-key: ${{ secrets.OPENAI_API_KEY }}
          codex-home: ${{ runner.temp }}/codex-home
          prompt-file: review-prompt.txt
          output-file: codex-review.md
      - name: Print token usage
        if: always()
        run: ./.github/scripts/codex-token-usage.sh "${{ runner.temp }}/codex-home"
      - name: Post review comment
        if: success()
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
        run: ./.github/scripts/post-review-comment.sh codex-review.md "Codex Review"
name: Token Dashboard CI
on:
  workflow_dispatch:
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - main

jobs:
  build:
    name: Build
    runs-on: macos-latest
    timeout-minutes: 20
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7
      - name: Build
        run: |
          cd TokensDashboard
          xcodebuild -project TokensDashboard.xcodeproj \
            -scheme TokensDashboard \
            -destination 'platform=iOS Simulator,name=iPhone 17' \
            build

  test:
    name: Test
    runs-on: macos-latest
    timeout-minutes: 20
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7
      - name: Test
        run: |
          cd TokensDashboard
          xcodebuild -project TokensDashboard.xcodeproj \
            -scheme TokensDashboard \
            -destination 'generic/platform=iOS Simulator' \
            test
# Review jobs (Copilot and Codex) — no provider key in the CI workflow.
permissions:
  contents: read
  pull-requests: write

# Claude job only — keep id-token write for federated authentication.
permissions:
  contents: read
  pull-requests: write
  id-token: write

# Build and test jobs — no pull-request write, no provider secrets.
permissions:
  contents: read
# CI_GUIDANCE.md — Starter (before)

Treat the CODE_REVIEW.md contents below as the review criteria.

# CI_GUIDANCE.md — after the feedback-loop edit

Treat the CODE_REVIEW.md contents below as the authoritative review policy.
- Follow its priority order exactly. MainActor and actor-isolation correctness come first.
- Suppress any finding the "Do not flag" section marks as out of scope, including unused helpers or force-unwraps this diff does not call.
- Match the preferred comment style, including file:line references and a minimal fix. Do not include process preamble.
# Each review workflow already parameterises the file:
env:
  CI_GUIDANCE: CI_GUIDANCE.md

# Copilot
./.github/scripts/ai-code-review.sh "${{ github.base_ref }}" \
  --guidance "${{ env.CI_GUIDANCE }}"

# Claude / Codex
./.github/scripts/build-review-prompt.sh … --guidance "${{ env.CI_GUIDANCE }}"
See forum comments
Cinema mode Download course materials from Github
Previous: Instruction Next: Conclusion