Package.swift on main falls back to a published xcframework when no local one
is staged. That fallback cannot hold the pairing the generated code enforces.
The binary comes from a release commit. The Swift bindings are gitignored and
generated from whatever commit you are on. UniFFI checks that pairing at
initialization, so unless the two come from the same commit the pairing is only
correct by luck.
Measured between main and the newest published asset, 0.13.1:
|
main |
tag 0.13.1 |
bindings_contract_version |
30 |
30 |
| checksum constants |
62 |
62 |
..._hostcallbacks_auth_state_changed() |
35488 |
50346 |
One value differs. That is enough, because the check is a single lazy global and
any mismatch returns apiChecksumMismatch for the whole library:
private let initializationResult: InitializationResult = {
...
if (uniffi_truapi_server_checksum_method_hostcallbacks_auth_state_changed() != 35488) {
return InitializationResult.apiChecksumMismatch
}
So it is not one degraded callback with the rest working. Every use of
TrUAPIHost fails to initialize.
Who this reaches
Almost nobody, which is why it has gone unnoticed.
|
resolves the fallback |
| CI |
no, TRUAPI_USE_LOCAL_BINARY: "1" |
| the in-tree iOS host |
no, in-tree core and a staged binary since #679 |
| a consumer pinning a semver tag |
no, it reads that tag's own manifest |
| a clean clone with bindings generated but no xcframework |
yes |
swift package resolve also downloads the 74 MB asset before the build fails on
missing FFI headers, so a clean clone pays for it and cannot use it.
What is not wrong
The consumer tags are correct and should stay as they are. Each plain semver tag
carries generated sources and a manifest pointing at its own asset:
| tag |
publishedBinaryURL |
FFI headers on the tag |
0.7.0 |
0.7.0 |
present |
0.12.0 |
0.12.0 |
present |
0.13.1 |
0.13.1 |
present |
Verified by resolving exact: "0.13.1" from a throwaway consumer package: it
fetched the 0.13.1 asset and the checksum validated. Bindings and binary on a
tag come from one commit, which is exactly why tags work. That property is the
reason to keep the fallback on tags and reconsider it only on main.
The bookkeeping problem underneath
release-ios.yml opens a pull request after each release to move main onto the
new asset. Those do not land:
#582 has no CI Status because a bot-authored pull request needs its workflows
approved, and the explicit workflow_dispatch run the release workflow triggers
does not attach as a pull request check. So main sits on 0.7.0.
Merging #582 is accurate bookkeeping and changes no behaviour: the asset and
checksum verify, and the one case that reads the fallback fails at init either
way. #556 is superseded and would walk main backwards if it merged later.
Decisions
Package.swiftonmainfalls back to a published xcframework when no local oneis staged. That fallback cannot hold the pairing the generated code enforces.
The binary comes from a release commit. The Swift bindings are gitignored and
generated from whatever commit you are on. UniFFI checks that pairing at
initialization, so unless the two come from the same commit the pairing is only
correct by luck.
Measured between
mainand the newest published asset,0.13.1:main0.13.1bindings_contract_version..._hostcallbacks_auth_state_changed()One value differs. That is enough, because the check is a single lazy global and
any mismatch returns
apiChecksumMismatchfor the whole library:So it is not one degraded callback with the rest working. Every use of
TrUAPIHostfails to initialize.Who this reaches
Almost nobody, which is why it has gone unnoticed.
TRUAPI_USE_LOCAL_BINARY: "1"swift package resolvealso downloads the 74 MB asset before the build fails onmissing FFI headers, so a clean clone pays for it and cannot use it.
What is not wrong
The consumer tags are correct and should stay as they are. Each plain semver tag
carries generated sources and a manifest pointing at its own asset:
publishedBinaryURL0.7.00.12.00.13.1Verified by resolving
exact: "0.13.1"from a throwaway consumer package: itfetched the
0.13.1asset and the checksum validated. Bindings and binary on atag come from one commit, which is exactly why tags work. That property is the
reason to keep the fallback on tags and reconsider it only on
main.The bookkeeping problem underneath
release-ios.ymlopens a pull request after each release to movemainonto thenew asset. Those do not land:
mainto 0.13.1, open since 2026-09-02mainto 0.12.0, open since 2026-08-31#582 has no
CI Statusbecause a bot-authored pull request needs its workflowsapproved, and the explicit
workflow_dispatchrun the release workflow triggersdoes not attach as a pull request check. So
mainsits on 0.7.0.Merging #582 is accurate bookkeeping and changes no behaviour: the asset and
checksum verify, and the one case that reads the fallback fails at init either
way. #556 is superseded and would walk
mainbackwards if it merged later.Decisions
mainkeep a published fallback at all, given it cannot be correctbetween releases?
requires generated bindings?
TRUAPI_USE_LOCAL_BINARY: "1"means a wrong URL or checksum is invisibleto every current job?