diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ce29297..79fde47 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -109,6 +109,12 @@ jobs:
- name: Validate packages
run: dotnet test tests/DatadogNet.iOS.PackageTests --logger 'trx;LogFileName=package-tests.trx'
+ # After the merge, on the finished artifact - the per-pass intermediates legitimately lack
+ # the other band's frameworks, so validating them against the published baseline reports
+ # every band-to-band difference as a break. See the script header.
+ - name: Validate package API against the published baseline
+ run: ./build/ValidatePackageApi.sh
+
- name: Upload packages
uses: actions/upload-artifact@v4
with:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index efb38e4..cdb94c5 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -46,6 +46,36 @@ jobs:
echo "prerelease=${prerelease}" >> "$GITHUB_OUTPUT"
echo "Releasing ${version} (prerelease=${prerelease})" >> "$GITHUB_STEP_SUMMARY"
+ # The tag names the dd-sdk-ios line to bind; Directory.Build.props names the line this
+ # branch is developed and tested against. When they disagree, the release would publish
+ # packages binding a native SDK the committed ApiDefinitions.cs were never ported to - or
+ # simply a mistyped tag - so it stops here, before anything builds. Fix the tag (delete and
+ # re-push) or bump the props. Releasing a different line on purpose - v2.30.2.3 from a main
+ # that has moved on to 3.x - is still possible: set the repository variable
+ # RELEASE_ALLOW_VERSION_MISMATCH to 'true' for that release, and unset it afterwards.
+ - name: Check the tag against Directory.Build.props
+ if: vars.RELEASE_ALLOW_VERSION_MISMATCH != 'true'
+ run: |
+ version="${{ steps.resolve.outputs.version }}"
+ native="${{ steps.resolve.outputs.native-version }}"
+ props_native=$(sed -n 's:.*\(.*\).*:\1:p' Directory.Build.props | head -1)
+ props_revision=$(sed -n 's:.*\(.*\).*:\1:p' Directory.Build.props | head -1)
+
+ if [ "${native}" != "${props_native}" ]; then
+ echo "::error::tag ${GITHUB_REF_NAME} binds dd-sdk-ios ${native}, but Directory.Build.props has DatadogNativeVersion ${props_native}. Fix the tag or bump the props; for a deliberate different-line release, set the RELEASE_ALLOW_VERSION_MISMATCH repository variable to 'true'."
+ exit 1
+ fi
+
+ # The fourth component is this repository's binding revision. A tag may omit it, and a
+ # prerelease carries a suffix; both are stripped before comparing.
+ revision=$(printf '%s' "${version%%-*}" | cut -s -d. -f4)
+ if [ -n "${revision}" ] && [ "${revision}" != "${props_revision}" ]; then
+ echo "::error::tag ${GITHUB_REF_NAME} is binding revision ${revision}, but Directory.Build.props has DatadogBindingRevision ${props_revision}. Fix the tag or bump the props; for a deliberate mismatch, set the RELEASE_ALLOW_VERSION_MISMATCH repository variable to 'true'."
+ exit 1
+ fi
+
+ echo "Tag agrees with Directory.Build.props (${props_native}, revision ${props_revision})" >> "$GITHUB_STEP_SUMMARY"
+
build:
name: build
needs: version
diff --git a/.github/workflows/upstream-watch.yml b/.github/workflows/upstream-watch.yml
new file mode 100644
index 0000000..31b7dd8
--- /dev/null
+++ b/.github/workflows/upstream-watch.yml
@@ -0,0 +1,66 @@
+name: upstream watch
+
+# Opens an issue when Datadog publishes a dd-sdk-ios release newer than the one these bindings
+# are built against, so an upgrade starts as a tracked task rather than a discovery. Weekly is
+# plenty for an SDK that releases every few weeks; workflow_dispatch is for checking on demand.
+
+on:
+ schedule:
+ - cron: '30 6 * * 1'
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ issues: write
+
+jobs:
+ check:
+ name: compare against the latest dd-sdk-ios release
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Open an issue if upstream is ahead
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ current=$(sed -n 's:.*\(.*\).*:\1:p' Directory.Build.props | head -1)
+ if [ -z "${current}" ]; then
+ echo "::error::could not read DatadogNativeVersion from Directory.Build.props"
+ exit 1
+ fi
+
+ # Upstream tags releases with the bare version - no v prefix.
+ latest=$(gh api repos/DataDog/dd-sdk-ios/releases/latest --jq .tag_name)
+ echo "bound: ${current}, upstream latest: ${latest}" >> "$GITHUB_STEP_SUMMARY"
+
+ if [ "${latest}" = "${current}" ]; then
+ echo "Up to date." >> "$GITHUB_STEP_SUMMARY"
+ exit 0
+ fi
+
+ # sort -V puts the newer of the two last. This also covers upstream being *behind* -
+ # a yanked or re-cut latest release - which is not an upgrade prompt.
+ if [ "$(printf '%s\n%s\n' "${current}" "${latest}" | sort -V | tail -1)" != "${latest}" ]; then
+ echo "Upstream ${latest} is not newer than ${current}; nothing to do." >> "$GITHUB_STEP_SUMMARY"
+ exit 0
+ fi
+
+ # One open issue per upstream version, however many Mondays it stays open. Matched on
+ # the exact title rather than a search expression, which matches loosely.
+ title="dd-sdk-ios ${latest} is available"
+ if gh issue list --state open --limit 100 --json title --jq '.[].title' | grep -Fxq "${title}"; then
+ echo "An open issue for ${latest} already exists; nothing to do." >> "$GITHUB_STEP_SUMMARY"
+ exit 0
+ fi
+
+ cat > "${RUNNER_TEMP}/issue-body.md" <
> "$GITHUB_STEP_SUMMARY"
diff --git a/Directory.Build.props b/Directory.Build.props
index 6e9cfa1..0032cf7 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -16,9 +16,16 @@
impossible to tell which Datadog release a given CrashReporter package belonged to.
-->
3.14.0
- 3
+ 4
$(DatadogNativeVersion).$(DatadogBindingRevision)
+
+ 3.14.0.3
+
s.bokatuk
s.bokatuk
Copyright © s.bokatuk
diff --git a/README.md b/README.md
index 16ac598..a710c09 100644
--- a/README.md
+++ b/README.md
@@ -103,8 +103,8 @@ OS-provided Swift runtime, ABI-stable from 12.2.
```xml
-
-
+
+
```
@@ -113,8 +113,8 @@ restore them:
```xml
-
-
+
+
```
@@ -127,9 +127,11 @@ restore them:
## Usage
The C# names are the Objective-C selectors projected into C#. Each feature is in its own namespace
-now — one `using` per module. [`samples/DatadogNet.iOS.Example`](samples/DatadogNet.iOS.Example) is
-a working MAUI app that does all of the below;
-[`Datadog.cs`](samples/DatadogNet.iOS.Example/Datadog.cs) is the setup in one file.
+now — one `using` per module.
+[`samples/DatadogNet.iOS.Example`](https://github.com/sbokatuk/DatadogNet.iOS/tree/main/samples/DatadogNet.iOS.Example)
+is a working MAUI app that does all of the below;
+[`Datadog.cs`](https://github.com/sbokatuk/DatadogNet.iOS/blob/main/samples/DatadogNet.iOS.Example/Datadog.cs)
+is the setup in one file.
### Initialize
@@ -394,7 +396,8 @@ all, so there is nothing for a binding to bind:
callable API. They exist so the SDK is mirrored and so a future projection needs no new package.
There is a way around this — a hand-written Swift `@objc` wrapper, which we can compile ourselves —
-and a working prototype for Flags lives in [`shims/DatadogFlagsObjc/`](shims/DatadogFlagsObjc/).
+and a working prototype for Flags lives in
+[`shims/DatadogFlagsObjc/`](https://github.com/sbokatuk/DatadogNet.iOS/tree/main/shims/DatadogFlagsObjc).
Nothing ships yet.
---
@@ -453,13 +456,14 @@ DataDog/dd-sdk-ios release ──► Datadog.xcframework.zip (one archive, el
There is no Carthage step and nothing is compiled from source. Datadog publishes the built
xcframeworks as a release asset, so the build downloads one archive, verifies it against a hash
-pinned in [`build/checksums.txt`](build/checksums.txt), and strips it to the two iOS slices.
+pinned in [`build/checksums.txt`](https://github.com/sbokatuk/DatadogNet.iOS/blob/main/build/checksums.txt),
+and strips it to the two iOS slices.
**Why the two-pass build.** Each .NET SDK's iOS workload supports the current target framework and
the previous one — the .NET 9 band builds net8 + net9, the .NET 10 band builds net9 + net10. No
single SDK builds all three, so `BuildNugets.sh` packs twice and
-[`merge-packages.py`](build/merge-packages.py) grafts the net10 assets and dependency groups into
-the net9 packages.
+[`merge-packages.py`](https://github.com/sbokatuk/DatadogNet.iOS/blob/main/build/merge-packages.py)
+grafts the net10 assets and dependency groups into the net9 packages.
### Layout
@@ -498,7 +502,7 @@ dotnet test tests/DatadogNet.iOS.PackageTests
Run the on-simulator smoke tests against the packed packages:
```bash
-./.github/scripts/run-simulator-tests.sh 3.14.0.3 net9.0-ios18.0
+./.github/scripts/run-simulator-tests.sh 3.14.0.4 net9.0-ios18.0
```
Build and run the sample:
@@ -510,32 +514,38 @@ dotnet build samples/DatadogNet.iOS.Example/DatadogNetExample.csproj -p:RuntimeI
> Building anything that targets `net10.0-ios26.0` needs Xcode **26.0** specifically — .NET for iOS
> refuses any other version. If your default Xcode is newer, prefix the command:
> `DEVELOPER_DIR=/Applications/Xcode_26.0.1.app/Contents/Developer ...`. CI handles this with the
-> [`select-xcode`](.github/actions/select-xcode/action.yml) action.
+> [`select-xcode`](https://github.com/sbokatuk/DatadogNet.iOS/blob/main/.github/actions/select-xcode/action.yml) action.
---
## Upgrading the Datadog SDK
-1. Record the new archive's hash in [`build/checksums.txt`](build/checksums.txt):
-
- ```bash
- v=3.15.0
- curl -fsSL -O "https://github.com/DataDog/dd-sdk-ios/releases/download/$v/Datadog.xcframework.zip"
- shasum -a 256 Datadog.xcframework.zip
- ```
-
-2. Bump `DatadogNativeVersion` in [`Directory.Build.props`](Directory.Build.props) and reset
- `DatadogBindingRevision` to `1`.
-3. `./build/FetchXcFrameworks.sh`
-4. `./build/DiffSwiftHeaders.sh ` — writes one `build/.Swift.h.diff` per
- framework whose generated header changed, which is the porting work list;
- [`docs/regenerating-bindings.md`](docs/regenerating-bindings.md) explains how to read one.
+1. `./build/BumpNativeVersion.sh 3.15.0` does the mechanical part: bumps `DatadogNativeVersion`
+ in
+ [`Directory.Build.props`](https://github.com/sbokatuk/DatadogNet.iOS/blob/main/Directory.Build.props)
+ and resets `DatadogBindingRevision` to `1`; pins the new archive's hash in
+ [`build/checksums.txt`](https://github.com/sbokatuk/DatadogNet.iOS/blob/main/build/checksums.txt)
+ from the digest GitHub publishes for the release asset, verified against a fresh download of
+ the same bytes (`build/UpdateChecksums.sh`); points `PackageValidationBaselineVersion` at the
+ version being left behind; rewrites this README's badge, prose and install snippets; and
+ scaffolds `docs/release-notes/3.15.0.1.md`.
+2. `./build/DiffSwiftHeaders.sh 3.15.0` — before fetching, while `libs/` still holds the previous
+ release — writes one `build/.Swift.h.diff` per framework whose generated header
+ changed, which is the porting work list;
+ [`docs/regenerating-bindings.md`](https://github.com/sbokatuk/DatadogNet.iOS/blob/main/docs/regenerating-bindings.md)
+ explains how to read one.
(`./build/GenerateBindings.sh` is the automated alternative for when Objective Sharpie works
again — it writes to `Binding/`, **not** over the committed sources, and its header lists the
fixes the committed files carry that regenerating would otherwise undo.)
+3. `./build/FetchXcFrameworks.sh` — replaces `libs/` with the new release's frameworks.
+4. Port the diffs into the committed `ApiDefinitions.cs` files.
5. `./build/BuildNugets.sh` and run both test suites.
-6. Update the `dd-sdk-ios` badge at the top of this file — both its label and its release link.
- It is hardcoded, so nothing else will notice when it goes stale.
+6. Finish the scaffolded release notes — its TODOs mark what cannot be generated — then tag (see
+ [Releasing](#releasing)).
+
+`build/CheckReadmeVersions.sh` verifies the badge, the "Built against" prose and the install
+snippets against `Directory.Build.props` — the bump script runs it last, and CI runs it on every
+build, so a stale spot fails loudly rather than shipping.
If Datadog adds or removes a framework, `FetchXcFrameworks.sh` fails loudly rather than silently
dropping a package — update the `FRAMEWORKS` list, add or remove the binding project, and update
@@ -545,7 +555,7 @@ dropping a package — update the `FRAMEWORKS` list, add or remove the binding p
> fails on recent iOS SDK module maps. The committed sources were produced by parsing the shipped
> `-Swift.h` headers directly; `GenerateBindings.sh` documents the problem and honours a `SHARPIE`
> override for when it is fixed. Until then the header diff **is** the upgrade path — see
-> [`docs/regenerating-bindings.md`](docs/regenerating-bindings.md).
+> [`docs/regenerating-bindings.md`](https://github.com/sbokatuk/DatadogNet.iOS/blob/main/docs/regenerating-bindings.md).
---
@@ -555,6 +565,12 @@ Tag it. `v3.14.0.3` builds, tests, publishes every package to nuget.org via trus
creates a GitHub release. The tag drives which native SDK is bound, so an older line can be released
by tagging it.
+The tag must agree with `Directory.Build.props`: pushing `v3.15.0.1` while `DatadogNativeVersion`
+still says `3.14.0` — or a fourth component that is not `DatadogBindingRevision` — fails the
+workflow before anything builds, since it would publish packages whose version does not describe
+their contents. For a deliberate different-line release, set the `RELEASE_ALLOW_VERSION_MISMATCH`
+repository variable to `true` for that release, and unset it afterwards.
+
Pull requests publish a `-beta..` prerelease of the whole set.
Curated notes in `docs/release-notes/.md` replace the generated commit list when present.
@@ -590,7 +606,8 @@ copies and restore again: `rm -rf ~/.nuget/packages/datadognet.*`.
## Licence
-The binding code in this repository is [MIT](LICENSE). The native binaries the packages ship are
+The binding code in this repository is
+[MIT](https://github.com/sbokatuk/DatadogNet.iOS/blob/main/LICENSE). The native binaries the packages ship are
built and published by Datadog and are Apache-2.0 — which covers `dd-sdk-ios`, KSCrash and
opentelemetry-swift alike. Every package declares `MIT AND Apache-2.0` and carries both texts under
`licenses/`.
diff --git a/build/BumpNativeVersion.sh b/build/BumpNativeVersion.sh
new file mode 100755
index 0000000..bc9efac
--- /dev/null
+++ b/build/BumpNativeVersion.sh
@@ -0,0 +1,131 @@
+#!/bin/sh
+# Does the mechanical half of a dd-sdk-ios upgrade, so the human half can start at the part that
+# needs judgement - porting the header diffs into the committed ApiDefinitions.cs files.
+#
+# What it does, in order:
+#
+# * Directory.Build.props: DatadogNativeVersion to the new release, DatadogBindingRevision back
+# to 1, and DatadogPackageValidationBaselineVersion to the version being left behind - the
+# last one published, which is what the next pack validates against.
+# * build/UpdateChecksums.sh: pins the new archive's SHA-256 from the digest GitHub publishes,
+# verified against a fresh download of the asset.
+# * README.md: the dd-sdk-ios badge (label, image path, release link), the "Built against"
+# prose, the install-snippet pins and the device-check example.
+# * docs/release-notes/.md: scaffolded with TODOs in the established format,
+# unless it already exists.
+# * build/CheckReadmeVersions.sh: run at the end, so a rewrite this script missed is caught
+# here rather than in CI.
+#
+# It then prints what remains manual: the header diff, the fetch, the porting, the builds and
+# tests, the notes, the tag. See the README's "Upgrading the Datadog SDK" section.
+#
+# Usage:
+# ./BumpNativeVersion.sh 3.15.0
+#
+# If a run dies halfway (offline during the checksum pin, say), reset with
+# `git checkout -- Directory.Build.props README.md build/checksums.txt` and run it again.
+set -eu
+
+root="$(cd "$(dirname "$0")/.." && pwd)"
+props="$root/Directory.Build.props"
+readme="$root/README.md"
+notes_dir="$root/docs/release-notes"
+
+new="${1:-}"
+if [ -z "$new" ]; then
+ echo "usage: $0 e.g. $0 3.15.0" >&2
+ exit 1
+fi
+if ! printf '%s' "$new" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
+ echo "error: '$new' does not look like a dd-sdk-ios version (expected e.g. 3.15.0)" >&2
+ exit 1
+fi
+
+prop() {
+ sed -n "s/.*<$1>\(.*\)<\/$1>.*/\1/p" "$props" | head -1
+}
+
+old_native="$(prop DatadogNativeVersion)"
+old_revision="$(prop DatadogBindingRevision)"
+if [ -z "$old_native" ] || [ -z "$old_revision" ]; then
+ echo "error: could not read DatadogNativeVersion/DatadogBindingRevision from $props" >&2
+ exit 1
+fi
+old_package="$old_native.$old_revision"
+new_package="$new.1"
+
+if [ "$new" = "$old_native" ]; then
+ echo "error: already bound to dd-sdk-ios $old_native - nothing to bump" >&2
+ exit 1
+fi
+
+# The version being left behind becomes the package-validation baseline, which only works if it
+# was actually published. The tag is the best offline evidence; its absence is a note rather
+# than a failure, since a shallow clone has no tags at all.
+if ! git -C "$root" tag -l "v$old_package" 2>/dev/null | grep -q .; then
+ echo "note: no v$old_package tag here - if that version was never published, point"
+ echo " DatadogPackageValidationBaselineVersion at the newest version that was."
+fi
+
+echo "==> $props: $old_native -> $new, revision $old_revision -> 1, baseline -> $old_package"
+sed \
+ -e "s|$old_native|$new|" \
+ -e "s|$old_revision|1|" \
+ -e "s|.*|$old_package|" \
+ "$props" > "$props.tmp" && mv "$props.tmp" "$props"
+
+echo "==> pinning the $new archive checksum"
+"$root/build/UpdateChecksums.sh" "$new"
+
+echo "==> rewriting the README badge, prose and version pins"
+sed \
+ -e "s|\[!\[dd-sdk-ios $old_native\]|[![dd-sdk-ios $new]|" \
+ -e "s|dd--sdk--ios-$old_native-|dd--sdk--ios-$new-|" \
+ -e "s|dd-sdk-ios/releases/tag/$old_native|dd-sdk-ios/releases/tag/$new|" \
+ -e "s|Built against \*\*dd-sdk-ios $old_native\*\*|Built against **dd-sdk-ios $new**|" \
+ -e "s|Version=\"$old_package\"|Version=\"$new_package\"|g" \
+ -e "s|run-simulator-tests\.sh $old_package|run-simulator-tests.sh $new_package|" \
+ "$readme" > "$readme.tmp" && mv "$readme.tmp" "$readme"
+
+notes="$notes_dir/$new_package.md"
+if [ -f "$notes" ]; then
+ echo "==> $notes already exists; leaving it alone"
+else
+ echo "==> scaffolding $notes"
+ cat > "$notes" < **Package versions are \`.\`.** \`$new_package\` is dd-sdk-ios
+> \`$new\`, binding revision \`1\`. The fourth component belongs to this repository and advances when
+> the bindings or packaging change while the native binaries stay put.
+
+TODO: summarise what the upgrade means for a consumer, in the format of the previous files in
+this directory - consumer-visible changes first, each with why it matters, then documentation and
+tests. The raw material is upstream's release notes and the \`build/*.Swift.h.diff\` files that
+\`./build/DiffSwiftHeaders.sh $new\` writes.
+
+## Upgrading from $old_package
+
+TODO: state exactly what a consumer must change - or that it is a version bump alone.
+NOTES
+fi
+
+echo "==> checking the README rewrite"
+"$root/build/CheckReadmeVersions.sh"
+
+cat < done. What remains is the part that needs judgement:
+
+ 1. ./build/DiffSwiftHeaders.sh $new - run BEFORE fetching, while libs/ still holds
+ $old_native: writes one build/.Swift.h.diff per framework whose generated
+ header changed, which is the porting work list (docs/regenerating-bindings.md).
+ 2. ./build/FetchXcFrameworks.sh - replaces libs/ with the $new frameworks.
+ 3. Port the diffs into the committed ApiDefinitions.cs files.
+ 4. ./build/BuildNugets.sh, then both test suites (PackageTests, simulator).
+ 5. Finish docs/release-notes/$new_package.md - the TODOs mark what cannot be generated.
+ 6. Commit, PR, and tag v$new_package once merged (see the README's Releasing section).
+REMAINING
diff --git a/build/CheckReadmeVersions.sh b/build/CheckReadmeVersions.sh
index 7d37d19..f0e15e6 100755
--- a/build/CheckReadmeVersions.sh
+++ b/build/CheckReadmeVersions.sh
@@ -4,10 +4,13 @@
# stale silently on every release - 3.14.0.1 sat in the snippets while 3.14.0.2 shipped. Running
# this in CI makes the version bump before a release drag the README along with it.
#
-# What is checked: every pin, and the
-# device-check example (run-simulator-tests.sh ...). Prose that explains the version
-# *scheme* ("3.14.0.1 is dd-sdk-ios 3.14.0, binding revision 1") is deliberately not checked -
-# it describes the format, not the current release.
+# What is checked: every pin, the
+# device-check example (run-simulator-tests.sh ...), the dd-sdk-ios badge - which
+# names the native version three times over: its label, the shields.io image path and the
+# release link - and the "Built against **dd-sdk-ios N**" intro prose. Prose that explains the
+# version *scheme* ("3.14.0.1 is dd-sdk-ios 3.14.0, binding revision 1") is deliberately not
+# checked - it describes the format, not the current release - and the anchored patterns below
+# do not reach the historical "dd-sdk-ios 3.0 deleted that framework" style of sentence either.
set -eu
root="$(cd "$(dirname "$0")/.." && pwd)"
@@ -18,7 +21,8 @@ prop() {
sed -n "s/.*<$1>\(.*\)<\/$1>.*/\1/p" "$props" | head -1
}
-version="$(prop DatadogNativeVersion).$(prop DatadogBindingRevision)"
+native="$(prop DatadogNativeVersion)"
+version="$native.$(prop DatadogBindingRevision)"
bad=0
@@ -40,6 +44,30 @@ for token in $(grep -oE 'run-(simulator|emulator)-tests\.sh +[0-9][0-9.]*' "$rea
bad=1
fi
done
+
+# The badge and the prose, each matched by its own anchored pattern so the error names the spot
+# that went stale - and each required to exist, so deleting one does not pass as up to date.
+check_native() {
+ pattern=$1
+ what=$2
+ found=0
+ for token in $(grep -oE "$pattern" "$readme" | grep -oE '[0-9][0-9.]*'); do
+ found=1
+ if [ "$token" != "$native" ]; then
+ echo "README $what says dd-sdk-ios $token, but the bound native version is $native" >&2
+ bad=1
+ fi
+ done
+ if [ "$found" -eq 0 ]; then
+ echo "README has no $what to check (expected to match: $pattern)" >&2
+ bad=1
+ fi
+}
+
+check_native '\[!\[dd-sdk-ios [0-9][0-9.]*\]' 'badge label'
+check_native 'dd--sdk--ios-[0-9][0-9.]*' 'badge image path'
+check_native 'dd-sdk-ios/releases/tag/[0-9][0-9.]*' 'badge release link'
+check_native 'Built against \*\*dd-sdk-ios [0-9][0-9.]*\*\*' '"Built against" prose'
IFS=$old_ifs
if [ "$bad" -ne 0 ]; then
diff --git a/build/DiffSwiftHeaders.sh b/build/DiffSwiftHeaders.sh
index d851500..9e24ca6 100755
--- a/build/DiffSwiftHeaders.sh
+++ b/build/DiffSwiftHeaders.sh
@@ -11,9 +11,9 @@ set -eu
# ./DiffSwiftHeaders.sh 3.15.0 # download that release, diff against libs/
# ./DiffSwiftHeaders.sh DIR_OLD DIR_NEW # diff two directories of *.xcframework
#
-# The download mode also prints the archive's SHA-256, which is the value step 1 of the upgrade
-# guide records in build/checksums.txt - so the hash is captured from the same bytes the diff
-# was read from.
+# The download mode also prints the archive's SHA-256 so it can be compared against the pin
+# UpdateChecksums.sh records in build/checksums.txt from GitHub's own asset digest - a third
+# reading of the same bytes, independent of both the API and the pinning download.
INVOKE_DIR="$(pwd)"
cd "$(dirname "$0")"
@@ -47,11 +47,11 @@ elif [ "$#" -eq 1 ]; then
trap 'rm -rf "$WORK"' EXIT
zip="$WORK/Datadog.xcframework.zip"
- echo "==> downloading dd-sdk-ios $version (no checksum pinned yet - that is what this run produces)"
+ echo "==> downloading dd-sdk-ios $version"
curl -fSL -o "$zip" \
"https://github.com/DataDog/dd-sdk-ios/releases/download/$version/Datadog.xcframework.zip"
- echo "==> SHA-256 for build/checksums.txt:"
+ echo "==> SHA-256 of this download (must match the UpdateChecksums.sh pin in build/checksums.txt):"
shasum -a 256 "$zip" | sed "s|$WORK/||"
NEW_DIR="$WORK/extracted"
diff --git a/build/FetchXcFrameworks.sh b/build/FetchXcFrameworks.sh
index 47658bc..43572f0 100755
--- a/build/FetchXcFrameworks.sh
+++ b/build/FetchXcFrameworks.sh
@@ -60,6 +60,43 @@ if [ -z "$expected" ]; then
exit 1
fi
+# Best-effort cross-check of the pin against the digest GitHub publishes for the asset, before
+# anything is downloaded. UpdateChecksums.sh derives the pin from this same digest, so a
+# disagreement means either the pin was recorded wrongly or the release asset was replaced in
+# place - both must stop the build. The API being unreachable (offline, rate-limited) must not:
+# the pinned hash still guards the actual download, so that case is a one-line note and the
+# fetch continues.
+published=$(curl -fsS --max-time 15 \
+ "https://api.github.com/repos/DataDog/dd-sdk-ios/releases/tags/$DATADOG_VERSION" 2>/dev/null \
+ | python3 -c '
+import json
+import sys
+
+try:
+ release = json.load(sys.stdin)
+except Exception:
+ raise SystemExit(0)
+for asset in release.get("assets", []):
+ if asset.get("name") == "Datadog.xcframework.zip":
+ digest = asset.get("digest") or ""
+ if digest.startswith("sha256:"):
+ print(digest[len("sha256:"):].lower())
+ break
+' 2>/dev/null) || published=""
+if [ -n "$published" ]; then
+ if [ "$published" != "$expected" ]; then
+ echo "error: the pinned SHA-256 for $DATADOG_VERSION disagrees with the digest GitHub publishes" >&2
+ echo " pinned $expected ($CHECKSUMS)" >&2
+ echo " published $published (releases API asset digest)" >&2
+ echo " Either the pin was recorded wrongly - re-run ./build/UpdateChecksums.sh $DATADOG_VERSION -" >&2
+ echo " or the release asset was replaced after it was pinned, which wants investigating upstream." >&2
+ exit 1
+ fi
+ echo "==> pin matches the digest GitHub publishes for $DATADOG_VERSION"
+else
+ echo "note: could not read the asset digest from the GitHub API - continuing with the pinned hash"
+fi
+
sha256_of() {
if command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$1" | cut -d' ' -f1
diff --git a/build/GenerateBindings.sh b/build/GenerateBindings.sh
index 78baf28..e8359ec 100755
--- a/build/GenerateBindings.sh
+++ b/build/GenerateBindings.sh
@@ -8,8 +8,8 @@ set -e
# Run this after bumping DatadogNativeVersion and re-running FetchXcFrameworks.sh.
#
# Usage:
-# ./GenerateBindings.sh # every framework
-# ./GenerateBindings.sh DatadogObjc # just one
+# ./GenerateBindings.sh # every framework
+# ./GenerateBindings.sh DatadogRUM # just one
#
# Requires Objective Sharpie, which is not on nuget.org and has to be obtained separately:
#
diff --git a/build/UpdateChecksums.sh b/build/UpdateChecksums.sh
new file mode 100755
index 0000000..7c6413e
--- /dev/null
+++ b/build/UpdateChecksums.sh
@@ -0,0 +1,144 @@
+#!/bin/sh
+# Records the SHA-256 pin for one dd-sdk-ios release in build/checksums.txt, anchored to the
+# digest GitHub itself publishes for the release asset rather than to whatever a first download
+# happened to contain.
+#
+# The releases API reports each asset's digest ("sha256:"), computed by GitHub at upload
+# time. Pinning that value closes the trust-on-first-use hole the old "download it and record
+# what you got" instructions had: a machine whose download is tampered with - or an asset
+# replaced before anyone here pinned it - would have minted the poisoned hash as the new truth.
+# The asset is still downloaded and hashed by this script, so the two sources must agree before
+# anything is recorded; a download that does not match the published digest is exactly the event
+# the pins exist to catch, and stops this script loudly.
+#
+# Run it after bumping DatadogNativeVersion (build/BumpNativeVersion.sh does), then diff the
+# result - a pin that CHANGED for an unchanged version means the release asset was replaced in
+# place, and wants investigating rather than committing.
+#
+# Usage:
+# ./UpdateChecksums.sh # version from Directory.Build.props
+# ./UpdateChecksums.sh 3.15.0 # pin a different release, e.g. ahead of a bump
+#
+# FetchXcFrameworks.sh verifies every download against the recorded pin, and cross-checks the
+# pin against the live digest whenever the API is reachable.
+set -eu
+
+root="$(cd "$(dirname "$0")/.." && pwd)"
+props="$root/Directory.Build.props"
+out="$root/build/checksums.txt"
+work="$(mktemp -d)"
+trap 'rm -rf "$work"' EXIT
+
+version="${1:-}"
+if [ -z "$version" ]; then
+ version=$(sed -n 's:.*\(.*\).*:\1:p' "$props" | head -1)
+fi
+if [ -z "$version" ]; then
+ echo "error: could not read DatadogNativeVersion from $props" >&2
+ exit 1
+fi
+
+# Interpolated into URLs and matched against pin lines, so reject anything exotic up front.
+case "$version" in
+ *[!A-Za-z0-9._-]*)
+ echo "error: invalid version '$version'" >&2
+ exit 1
+ ;;
+esac
+
+echo "==> asking the GitHub releases API for the $version asset digest"
+digest=$(curl -fsSL "https://api.github.com/repos/DataDog/dd-sdk-ios/releases/tags/$version" | python3 -c '
+import json
+import sys
+
+release = json.load(sys.stdin)
+for asset in release.get("assets", []):
+ if asset.get("name") == "Datadog.xcframework.zip":
+ digest = asset.get("digest") or ""
+ if digest.startswith("sha256:"):
+ print(digest[len("sha256:"):].lower())
+ break
+')
+if [ -z "$digest" ]; then
+ echo "error: the releases API served no sha256 digest for Datadog.xcframework.zip $version" >&2
+ echo " (release missing, asset renamed, or GitHub stopped publishing digests)" >&2
+ exit 1
+fi
+case "$digest" in
+ *[!0-9a-f]*)
+ echo "error: '$digest' does not look like a SHA-256" >&2
+ exit 1
+ ;;
+esac
+
+# Download the same asset and require it to hash to the published digest: two independent paths
+# to one value - what GitHub says it stored, and what it actually serves today.
+echo "==> downloading Datadog.xcframework.zip $version to verify the digest against real bytes"
+archive="$work/Datadog.xcframework.zip"
+curl -fSL -o "$archive" \
+ "https://github.com/DataDog/dd-sdk-ios/releases/download/$version/Datadog.xcframework.zip"
+if command -v shasum >/dev/null 2>&1; then
+ actual=$(shasum -a 256 "$archive" | cut -d' ' -f1)
+else
+ actual=$(sha256sum "$archive" | cut -d' ' -f1)
+fi
+if [ "$actual" != "$digest" ]; then
+ echo "error: the downloaded archive does not match the digest GitHub publishes for it" >&2
+ echo " published $digest" >&2
+ echo " download $actual" >&2
+ echo " Nothing was recorded. Either the download was corrupted or tampered with in transit," >&2
+ echo " or the asset was replaced mid-request; retry, and investigate upstream if it persists." >&2
+ exit 1
+fi
+
+previous=$(sed -n "s/^$version[[:space:]]\{1,\}\([0-9a-f]\{64\}\).*/\1/p" "$out" | head -1)
+if [ -n "$previous" ] && [ "$previous" != "$digest" ]; then
+ # Same version, different hash: for an asset that is supposed to be immutable, this is the
+ # replacement event the pins exist to catch. Recording it is a deliberate act - the diff of
+ # checksums.txt is the audit trail - but it must never happen silently.
+ echo "WARNING: the pin for $version is changing" >&2
+ echo " was $previous" >&2
+ echo " now $digest" >&2
+ echo " The release asset was replaced upstream. Diff the frameworks before committing this." >&2
+fi
+
+# The whole file is rewritten - canonical header, then the pins in their existing order with this
+# version replaced in place or appended - so the instructions above the pins cannot drift from
+# what this script actually does.
+pins="$work/pins"
+awk -v version="$version" -v digest="$digest" '
+ /^#/ || /^[[:space:]]*$/ { next }
+ $1 == version { print version, digest; replaced = 1; next }
+ { print }
+ END { if (!replaced) print version, digest }
+' "$out" > "$pins"
+
+{
+ echo "# SHA-256 of each dd-sdk-ios release's Datadog.xcframework.zip."
+ echo "#"
+ echo "# Upstream publishes no checksum manifest of its own, but the GitHub releases API reports"
+ echo "# the digest it computed for every asset at upload time. build/UpdateChecksums.sh records"
+ echo "# that digest here - after downloading the asset and requiring the bytes to hash to the"
+ echo "# same value - so the pin is anchored to what GitHub stored, not to whatever one machine's"
+ echo "# first download happened to contain. FetchXcFrameworks.sh refuses an archive that does"
+ echo "# not match, and cross-checks the pin against the live digest when the API is reachable."
+ echo "#"
+ echo "# That makes this file the trust anchor for ~116 MB of native code that gets linked into"
+ echo "# consumers' apps: a substituted or truncated archive fails the fetch instead of being"
+ echo "# bound and published. It also makes the build reproducible, since a GitHub release asset"
+ echo "# can be replaced in place without the tag changing."
+ echo "#"
+ echo "# To add or re-pin a version:"
+ echo "#"
+ echo "# ./build/UpdateChecksums.sh 3.15.0"
+ echo "#"
+ echo "# Format: "
+ echo ""
+ cat "$pins"
+} > "$out.tmp" && mv "$out.tmp" "$out"
+
+if [ "$previous" = "$digest" ]; then
+ echo "==> $version was already pinned at this digest; verified and unchanged"
+else
+ echo "==> pinned $version $digest"
+fi
diff --git a/build/ValidatePackageApi.sh b/build/ValidatePackageApi.sh
new file mode 100755
index 0000000..8cc78fe
--- /dev/null
+++ b/build/ValidatePackageApi.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+# Compares every packed .nupkg in artifacts/ against the last published release, so an accidental
+# API break - a regenerated binding quietly dropping a member is the likely shape of it - fails
+# here instead of reaching nuget.org and a consumer's compiler.
+#
+# This deliberately runs AFTER merge-packages.py, on the finished artifact, not at pack time via
+# EnablePackageValidation. The two-pass band arrangement makes the MSBuild-time gate structurally
+# wrong: each pass packs an intermediate that lacks the other band's target frameworks, so
+# ApiCompat maps the baseline's net10 assemblies onto the current net9 ones and reports every
+# band-to-band difference as a break. Only the merged package has the baseline's framework set.
+#
+# Requires network twice over: the baseline packages download from nuget.org, and the apicompat
+# tool installs on first use. CI runs it in the pack job; locally it is an explicit invocation,
+# never part of an offline build.
+#
+# Usage:
+# ./build/ValidatePackageApi.sh # baseline from Directory.Build.props
+# ./build/ValidatePackageApi.sh 3.14.0.3 # explicit baseline version
+set -eu
+
+root="$(cd "$(dirname "$0")/.." && pwd)"
+artifacts="$root/artifacts"
+
+# Pinned like every other tool this repository pulls in. Advance deliberately.
+apicompat_version="9.0.316"
+
+baseline="${1:-$(sed -n 's/.*\(.*\)<\/DatadogPackageValidationBaselineVersion>.*/\1/p' "$root/Directory.Build.props" | head -1)}"
+if [ -z "$baseline" ]; then
+ echo "error: no baseline version - set DatadogPackageValidationBaselineVersion in Directory.Build.props or pass one" >&2
+ exit 1
+fi
+
+packages=$(ls "$artifacts"/*.nupkg 2>/dev/null || true)
+if [ -z "$packages" ]; then
+ echo "error: no packages in $artifacts - run ./build/BuildNugets.sh first" >&2
+ exit 1
+fi
+
+tools="$root/artifacts/.apicompat"
+if [ ! -x "$tools/apicompat" ]; then
+ echo "==> installing Microsoft.DotNet.ApiCompat.Tool $apicompat_version"
+ dotnet tool install Microsoft.DotNet.ApiCompat.Tool --version "$apicompat_version" --tool-path "$tools" >/dev/null
+fi
+
+baselines="$root/artifacts/.baseline-$baseline"
+mkdir -p "$baselines"
+
+failed=0
+for package in $packages; do
+ name=$(basename "$package")
+ id=$(printf '%s' "$name" | sed -E 's/\.[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?(-[0-9A-Za-z.-]+)?\.nupkg$//')
+ lower=$(printf '%s' "$id" | tr '[:upper:]' '[:lower:]')
+
+ base="$baselines/$lower.$baseline.nupkg"
+ if [ ! -f "$base" ]; then
+ url="https://api.nuget.org/v3-flatcontainer/$lower/$baseline/$lower.$baseline.nupkg"
+ if ! curl -fsSL --max-time 120 -o "$base" "$url"; then
+ rm -f "$base"
+ # A package that has never shipped has no baseline to hold it to - the first release IS the
+ # baseline. Anything else failing to download is a real problem.
+ echo "==> $id: no $baseline on nuget.org - new package, nothing to validate against"
+ continue
+ fi
+ fi
+
+ echo "==> $id: validating against $baseline"
+ if ! "$tools/apicompat" package "$package" --baseline-package "$base"; then
+ failed=1
+ fi
+done
+
+if [ "$failed" -ne 0 ]; then
+ echo "ValidatePackageApi: API breaking changes against $baseline - see above" >&2
+ exit 1
+fi
+
+echo "ValidatePackageApi: every package is API-compatible with $baseline"
diff --git a/build/checksums.txt b/build/checksums.txt
index c7bde9d..52f1d54 100644
--- a/build/checksums.txt
+++ b/build/checksums.txt
@@ -1,19 +1,20 @@
# SHA-256 of each dd-sdk-ios release's Datadog.xcframework.zip.
#
-# Upstream publishes no checksum manifest of its own - unlike, say, the ffmpeg_kit_flutter
-# releases, which ship a checksums.json next to the archives. So the hash is pinned here, in the
-# repository, and FetchXcFrameworks.sh refuses to use an archive that does not match.
+# Upstream publishes no checksum manifest of its own, but the GitHub releases API reports
+# the digest it computed for every asset at upload time. build/UpdateChecksums.sh records
+# that digest here - after downloading the asset and requiring the bytes to hash to the
+# same value - so the pin is anchored to what GitHub stored, not to whatever one machine's
+# first download happened to contain. FetchXcFrameworks.sh refuses an archive that does
+# not match, and cross-checks the pin against the live digest when the API is reachable.
#
# That makes this file the trust anchor for ~116 MB of native code that gets linked into
-# consumers' apps: a substituted or truncated archive fails the fetch instead of being bound and
-# published. It also makes the build reproducible, since a GitHub release asset can be replaced
-# in place without the tag changing.
+# consumers' apps: a substituted or truncated archive fails the fetch instead of being
+# bound and published. It also makes the build reproducible, since a GitHub release asset
+# can be replaced in place without the tag changing.
#
-# To add a version, download the asset and record its hash:
+# To add or re-pin a version:
#
-# v=2.18.0
-# curl -fsSL -O "https://github.com/DataDog/dd-sdk-ios/releases/download/$v/Datadog.xcframework.zip"
-# shasum -a 256 Datadog.xcframework.zip
+# ./build/UpdateChecksums.sh 3.15.0
#
# Format:
diff --git a/docs/release-notes/3.14.0.4.md b/docs/release-notes/3.14.0.4.md
new file mode 100644
index 0000000..bee4253
--- /dev/null
+++ b/docs/release-notes/3.14.0.4.md
@@ -0,0 +1,83 @@
+## What's changed
+
+Binding-only release. The native SDK is unchanged — still
+[dd-sdk-ios 3.14.0](https://github.com/DataDog/dd-sdk-ios/releases/tag/3.14.0) — and so are the
+package ids, namespaces and API. The fourth component advances for the changes below: round two of
+the ergonomics review, aimed this time at the packaging metadata, the supply chain around the
+native archive, and the release machinery.
+
+## Descriptions no longer point at an empty package
+
+Since 3.14.0.1 the RUM, Logs and Trace descriptions ended "The managed DD* API lives in
+DatadogNet.Objc.iOS; this package ships the native feature" — carried over from 2.x, where the
+DatadogObjc framework really did hold every DD* type, and wrong ever since 3.0 deleted it: each
+package binds its own surface (RUM's `ApiDefinitions.cs` alone is 7,400 lines), and
+DatadogNet.Objc.iOS is a dependency-only meta-package with no assembly at all. A reader on
+nuget.org following that sentence installed an empty package. The descriptions now say where the
+API actually is, the stale rationale comment in `src/Datadog.Binding.props` is rewritten to match
+reality, and the package tests reject any description that makes the old claim again.
+
+## The packed README's links now work on nuget.org
+
+The same README.md ships in every package and is rendered on each package's nuget.org page — where
+its eleven repo-relative links (the sample, `build/checksums.txt`, the regeneration guide, the
+Flags shim, the licence and friends) resolved against nuget.org and rendered as dead text. Every
+repository link is absolute now; in-page anchors stay as they are, since those work everywhere the
+file renders.
+
+## Checksums: trust-on-first-use replaced by GitHub's own digests
+
+`build/checksums.txt` is the trust anchor for the ~200 MB native archive, but its pins were
+recorded by downloading the archive and writing down whatever hash arrived — so a tampered first
+download would have minted the poisoned hash as the truth. The new `build/UpdateChecksums.sh`
+records the digest the GitHub releases API reports for the asset, computed by GitHub at upload
+time, and requires a fresh download to hash to the same value before anything is written.
+`FetchXcFrameworks.sh` additionally cross-checks the pin against the live digest before
+downloading and hard-fails on a disagreement; when the API is unreachable — offline,
+rate-limited — the pinned hash still guards the download, so no build grows a network dependency
+it did not have. Run against 3.14.0, the published digest equals the pin recorded under the old
+flow: a retroactive check the old flow could not offer.
+
+## Package validation against the published baseline
+
+CI now holds every packed package's API to the last published release — this one's baseline is
+`3.14.0.3` — so an accidental API break, most plausibly a regenerated binding quietly dropping a
+member, fails in CI instead of surfacing in a consumer's compiler. It runs as
+`build/ValidatePackageApi.sh` on the merged artifacts rather than at pack time: each band's pack
+pass produces an intermediate that legitimately lacks the other band's frameworks, and only the
+finished package carries the framework set the baseline has. The baseline downloads from
+nuget.org, so the check is CI's and an explicit local invocation's, never part of an offline
+pack.
+
+## Release machinery
+
+- **A release tag must agree with `Directory.Build.props`.** The tag alone drives which native
+ line is bound, so a mistyped tag would have published packages whose version does not describe
+ their contents. The workflow now fails before building when the tag's native prefix or binding
+ revision disagrees with the props; releasing a different line on purpose remains possible
+ through a documented repository variable.
+- **Upstream releases are watched.** A weekly workflow compares `DatadogNativeVersion` against
+ the latest dd-sdk-ios release and opens one issue — idempotently — when upstream is ahead,
+ linking the release and the upgrade ritual.
+- **`build/BumpNativeVersion.sh` starts a native upgrade in one command**: props, digest-anchored
+ checksum pin, README badge/prose/snippets, the validation baseline, and a scaffolded
+ release-notes file, finishing with a self-check. The README's upgrade guide now leads with it —
+ and puts `DiffSwiftHeaders.sh` *before* `FetchXcFrameworks.sh`, since the previous step order
+ would have diffed the new release against itself.
+- **`build/CheckReadmeVersions.sh` now also verifies the dd-sdk-ios badge and the "Built
+ against" prose** — the two hardcoded spots the upgrade guide used to shrug about ("nothing
+ else will notice when it goes stale"). Something notices.
+- `GenerateBindings.sh`'s usage example no longer names DatadogObjc, a framework deleted
+ upstream in 3.0.
+
+## Upgrading from 3.14.0.3
+
+```diff
+-
++
+```
+
+Nothing to change beyond the version: same native SDK, same API, same package set, and the native
+xcframeworks are byte-for-byte the same build. All packages move together, as they depend on each
+other at an exact version. The corrected descriptions and working README links appear on
+nuget.org with this version.
diff --git a/src/Datadog.Binding.props b/src/Datadog.Binding.props
index 90a1216..1aaf755 100644
--- a/src/Datadog.Binding.props
+++ b/src/Datadog.Binding.props
@@ -149,10 +149,12 @@
diff --git a/src/DatadogNet.Logs.iOS/DatadogNet.Logs.iOS.csproj b/src/DatadogNet.Logs.iOS/DatadogNet.Logs.iOS.csproj
index 9fba91d..6b85c6f 100644
--- a/src/DatadogNet.Logs.iOS/DatadogNet.Logs.iOS.csproj
+++ b/src/DatadogNet.Logs.iOS/DatadogNet.Logs.iOS.csproj
@@ -3,7 +3,7 @@
DatadogLogs
Logs
- .NET for iOS / .NET MAUI bindings for the native Datadog iOS SDK's DatadogLogs framework: structured log collection with levels, attributes, tags and error reporting, correlated with RUM sessions. Built against dd-sdk-ios $(DatadogNativeVersion). The managed DD* API lives in DatadogNet.Objc.iOS; this package ships the native feature.
+ .NET for iOS / .NET MAUI bindings for the native Datadog iOS SDK's DatadogLogs framework: structured log collection with levels, attributes, tags and error reporting, correlated with RUM sessions. Built against dd-sdk-ios $(DatadogNativeVersion). The managed DDLogs/DDLogger API ships in this package; DatadogNet.Objc.iOS remains as a dependency-only compatibility meta-package.
datadog-logs;logging
diff --git a/src/DatadogNet.RUM.iOS/DatadogNet.RUM.iOS.csproj b/src/DatadogNet.RUM.iOS/DatadogNet.RUM.iOS.csproj
index ab180da..45f2eda 100644
--- a/src/DatadogNet.RUM.iOS/DatadogNet.RUM.iOS.csproj
+++ b/src/DatadogNet.RUM.iOS/DatadogNet.RUM.iOS.csproj
@@ -3,7 +3,7 @@
DatadogRUM
RUM
- .NET for iOS / .NET MAUI bindings for the native Datadog iOS SDK's DatadogRUM framework: Real User Monitoring - views, actions, resources, errors, frustration signals and mobile vitals, with automatic UIKit instrumentation. Built against dd-sdk-ios $(DatadogNativeVersion). The managed DD* API lives in DatadogNet.Objc.iOS; this package ships the native feature.
+ .NET for iOS / .NET MAUI bindings for the native Datadog iOS SDK's DatadogRUM framework: Real User Monitoring - views, actions, resources, errors, frustration signals and mobile vitals, with automatic UIKit instrumentation. Built against dd-sdk-ios $(DatadogNativeVersion). The managed DDRUM* API ships in this package; DatadogNet.Objc.iOS remains as a dependency-only compatibility meta-package.
datadog-rum;rum;real-user-monitoring
diff --git a/src/DatadogNet.Trace.iOS/DatadogNet.Trace.iOS.csproj b/src/DatadogNet.Trace.iOS/DatadogNet.Trace.iOS.csproj
index e4f58fd..85eea83 100644
--- a/src/DatadogNet.Trace.iOS/DatadogNet.Trace.iOS.csproj
+++ b/src/DatadogNet.Trace.iOS/DatadogNet.Trace.iOS.csproj
@@ -3,7 +3,7 @@
DatadogTrace
Trace
- .NET for iOS / .NET MAUI bindings for the native Datadog iOS SDK's DatadogTrace framework: distributed tracing (APM), spans, and trace-context propagation across Datadog, B3 and W3C tracecontext headers. Built against dd-sdk-ios $(DatadogNativeVersion). The managed DD* API lives in DatadogNet.Objc.iOS; this package ships the native feature.
+ .NET for iOS / .NET MAUI bindings for the native Datadog iOS SDK's DatadogTrace framework: distributed tracing (APM), spans, and trace-context propagation across Datadog, B3 and W3C tracecontext headers. Built against dd-sdk-ios $(DatadogNativeVersion). The managed DDTrace/DDTracer API ships in this package; DatadogNet.Objc.iOS remains as a dependency-only compatibility meta-package.
datadog-trace;apm;tracing;opentelemetry
diff --git a/tests/DatadogNet.iOS.PackageTests/PackageLayoutTests.cs b/tests/DatadogNet.iOS.PackageTests/PackageLayoutTests.cs
index 945536f..1282eb8 100644
--- a/tests/DatadogNet.iOS.PackageTests/PackageLayoutTests.cs
+++ b/tests/DatadogNet.iOS.PackageTests/PackageLayoutTests.cs
@@ -252,6 +252,13 @@ string Value(string element) => nuspec.Descendants()
{
Assert.Contains("meta", Value("description"), StringComparison.OrdinalIgnoreCase);
}
+
+ // Until 3.14.0.4 the RUM, Logs and Trace descriptions claimed the managed DD* API "lives
+ // in DatadogNet.Objc.iOS" - true of the 2.x packages, where DatadogObjc held every DD*
+ // type, but wrong since 3.0: each package binds its own surface, and DatadogNet.Objc.iOS
+ // is a dependency-only meta-package with no assembly at all. Sending a reader there for
+ // the API must not come back.
+ Assert.DoesNotContain("lives in DatadogNet.Objc.iOS", Value("description"), StringComparison.OrdinalIgnoreCase);
}
[Theory]