# xcov: turning Xcode coverage files into an HTML report a CI job can publish

> xcov is a Ruby command line tool that reads the .xccoverage and .xccovreport files Xcode produces during a test run and renders them as HTML, JSON, or Markdown, with an ignore list, a coverage threshold that fails the build, and optional posting to Slack or Coveralls. It is built on fastlane, so it drops straight into an existing fastlane pipeline.

**fastlane-community/xcov** — Nice code coverage reporting without hassle

- Repository: https://github.com/fastlane-community/xcov
- Website: https://rubygems.org/gems/xcov
- Stars: 573 · Forks: 113
- Language: Ruby
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/fastlane-community-xcov

## The input is an Xcode file, which fixes the scope of the tool

xcov is described as a friendly visualizer for Xcode's code coverage files, and the repository description says nice code coverage reporting without hassle. The scope is fixed by the input format. xcov analyzes the `.xccoverage` and `.xccovreport` files created after running your tests, so before executing xcov you need to run your tests with either `Xcode`, `xcodebuild`, or fastlane's scan action. There is no other coverage source. That single constraint decides who should use this. An iOS team running its tests on a Mac with Xcode installed already has those files lying in the Derived Data directory, and xcov turns them into something a human can read. A team whose coverage comes from another tool, or from a Swift build on Linux, gets nothing from it.

## Three scheme settings decide whether you have anything to report

The Requirements section lists what has to be true before xcov can run, and two of the four are Xcode settings rather than software. You must use Xcode 7 or above, and have the latest version of the Xcode command line tools. Then you set your project scheme as shared, and enable the Gather coverage data setting available on your scheme settings window. Both of those are easy to miss, and the shared scheme requirement in particular trips up teams that generate their schemes or keep them local to a developer machine. If the scheme is not shared, the coverage data may not exist for the scheme you are pointing at, and xcov will have nothing to parse. This is the part of the setup worth doing carefully the first time.

## One command produces the report, and the parameters are explicit

The Usage section gives the whole invocation in one line:

```bash
xcov -w LystSDK.xcworkspace -s LystSDK -o xcov_output
```

The three values are the workspace, the scheme, and the output directory. The parameter list is long and well specified, and the ones that matter most for a real pipeline are the threshold and the ignore list. `--minimum_coverage_percentage` raises an exception if the overall coverage percentage is under the value, so it can fail a build rather than merely report a number. `--ignore_file_path` points at a file listing what to exclude. There are also target filters, with `--exclude_targets` and `--include_targets` taking comma separated lists, and `--only_project_targets` to display coverage only for main project targets and skip Pods targets. There is a note that all paths you provide should be absolute and unescaped, which is a small but real trap in a CI environment where the working directory moves.

## The ignore file is a gitignore style list, and it is case-insensitive

Coverage reports that include generated code or third party frameworks are close to useless, so the ignore list is where a lot of the value of the tool is. You add filenames to the ignore file specified with the `--ignore_file_path` parameter, and that file is `.xcovignore` by default. Wildcard expressions work for groups of files, and to ignore everything in a directory you give the directory's relative path, and you should also specify `source_directory` if that differs from the working directory, which is the default value. Every filename must be prefixed with the dash symbol, and you can comment lines by prefixing them with `#`:

```yaml
# Api files
- LSTSessionApi.swift
- LSTComponentsApi.swift
- LSTNotificationsApi.swift

# Managers
- LSTRequestManager.m
- LSTCookiesManager.m

# Utils
- LSTStateMachine.swift

# Exclude all files ending by "View.swift"
- .*View.swift

# Exclude all dependencies
- Pods
- Carthage/Checkouts
```

One behaviour to know before you rely on it: ignores are handled case-insensitively, so `Pods` will match any of `pods`, `PODS`, or `Pods`.

## Reports come as HTML, JSON, or Markdown, and the HTML one is on by default

The report formats are selected with three separate flags, and the default matters. `--html_report` enables the creation of an HTML report and is enabled by default, while `--json_report` and `--markdown_report` enable the JSON and Markdown reports and are not on unless you ask. So the shortest useful invocation gives you a browsable page and nothing else, and a CI job that wants to diff coverage over time or feed a bot wants the JSON or the Markdown. There is also `--disable_coveralls` to switch off the automatic submission to Coveralls, and `--skip_slack` to avoid publishing results on Slack. The report is described as a nice HTML report with the screenshot in the README showing a per-file breakdown, and the JSON output is what most pipelines would actually consume.

## Slack and Coveralls are integrations, not the point of the tool

Two integrations are documented in detail. For Slack, `--slack_url` takes the Incoming WebHook for the group, `--slack_channel` is where results are posted, and `--slack_username` and `--slack_message` control how they appear, with `--skip_slack` to suppress it. The message is published together with a successful report, which means a failed coverage threshold stops the report rather than posting a red number. For Coveralls, you create an account and set the options `coveralls_service_name` and `coveralls_service_job_id` for compatible CI environments, and to post from the console or any custom environment you set the `coveralls_repo_token` option instead. The Coveralls compatibility is listed among the headline features, and it is the part that turns a report into a tracked trend.

## Fastlane and Danger are the intended integration paths

The tool is built on top of fastlane, which the README says means you can easily plug it on to your CI environment. Fastlane 1.61.0 includes xcov as a custom action, and the usage is a single action call:

```ruby
xcov(
  workspace: "YourWorkspace.xcworkspace",
  scheme: "YourScheme",
  output_directory: "xcov_output"
)  
```

The Danger plugin is the other documented path, and with it you receive your coverage reports directly on your pull requests. That is a real workflow improvement rather than a cosmetic one, because coverage that appears in the pull request conversation is coverage that gets read. The repository layout backs the fastlane claim, with a `fastlane/` directory alongside `lib/`, `bin/`, `tasks/`, `views/`, `spec/`, and `examples/`, plus a `Gemfile`, a `Rakefile`, and an `xcov.gemspec`.

## Where xcov is the wrong tool

Two limits are structural rather than fixable. The first is the Xcode dependency, and it is absolute. xcov parses the format Xcode produces, and there is no adapter for another tool's output, so a project that standardises on a different coverage generator cannot use it, and a project with a mixed Swift and Kotlin or Swift and Go codebase can only use it on the Xcode side. The second is the format churn. The `--legacy_support` option exists to enable parsing coverage reports generated by Xcode 9.2 or previous versions, which tells you the format has changed before, and the project has to keep up with a moving target that Apple controls. A team that wants coverage reporting that does not depend on a vendor format, or that needs a report format that Xcode does not produce, needs a different tool.

## Conclusion

xcov fits an iOS team that already runs tests on CI, already has Xcode coverage data on disk, and wants a readable report plus a hard coverage gate without writing the parsing themselves. It is the wrong tool if you do not run Xcode, since there is no other coverage source, and it is the wrong tool for a multi platform codebase where Swift on Linux or a different CI image produces no xccoverage files at all. Before you adopt it, set the scheme as shared and enable Gather coverage data, since xcov cannot read anything without those two settings, and read the `--legacy_support` note if you are still on an old Xcode, because the coverage file format has changed over time.

## FAQ

### What does xcov need before it can produce a report?

You must run your tests first with `Xcode`, `xcodebuild`, or fastlane's scan action so that `.xccoverage` and `.xccovreport` files exist. You also need Xcode 7 or above, the latest Xcode command line tools, your project scheme set as shared, and the Gather coverage data setting enabled on that scheme.

### How do I run xcov on a project?

You pass the workspace, the scheme, and an output directory, for example `xcov -w LystSDK.xcworkspace -s LystSDK -o xcov_output`. The README notes that all paths you provide should be absolute and unescaped.

### How does xcov ignore files from the coverage report?

You list them in the file given to `--ignore_file_path`, which is `.xcovignore` by default. Each entry is prefixed with a dash, comments start with `#`, and wildcard expressions are supported, such as `- .*View.swift` or a directory name like `Pods`. Ignores are handled case-insensitively.

### Can xcov fail a build when coverage is too low?

Yes. The `--minimum_coverage_percentage` option raises an exception if the overall coverage percentage is under the value you set, for example 75.0, which lets a CI job fail on a coverage regression.

### Which report formats does xcov produce?

The `--html_report` option enables the HTML report and is enabled by default, while `--json_report` and `--markdown_report` enable the JSON and Markdown reports. You can also post to Slack with `--slack_url` and `--slack_channel`, and submit to Coveralls unless you pass `--disable_coveralls`.

## Sources

- [fastlane-community/xcov on GitHub](https://github.com/fastlane-community/xcov)
- [License: MIT](https://github.com/fastlane-community/xcov/blob/master/LICENSE)
- [Project website](https://rubygems.org/gems/xcov)
- [README](https://github.com/fastlane-community/xcov/blob/master/README.md)
- [Releases](https://github.com/fastlane-community/xcov/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/fastlane-community-xcov
