Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Xcode doesn’t gather code coverage from packages when set to “some targets”. This script fixes this. Also see
# 1. Configure your Swift Package scheme to collect code coverage for "all targets" - NOT "some targets"
# 2. Test your Swift Package using either `xcodebuild test`, which should generate a `.xcresult` file.
# 3. Convert that `.xcresult` file into a `.json` file using:
# ```sh
# $ xcrun xccov view --report --json #{path_to_xcresult_file} > #{xccov_json_report}
# ```
# 4. Lastly, strip out the targets that you're not interested using this method.
# Note 1: This code considers you only want to keep 1 of the targets.
# Note 2: The resulting json file will replace/override the existing one without any warning.
# Note 3: The resulting json file will look like this:
# ```js
# {
# "coveredLines":373,
# "lineCoverage":0.979002624671916,
# "targets":[
# { … Your desired targets information will be declared here … }
# ],
# "executableLines":381
# }
# ```
# So please note that this script is only modifying the targets array, thus the "coveredLines",
# "lineCoverage" and "executableLines" will still report information relevant to all the targets.
# This is not a problem if you're using Code Climate, because Code Climate's `format-coverage`
# script will ignore those top level information and look only at the information declared for the
# target. Your mileage may vary with other Code Coverage services/tools.
def remove_all_targets_except(xccov_json_path, targets_to_keep)
require 'json'
file =
json_hash = JSON.parse(file)
targets = json_hash['targets']
targets = targets.filter do |target|
# This file has 2 targets with the name of the Swift Package, for some reason.
# One of them is invalid (it has zero files attached to it), thus generates 0% code coverage.
# Here we select the right one - the one that has files.
# If you select "some targets" in your Swift Package's scheme code coverage settings,
# you'll see 0% code coverage because it seems like Xcode picks up this "invalid" target
targets_to_keep.include?(target['name']) and !target['files'].empty?
json_hash['targets'] = targets
File.write(xccov_json_path, JSON.dump(json_hash))
# Similar to the script above, but for `swift test`. `xcodebuild test` can be ~12x slower than `swift test`, so prefer the latter.
# 1. Configure your Swift Package scheme to collect code coverage for "all targets" - NOT "some targets"
# 2. Run `swift test --enable-code-coverage` which generates `.profdata` and `.profraw` files.
# 3. Run `swift test --show-codecov-path`.strip to get the absolute path of the code coverage file.
# 4. Lastly, pass in the result from the previous step as the `lcov_json_path` argument in the method below.
def remove_files_from_unwanted_targets(lcov_json_path, unwanted_targets_regex = /\.build|\/Tests\//)
require 'json'
file =
json_hash = JSON.parse(file)
files = json_hash['data'][0]['files']
files = files.filter do |file|
file['filename'].match?(unwanted_targets_regex) ? false : true
json_hash['data'][0]['files'] = files
functions = json_hash['data'][0]['functions']
functions = functions.filter do |function|
function['filenames'][0].match?(unwanted_targets_regex) ? false : true
json_hash['data'][0]['functions'] = functions
File.write(lcov_json_path, JSON.dump(json_hash))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment