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.