Swift Package Manager binary distribution of Bitcoin Core libsecp256k1 for
Apple platforms.
This package exists because SwiftPM is good at distributing Swift packages, but
Apple apps still need a correctly sliced, module-map-backed XCFramework when
they consume libsecp256k1 as C. CSecp256k1 gives Swift projects a small,
auditable package URL instead of asking each app to vendor a binary framework or
repeat the C build machinery.
The C source is not forked or patched. Releases pin upstream
bitcoin-core/secp256k1, build a static-library XCFramework in GitHub Actions,
publish the binary as a release asset, and record the SwiftPM checksum in
Package.swift.
- iOS 16.0 and newer
- macOS 13.0 and newer
The release artifact includes:
- iOS device
arm64 - iOS simulator
arm64andx86_64 - macOS
arm64andx86_64
Mac Catalyst and visionOS slices are not built. A Catalyst or visionOS target that links this package will fail at link time; file an issue if you need those slices added.
Swift Package Manager consumes packages directly from Git repositories, so the
GitHub URL is the package address. Add it to Package.swift:
.package(
url: "https://github.com/reallyme/CSecp256k1",
from: "0.1.0"
)Add the product to your target:
.target(
name: "YourTarget",
dependencies: [
.product(name: "CSecp256k1", package: "CSecp256k1"),
]
)Import the Clang module:
import CSecp256k1Minimal smoke test:
import CSecp256k1
let context = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_NONE))
if let context {
secp256k1_context_destroy(context)
}The upstream library is pinned at bitcoin-core/secp256k1 v0.7.1.
Enabled modules (each pinned explicitly in the build script, so upstream default changes cannot alter the artifact):
- ECDH
- ECDSA public-key recovery
- extrakeys
- schnorrsig
- MuSig2
- ElligatorSwift
The binary artifact is produced from the pinned upstream source in
vendor/secp256k1. The generated .xcframework and .zip are release
artifacts only; they are intentionally not committed.
Every release asset carries a build provenance attestation generated by the Release workflow. Verify that the zip you downloaded was built by CI from this repository:
gh attestation verify CSecp256k1.xcframework.zip --repo reallyme/CSecp256k1SwiftPM independently enforces the checksum in Package.swift when it
downloads the binary target, so a tampered asset fails resolution.
Each release also attaches a build-info.txt recording the runner image,
Xcode version, upstream secp256k1 commit, and the workflow run that produced
the artifact.
git submodule update --init --recursive
scripts/build_xcframework.shOutputs are written to out/:
CSecp256k1.xcframeworkCSecp256k1.xcframework.zipCSecp256k1.xcframework.checksum
The script finishes with a smoke test that compiles and runs a Swift program against the macOS slice through the Clang module.
Note that a locally built zip will not match the released checksum: the zip bytes depend on file timestamps and the exact toolchain, which is why releases are built only in CI (see below). Local builds are for development and verification of the build process, not for producing release assets.
Verify the slices:
lipo -info \
out/CSecp256k1.xcframework/ios-arm64/libsecp256k1.a \
out/CSecp256k1.xcframework/ios-arm64_x86_64-simulator/libsecp256k1.a \
out/CSecp256k1.xcframework/macos-arm64_x86_64/libsecp256k1.aExpected architectures:
ios-arm64: arm64
ios-arm64_x86_64-simulator: arm64 x86_64
macos-arm64_x86_64: arm64 x86_64
Releases are cut by CI, never by hand. The Release workflow is the source of truth for the binary, its checksum, and the manifest that references it:
Before the first release, create a fine-grained GitHub token scoped only to
reallyme/CSecp256k1 with Contents: Read and write, Actions: Read-only,
and Metadata: Read-only, then store it as the repository secret
CSECP256K1_RELEASE_TOKEN. The ReallyMe organization keeps the default
GITHUB_TOKEN read-only, so the workflow uses this narrow token to commit the
CI-generated checksum and create the release tag.
- Run the Release workflow (Actions → Release → Run workflow) with the
version, for example
0.1.0. Novprefix. - CI builds the XCFramework on a pinned runner image and pinned Xcode.
- CI computes the SwiftPM checksum of the zip it just built and patches the
urlandchecksuminPackage.swift. - CI commits that manifest to
mainand creates the release tag on that commit, so the tag SwiftPM resolves always matches the asset it downloads. - CI attests build provenance for the zip and publishes the GitHub Release
with the zip, the checksum file, and
build-info.txt.
Do not push release tags manually and do not edit the url or checksum in
Package.swift by hand; the workflow refuses to overwrite an existing tag.
Do not commit the generated .xcframework or .zip. They belong in GitHub
Releases.
The Upstream Watch workflow checks weekly whether
bitcoin-core/secp256k1 has published a newer release tag than the pinned
submodule. It only reports drift; it never updates the submodule and never cuts
a package release.
Check the latest upstream release:
git -C vendor/secp256k1 fetch --tags origin
git -C vendor/secp256k1 tag --sort=-v:refname | headMove to a new upstream release:
git -C vendor/secp256k1 checkout <tag>
git add vendor/secp256k1
scripts/build_xcframework.shReview upstream release notes, update the pinned ref recorded in NOTICE and
this README, commit, then cut a release with the Release workflow.
ReallyMe package files, build scripts, and release automation are licensed under Apache-2.0.
Bitcoin Core libsecp256k1 is licensed under MIT. Its license text is included
at vendor/secp256k1/COPYING.