Skip to content

Instantly share code, notes, and snippets.

Last active August 25, 2022 22:27
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