RSS

Tips on Use Swift Package Manager (SPM) with Continuous Integration / Delivery (CI/CD)

Tips and Troubleshooting Use Swift Package Manager (SPM) with Continuous Integration / Delivery (CI/CD).

Introduction

Continuous integration (CI) is the process of automating and streamlining the building, analyzing, testing, archiving, and publishing of your apps to ensure that they’re always in a releasable state.

Resolve Package Dependencies

xcodebuild options related resolve package dependencies:

xcodebuild -resolvePackageDependencies [-project <projectname>|-workspace <workspacename>] -clonedSourcePackagesDirPath <path>

-runFirstLaunch                         install packages and agree to the license
-clonedSourcePackagesDirPath PATH       specifies the directory to which remote source packages are fetch or expected to be found
-resolvePackageDependencies             resolves any Swift package dependencies referenced by the project or workspace
-disableAutomaticPackageResolution      prevents packages from automatically being resolved to versions other than those recorded in the `Package.resolved` file
-onlyUsePackageVersionsFromResolvedFile prevents packages from automatically being resolved to versions other than those recorded in the `Package.resolved` file
-skipPackageUpdates                     Skip updating package dependencies from their remote
-disablePackageRepositoryCache          disable use of a local cache of remote package repositories
-skipPackagePluginValidation            Skip validation of package plugins (this can be a security risk if they are not from trusted sources)
-packageCachePath                       path of caches used for package support

The easiest way to resolve package dependencies is to simply run xcodebuild -resolvePackageDependencies.

SPM Caches

There are two directories for the SPM cache.

  • ~/Library/org.swift.swiftpm
  • ~/Library/Caches/org.swift.swiftpm

~/Library/Caches/org.swift.swiftpm/repositories/ includes git clone for remote package repo.

Sometimes we may need to clear these caches before the build to make sure the build is clean.

Fastlane

Two lanes you for resolving dependencies and clear SPM cache. You may also need clear_derived_data before your build to make sure the build is clean.

  lane :resolve_package_dependencies do
    Dir.chdir("..") do
      sh("xcodebuild -resolvePackageDependencies -onlyUsePackageVersionsFromResolvedFile")
    end
  end

  lane :clear_spm_cache do
    sh("rm -rf ~/Library/org.swift.swiftpm")
    sh("rm -rf ~/Library/Caches/org.swift.swiftpm")
  end

Sample code in Fastfile to an ipa:

  lane :build do
    clear_derived_data
    clear_spm_cache
    resolve_package_dependencies
    gym(...)
  end

Private SPM dependencies

If your Xcode project have depends on private git repo. To resolve package dependencies that require authentication, or private packages, you need to provide credentials to your CI setup. In CI environment, Xcode can reuse git credential which used from clone code. The token may store in keychain, so make sure your keychain is accessible by CI scripts.

If you’re using the xcodebuild command directly, use SSH–based Git URLs for your packages and configure your SSH credentials. Set up your known_hosts file in the ~/.ssh directory of the macOS user that runs your CI tasks. xcodebuild honors your SSH configuration — there’s no additional setup required.

Tips

Commit your Package.resolved file to your Git repository

Commit your project’s Package.resolved file to your Git repository. This ensures a reliable CI workflow that always uses the expected version of a package dependency.

Use the expected version of a package dependency

To ensure the CI workflow’s reliability, make sure it uses the appropriate version of package dependencies. Xcode stores the exact version of each package dependency in a file called Package.resolved. The file automatically updates when package requirements in your Xcode project or in the Package.swift manifest file change. Commit this file to your Git repository to ensure it’s always up-to-date on the CI environment to prevent the CI from building your project with unexpected versions of package dependencies.

You can find the Package.resolved file inside your .xcodeproj directory at [appName].xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved.

In CI/CD, We want to prevent packages from automatically being resolved to versions other than those recorded in the Package.resolved file. This can be done with -onlyUsePackageVersionsFromResolvedFile

xcodebuild -resolvePackageDependencies -onlyUsePackageVersionsFromResolvedFile

It set IDEPackageOnlyUseVersionsFromResolvedFile = YES.

Example:

$ xcodebuild -resolvePackageDependencies  -onlyUsePackageVersionsFromResolvedFile
Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -resolvePackageDependencies -onlyUsePackageVersionsFromResolvedFile

User defaults from command line:
    IDEPackageOnlyUseVersionsFromResolvedFile = YES
    IDEPackageSupportUseBuiltinSCM = YES

Resolve Package Graph
...
...

resolved source packages: ...

Here is another option -disableAutomaticPackageResolution. You might be wondering: What’s the difference?

The answser is they are they same. From xcodebuild -help shows the same description.

-disableAutomaticPackageResolution        prevents packages from automatically being resolved to versions other than those recorded in the `Package.resolved` file
-onlyUsePackageVersionsFromResolvedFile   prevents packages from automatically being resolved to versions other than those recorded in the `Package.resolved` file

I also tried to play with -disableAutomaticPackageResolution and it have the same output with -onlyUsePackageVersionsFromResolvedFile.

You can compare this output with above output with -onlyUsePackageVersionsFromResolvedFile, both set IDEPackageOnlyUseVersionsFromResolvedFile = YES:

$ xcodebuild -resolvePackageDependencies  -disableAutomaticPackageResolution
Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -resolvePackageDependencies -disableAutomaticPackageResolution

User defaults from command line:
    IDEPackageOnlyUseVersionsFromResolvedFile = YES
    IDEPackageSupportUseBuiltinSCM = YES

Resolve Package Graph
...

Troubleshooting

Could not resolve package dependencies due to could not read Username for git server

Symptom:

xcodebuild: error: Could not resolve package dependencies:
  Couldn’t fetch updates from remote repositories:
    fatal: could not read Username for 'https://some-git-server.com': terminal prompts disabled

Root Cause Analyze:

This is indicate there is no git credentienal, incorrect or token expired (if use token like bitbucket SCM token.)

To verify the issue, run git pull should get password promot:

$ git pull
Password for 'https://some-git-server':

Keychain UI may also popup in this scenario.

Solution:

Use git ssh based authentication to avoid authentication issue. e.g. change from https:// to git@.

Related pages:

References

OmniLock - Block / Hide App on iOS

Block distractive apps from appearing on the Home Screen and App Library, enhance your focus and reduce screen time.

DNS Firewall for iOS and Mac OS

Encrypted your DNS to protect your privacy and firewall to block phishing, malicious domains, block ads in all browsers and apps

Ad