Docs

Xcode and Swift coverage

Use Xcode Cloud to export coverage from the test result bundle, convert it to coverage/lcov.info, and upload the LCOV report to 9to5 coverage.

Enable coverage

In Xcode, edit the scheme used by Xcode Cloud and enable code coverage for the test action. Add COVERAGE_UPLOAD_TOKEN as an Xcode Cloud secret before the workflow runs.

Generate LCOV

Xcode Cloud exposes the test result bundle at CI_RESULT_BUNDLE_PATH. Use xcrun xccov to read that bundle and write LCOV for upload.

mkdir -p coverage
xcrun xccov view --report --json "$CI_RESULT_BUNDLE_PATH" > coverage/xccov-report.json
xcrun xccov view --file-list "$CI_RESULT_BUNDLE_PATH"

Xcode Cloud build script

Create ci_scripts/ci_post_xcodebuild.sh in the repository root. The script runs after Xcode Cloud finishes the test action, converts xcrun xccov output to LCOV, and uploads coverage/lcov.info with the existing multipart API.

#!/bin/zsh
set -euo pipefail

if [[ "${CI_XCODE_CLOUD:-}" != "TRUE" ]]; then
  exit 0
fi

if [[ "${CI_XCODEBUILD_ACTION:-}" != "test"* ]]; then
  exit 0
fi

if [[ -z "${CI_RESULT_BUNDLE_PATH:-}" || ! -d "$CI_RESULT_BUNDLE_PATH" ]]; then
  echo "CI_RESULT_BUNDLE_PATH is missing; skipping coverage upload."
  exit 0
fi

if [[ -z "${COVERAGE_UPLOAD_TOKEN:-}" ]]; then
  echo "COVERAGE_UPLOAD_TOKEN is missing; skipping coverage upload."
  exit 0
fi

cd "$CI_PRIMARY_REPOSITORY_PATH"
mkdir -p coverage

xcrun xccov view --report --json "$CI_RESULT_BUNDLE_PATH" > coverage/xccov-report.json

{
xcrun xccov view --file-list "$CI_RESULT_BUNDLE_PATH" | while IFS= read -r source_file; do
    echo "SF:${source_file}"
    xcrun xccov view --file "$source_file" "$CI_RESULT_BUNDLE_PATH" \
      | sed -nE 's/^[[:space:]]*([0-9]+):[[:space:]]*([0-9]+|\*):.*/DA:\1,\2/p' \
      | sed 's/,\*/,0/'
    echo "end_of_record"
  done
} > coverage/lcov.info

curl -fsS -X POST "https://coverage.9to5.software/api/v1/repos/OWNER/REPO/coverage" \
  -H "Authorization: Bearer ${COVERAGE_UPLOAD_TOKEN}" \
  -F "commit_sha=${CI_COMMIT}" \
  -F "branch=${CI_BRANCH}" \
  -F "run_url=${CI_BUILD_URL:-}" \
  -F "coverage=@coverage/lcov.info"

Pull request behavior

Xcode Cloud uploads use the same API as other CI systems. For pull_request builds from GitHub-backed repositories, 9to5 coverage attaches project and patch checks to the uploaded commit.

Troubleshooting

If the LCOV file is empty, confirm the Xcode Cloud workflow ran tests with code coverage enabled. If upload authentication fails, rotate the repository or organization upload token and update the Xcode Cloud secret.