Tips on 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:
- Swift Data init with heap buffer from C and free in Swift
- DispatchQueue QoS Order Explained Best Practices & Common Pitfalls to Avoid
- iOS RUNNINGBOARD 0xdead10cc crash troubleshooting
- iOS objc_retain crash troubleshooting
- Fastlane Best Practices for iOS and Troubleshooting Tips
- SwiftUI: How to Tap to Select All Text in a TextField on iOS
- Direct Download Link for All Xcode Releases/Beta, Compilers and SDK Information
- Guide: Jailbreaking Apple TV 4K
- iOS Security: App security in iOS and iPadOS - code signing, entitlement and sandbox etc.
- Jailbreak iPhone 8 iOS 16.2 with palera1n and use frida dump to decrypt ipa
- Use frida and objection to penetration test iOS app security
- Troubleshooting: iOS auto layout warning about UIView-Encapsulated-Layout-Height
- Troubleshooting: loading carthage framework error: dyld: Library not loaded: @rpath/...
- What's new in SwiftUI iOS 16.4 beta 2
References
- Building Swift packages or apps that use them in continuous integration workflows
- Swift Package Manager build times in a continuous integration platform
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