This guide is going to be a bit more complicated as it is going to deploy a compiled binary framework from a private repository to a public repository using CI/CD.
Here is the scheme of what we are going to do:
As we can see the process doesn't look that complicated except one single tiny thing that we need to do. We need to synchronize 2 repos so when a git tag is created from the private repo where the code is located the public repo gets a new GitHub release and updated Package.swift file with a fresh checksum and url.
On the private repo a new git tag will trigger the code to get tested & compiled into a XCFramework. When we have the XCFramework we are going to then get its checksum (SHA256) and update the public repos Package.swift with the checksum but there is an issue here we also need to provide a public url of where the new XCFramework has been hosted. To solve this we are going to leverage GitHub's release and artifacts and upload the XCFramework there.
Step 1: Compiling
We are going to start off by creating a basic compilation script to compile our project and create an XCFramework:
FRAMEWORK_NAME="MyFramework"
PLATFORM_LIST=("iOS" "iOS Simulator")
# Create Archives
for platform in "${PLATFORM_LIST[@]}"
do
echo "Archiving platform: $platform"
xcodebuild -verbose archive \
-workspace "$FRAMEWORK_NAME.xcworkspace" \
-scheme $FRAMEWORK_NAME \
-destination "generic/platform=$platform" \
-archivePath "path/to/archive/$platform" \
done
After we have created a standalone .framework, we need to wrap all the platforms into a single XCFramework:
echo "Creating $FRAMEWORK_NAME XCFramework..."
xcodebuild -create-xcframework \
-archive $RELEASE_DIR/$ARCHIVE_DIR/iOS.xcarchive -framework $FRAMEWORK_NAME.framework \
-archive $RELEASE_DIR/$ARCHIVE_DIR/iOS\ Simulator.xcarchive -framework $FRAMEWORK_NAME.framework \
-output $RELEASE_DIR/$FRAMEWORK_NAME.xcframework
Step 2: Store the XCFramework
We have our product that we can deploy out to the wild. Here is a demo code that uploads our compiled XCFramework and creates a GitHub release using the gh CLI:
gh release create $GIT_TAG ./Release/MyFramework -t "v${GIT_TAG}" -F ./Changelog.md
Step 3: Triggering other pipelines
We are using CircleCI as our build server. On our private build server, trigger the HTTP request to start the public build server pipeline:
curl --request POST \
--url $PUBLIC_CIRCLE_PROJECT \
--header "Circle-Token: $CIRCLECI_API_KEY" \
--header "content-type: application/json" \
--data '{"parameters":{"run-release-workflow": true, "version":"'$CIRCLE_TAG'"}}'
Step 4: Publishing
When the pipeline on the public build server gets triggered, we need to download the framework and get the SHA-256:
gh release download --repo $PRIVATE_REPO_URL $VERSION
Calculate the CHECKSUM:
echo "CHECKSUM=$(shasum -a 256 MyFramework.zip | awk '{print $1}')" >> $BASH_ENV
Make sure that Package.swift has a top variable called let version:
// swift-tools-version: 5.6
import PackageDescription
let version = "<VERSION>"
let package = Package(
targets: [
.binaryTarget(
name: "MyFramework",
url: "https://github.com/org/repo/releases/download/\(version)/MyFramework.zip",
checksum: "<CHECKSUM>"
),
]
)
Then create the release:
git add Package.swift
git commit -m "release: $VERSION [skip ci]"
git push origin main
gh release create $VERSION ./MyFramework.zip -t "v$VERSION" -F ./CHANGELOG.md
Conclusion
That's all, folks. We have successfully created two pipelines that both validate our work, update all manifests, and publish them whenever we push out a new git tag.
Break Zero