From 07ce7ed4e479d8d9bc3ac4620573e3355eb1c76b Mon Sep 17 00:00:00 2001 From: Siarhei Bakatsiuk Date: Fri, 24 Jul 2026 11:17:39 +0300 Subject: [PATCH 1/3] Harden the release pipeline and document the generated surface - README: fix the stale 3.14.0.1 install pins, and fail CI when README versions drift from Directory.Build.props (build/CheckReadmeVersions.sh) - Pack the curated docs/release-notes/.md into PackageReleaseNotes, with the releases page as fallback, so nuget.org stops showing empty notes - Document why DDLogger's error/attributes parameters carry no [NullAllowed]: upstream declares them _Nonnull and the Swift implementation takes a non-optional [String: Any], so letting null through would trade today's managed ArgumentNullException for a native crash. The README's troubleshooting section points at the null-tolerant Log(...) instead. - Add doc summaries to the 26 main entry types in the ApiDefinitions files; the binding generator carries them into the packages' XML docs (measured, not assumed), so DDDatadog, DDConfiguration, the per-module Enable types, DDRUMMonitor, DDLogger and the header writers now have IntelliSense - Turn the trim and AOT analyzers on permanently; the whole surface analyses clean today, and IsTrimmable stays deliberately unset - the props explain - Add build/DiffSwiftHeaders.sh and docs/regenerating-bindings.md: the header diff is the upgrade work list while Objective Sharpie cannot regenerate, and the script also yields the SHA-256 checksums.txt records - Document the net8 sunset: dropped in the first release after .NET 8 leaves support on 10 November 2026 Co-Authored-By: Claude Fable 5 --- .github/workflows/build.yml | 5 +- Directory.Build.props | 12 ++ README.md | 45 ++++++-- build/CheckReadmeVersions.sh | 50 +++++++++ build/DiffSwiftHeaders.sh | 105 ++++++++++++++++++ docs/regenerating-bindings.md | 50 +++++++++ src/Datadog.Binding.props | 14 +++ src/DatadogNet.Core.iOS/ApiDefinitions.cs | 6 + .../ApiDefinitions.cs | 1 + src/DatadogNet.Logs.iOS/ApiDefinitions.cs | 13 +++ src/DatadogNet.RUM.iOS/ApiDefinitions.cs | 5 + .../ApiDefinitions.cs | 3 + src/DatadogNet.Trace.iOS/ApiDefinitions.cs | 6 + .../ApiDefinitions.cs | 1 + 14 files changed, 305 insertions(+), 11 deletions(-) create mode 100755 build/CheckReadmeVersions.sh create mode 100755 build/DiffSwiftHeaders.sh create mode 100644 docs/regenerating-bindings.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a990743..ce29297 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,6 @@ name: build -# Reusable pipeline: packs all eleven Datadog binding packages at a given version, validates the +# Reusable pipeline: packs all twelve Datadog binding packages at a given version, validates the # packed packages, builds the sample against them, runs the simulator smoke tests, and uploads the # packages as an artifact. Callers (pr.yml, release.yml) decide the version and what to do with the # result. @@ -45,6 +45,9 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Check README versions + run: ./build/CheckReadmeVersions.sh + - name: Select Xcode uses: ./.github/actions/select-xcode diff --git a/Directory.Build.props b/Directory.Build.props index 7b1072c..66942d0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -43,4 +43,16 @@ true + + + $(MSBuildThisFileDirectory)docs/release-notes/$(VersionPrefix).md + $([System.IO.File]::ReadAllText('$(DatadogReleaseNotesFile)')) + https://github.com/sbokatuk/DatadogNet.iOS/releases/tag/v$(VersionPrefix) + + diff --git a/README.md b/README.md index d6448ce..d576e8b 100644 --- a/README.md +++ b/README.md @@ -91,14 +91,20 @@ and adds `Logs`, `Trace`, `SessionReplay`, `CrashReporting` or `WebViewTracking` **Minimum deployment target**: iOS **12.2** — the Datadog frameworks are Swift and use the OS-provided Swift runtime, ABI-stable from 12.2. +> **net8 sunset.** The net8 head is already past its platform support window — the net8 mobile +> workloads left support with MAUI 8 on 14 May 2025 — and ships for the apps that still target it, +> with the simulator checks run against it so what works is verified rather than assumed. So the +> decision does not persist by inertia: **the net8 head is dropped in the first release after +> .NET 8 itself leaves support on 10 November 2026.** + --- ## Installing ```xml - - + + ``` @@ -107,8 +113,8 @@ restore them: ```xml - - + + ``` @@ -316,6 +322,14 @@ Attribute values may be strings, any numeric type, `bool`, `DateTime`, `DateTime enums, `NSObject`s, arrays, and nested dictionaries. Anything else throws `ArgumentException` rather than being silently dropped. `DatadogAttributes` lives in `DatadogCore`. +The convenience layer is fully documented, and the **main entry types of the generated tier carry +summaries too** — `DDDatadog`, `DDConfiguration`, `DDSite`, `DDTrackingConsent`, the `Enable`/ +configuration types of every module, `DDRUMMonitor`, `DDLogger`, `DDTracer` and the header +writers — written in `ApiDefinitions.cs`, which the binding generator carries through to the +package's XML docs. The long tail of generated members (the 377 RUM model types, mostly) has no +upstream docs to import and stays bare; the C# names map 1:1 onto the Objective-C ones, so +upstream's reference covers them. + ### Trace ids `GetTraceId` returns **32 lowercase hexadecimal characters** and `GetSpanId` returns **decimal**. @@ -469,7 +483,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.1 net9.0-ios18.0 +./.github/scripts/run-simulator-tests.sh 3.14.0.2 net9.0-ios18.0 ``` Build and run the sample: @@ -498,9 +512,12 @@ dotnet build samples/DatadogNet.iOS.Example/DatadogNetExample.csproj -p:RuntimeI 2. Bump `DatadogNativeVersion` in [`Directory.Build.props`](Directory.Build.props) and reset `DatadogBindingRevision` to `1`. 3. `./build/FetchXcFrameworks.sh` -4. `./build/GenerateBindings.sh` — writes to `Binding/`, **not** over the committed sources. Diff - and port real changes across; the script's header lists the fixes the committed files carry that - regenerating would otherwise undo. +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. + (`./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.) 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. @@ -512,13 +529,14 @@ dropping a package — update the `FRAMEWORKS` list, add or remove the binding p > **Note.** Objective Sharpie 3.5.116 cannot currently generate these bindings — its bundled clang > 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. +> override for when it is fixed. Until then the header diff **is** the upgrade path — see +> [`docs/regenerating-bindings.md`](docs/regenerating-bindings.md). --- ## Releasing -Tag it. `v3.14.0.1` builds, tests, publishes every package to nuget.org via trusted publishing, and +Tag it. `v3.14.0.2` builds, tests, publishes every package to nuget.org via trusted publishing, and creates a GitHub release. The tag drives which native SDK is bound, so an older line can be released by tagging it. @@ -543,6 +561,13 @@ alongside Crash Reporting, and dSYMs uploaded for symbolication. **`This version of .NET for iOS requires Xcode 26.0`.** Only affects `net10.0-ios26.0`. See [Building locally](#building-locally). +**`ArgumentNullException` passing `null` attributes to `logger.Info(message, attributes)`** — or +any other level. Faithful to upstream: the Objective-C projection declares the dictionary (and the +`NSError`) `_Nonnull`, and the Swift implementation takes a non-optional `[String: Any]`, so a +binding that allowed `null` through would crash natively in the bridging thunk instead. Use the +message-only overload, or the convenience `logger.Log(level, message, exception?, attributes?)`, +which accepts `null` for both. + **A framework fails to load at runtime.** Usually a partially restored package. Clear the cached copies and restore again: `rm -rf ~/.nuget/packages/datadognet.*`. diff --git a/build/CheckReadmeVersions.sh b/build/CheckReadmeVersions.sh new file mode 100755 index 0000000..7d37d19 --- /dev/null +++ b/build/CheckReadmeVersions.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# Fails when README.md pins a package version that is not the one this repository currently +# builds. The install snippets are copy-paste starting points, and a hardcoded version there goes +# 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. +set -eu + +root="$(cd "$(dirname "$0")/.." && pwd)" +readme="$root/README.md" +props="$root/Directory.Build.props" + +prop() { + sed -n "s/.*<$1>\(.*\)<\/$1>.*/\1/p" "$props" | head -1 +} + +version="$(prop DatadogNativeVersion).$(prop DatadogBindingRevision)" + +bad=0 + +old_ifs=$IFS +IFS=' +' +for pin in $(grep -oE 'Include="DatadogNet[^"]*" +Version="[0-9][^"]*"' "$readme"); do + id=$(printf '%s' "$pin" | sed -E 's/Include="([^"]*)".*/\1/') + ver=$(printf '%s' "$pin" | sed -E 's/.*Version="([^"]*)"/\1/') + if [ "$ver" != "$version" ]; then + echo "README pins $id $ver, but the current version is $version" >&2 + bad=1 + fi +done + +for token in $(grep -oE 'run-(simulator|emulator)-tests\.sh +[0-9][0-9.]*' "$readme" | grep -oE '[0-9][0-9.]*$'); do + if [ "$token" != "$version" ]; then + echo "README runs the device checks at $token, but the current version is $version" >&2 + bad=1 + fi +done +IFS=$old_ifs + +if [ "$bad" -ne 0 ]; then + echo "CheckReadmeVersions: README.md is stale - update the versions above (current: $version)" >&2 + exit 1 +fi + +echo "CheckReadmeVersions: README.md agrees with Directory.Build.props ($version)" diff --git a/build/DiffSwiftHeaders.sh b/build/DiffSwiftHeaders.sh new file mode 100755 index 0000000..d851500 --- /dev/null +++ b/build/DiffSwiftHeaders.sh @@ -0,0 +1,105 @@ +#!/bin/sh +set -eu + +# Diffs the generated Objective-C headers (-Swift.h) between the frameworks in libs/ and another +# dd-sdk-ios release - which is exactly the delta the committed ApiDefinitions.cs must absorb on +# an upgrade, now that Objective Sharpie cannot regenerate them (its bundled clang fails on +# recent iOS SDK module maps; see GenerateBindings.sh). The headers are the source of truth the +# committed bindings were produced from, so their diff IS the porting work list. +# +# Usage: +# ./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. + +INVOKE_DIR="$(pwd)" +cd "$(dirname "$0")" +ROOT="$(cd .. && pwd)" + +absolute() { + case "$1" in + /*) printf '%s' "$1" ;; + *) printf '%s' "$INVOKE_DIR/$1" ;; + esac +} + +header_of() { + # First device slice's -Swift.h for one framework name under one root. + find "$1" -path "*/$2.xcframework/ios-arm64*/$2.framework/Headers/$2-Swift.h" 2>/dev/null | sort | head -1 +} + +if [ "$#" -eq 2 ]; then + OLD_DIR="$(absolute "$1")" + NEW_DIR="$(absolute "$2")" +elif [ "$#" -eq 1 ]; then + version="$1" + case "$version" in + *[!A-Za-z0-9._-]*) + echo "error: invalid version '$version'" >&2 + exit 1 + ;; + esac + OLD_DIR="$ROOT/libs" + WORK="$(mktemp -d)" + 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)" + curl -fSL -o "$zip" \ + "https://github.com/DataDog/dd-sdk-ios/releases/download/$version/Datadog.xcframework.zip" + + echo "==> SHA-256 for build/checksums.txt:" + shasum -a 256 "$zip" | sed "s|$WORK/||" + + NEW_DIR="$WORK/extracted" + mkdir -p "$NEW_DIR" + unzip -q "$zip" -d "$NEW_DIR" +else + echo "usage: $0 | " >&2 + exit 1 +fi + +changed=0 +unchanged=0 +missing=0 + +for framework_dir in "$OLD_DIR"/*.xcframework; do + [ -d "$framework_dir" ] || continue + framework="$(basename "$framework_dir" .xcframework)" + + old_header="$(header_of "$OLD_DIR" "$framework")" + new_header="$(header_of "$NEW_DIR" "$framework")" + + if [ -z "$old_header" ]; then + # OpenTelemetryApi ships no -Swift.h at all; that is expected, not an upgrade signal. + echo "--- $framework: no -Swift.h in the current libs/ (expected for OpenTelemetryApi)" + continue + fi + if [ -z "$new_header" ]; then + echo "!!! $framework: present in libs/ but absent from the new release - a framework was" + echo " dropped or renamed upstream. FetchXcFrameworks.sh will fail loudly on this too." + missing=$((missing + 1)) + continue + fi + + if diff -u "$old_header" "$new_header" > "$framework.Swift.h.diff" 2>&1; then + rm -f "$framework.Swift.h.diff" + unchanged=$((unchanged + 1)) + else + lines=$(wc -l < "$framework.Swift.h.diff" | tr -d ' ') + echo "==> $framework: CHANGED - $lines diff lines in build/$framework.Swift.h.diff" + changed=$((changed + 1)) + fi +done + +echo +summary="==> $changed changed, $unchanged unchanged" +[ "$missing" -gt 0 ] && summary="$summary, $missing MISSING" +echo "$summary" +if [ "$changed" -gt 0 ]; then + echo " Each .diff in build/ is the porting work list for that framework's ApiDefinitions.cs." + echo " See docs/regenerating-bindings.md for how to read one." +fi diff --git a/docs/regenerating-bindings.md b/docs/regenerating-bindings.md new file mode 100644 index 0000000..f43322c --- /dev/null +++ b/docs/regenerating-bindings.md @@ -0,0 +1,50 @@ +# Regenerating the bindings + +The committed `ApiDefinitions.cs`/`StructsAndEnums.cs` files were produced from the frameworks' +generated Objective-C headers (`-Swift.h`) and then hand-corrected. This page is the +process for carrying them across a dd-sdk-ios upgrade — because the tool that would automate it +is currently broken, and pretending otherwise makes upgrades slower, not faster. + +## Why Objective Sharpie is out + +Objective Sharpie 3.5.116 — the newest release — bundles a clang that fails to parse recent iOS +SDK module maps, so `./build/GenerateBindings.sh` cannot produce a fresh `Binding/` tree against +any Xcode this repository builds with. The script's header records the exact failure and honours +a `SHARPIE` override for the day a fixed release appears; until then, step "regenerate and diff" +is really "diff the headers and port by hand". + +## The header diff is the work list + +```bash +./build/DiffSwiftHeaders.sh 3.15.0 +``` + +downloads that release's `Datadog.xcframework.zip`, prints its SHA-256 (the value step 1 of the +upgrade guide records in `build/checksums.txt` — same bytes, one download), and writes one +`build/.Swift.h.diff` per framework whose generated header changed. A framework with +no diff needs nothing: its committed binding is already exact. + +Reading a diff: + +| In the header diff | In the committed binding | +| --- | --- | +| New `@interface` / `@protocol` | New `[BaseType]` interface in `ApiDefinitions.cs`. | +| New method/property on an existing type | New `[Export]` member on the existing interface. | +| `SWIFT_ENUM` added or extended | New/extended enum in `StructsAndEnums.cs` (check backing type). | +| Removed or renamed member | Remove/rename the export — a stale export crashes at runtime when called, not at build time. | +| `_Nullable` appearing on a parameter | Add `[NullAllowed]`. The reverse — `_Nonnull` — means do **not** add it, however tempting; see the `DDLogger` comment for a case where a `[NullAllowed]` "fix" would have turned a managed exception into a native crash. | +| Type moving between frameworks | Move the interface between binding projects and fix the project references. | + +The hand-applied corrections the committed files carry on top of raw generation — the fixes a +regeneration would undo — are listed in `build/GenerateBindings.sh`'s header. Check a ported diff +against that list before committing. + +## After porting + +1. `./build/FetchXcFrameworks.sh` (the hash recorded above makes this pass). +2. `./build/BuildNugets.sh` and both test suites — the on-simulator smoke tests are what catch a + selector that no longer exists at runtime. +3. Re-run `./build/DiffSwiftHeaders.sh libs ` if anything was ported from memory: + zero diffs against the new headers is the done condition. +4. Sync `DatadogNet.Mac`: its binding sources are enforced verbatim copies of these + (`DatadogNet.Mac/build/SyncBindingsFromiOS.sh`, guarded by its CI). diff --git a/src/Datadog.Binding.props b/src/Datadog.Binding.props index 88c41e3..90a1216 100644 --- a/src/Datadog.Binding.props +++ b/src/Datadog.Binding.props @@ -77,6 +77,20 @@ something fixable in the binding definitions. --> $(NoWarn);CS1591;CS0108;CS0109 + + + true + true diff --git a/src/DatadogNet.Core.iOS/ApiDefinitions.cs b/src/DatadogNet.Core.iOS/ApiDefinitions.cs index a8b5848..867c0ee 100644 --- a/src/DatadogNet.Core.iOS/ApiDefinitions.cs +++ b/src/DatadogNet.Core.iOS/ApiDefinitions.cs @@ -38,6 +38,7 @@ interface DDServerDateProvider } // @interface DDConfiguration + /// What DDDatadog.InitializeWithConfiguration starts: client token, environment, site, service, and batching/upload behaviour. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDConfiguration @@ -102,6 +103,8 @@ interface DDCrossPlatformExtension } // @interface DDDatadog + /// The Datadog SDK entry point: initialisation, user and account info, tracking consent, verbosity and data clearing. + /// Initialise once, as early as possible, with InitializeWithConfiguration. Docs: https://docs.datadoghq.com/real_user_monitoring/mobile_and_tv_monitoring/setup/ios/ [BaseType (typeof(NSObject))] interface DDDatadog { @@ -215,6 +218,7 @@ interface DDSharedContext } // @interface DDSite + /// The Datadog site the SDK uploads to, as static factories: Us1(), Us3(), Us5(), Eu1(), Ap1(), Ap2(), Us1_fed(). Match your organisation's region - the wrong site is the most common reason nothing appears in Datadog. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDSite @@ -253,6 +257,7 @@ interface DDSite } // @interface DDTrackingConsent + /// Tracking consent, as static factories: Granted(), NotGranted(), and Pending() - which collects and holds data on the device until consent is decided. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDTrackingConsent @@ -271,6 +276,7 @@ interface DDTrackingConsent } // @interface DDURLSessionInstrumentation + /// Automatic RUM resources and distributed tracing for NSUrlSession. Prefer the generic Enable<TDelegate>() from the Additions layer over passing a raw class handle. [BaseType (typeof(NSObject))] interface DDURLSessionInstrumentation { diff --git a/src/DatadogNet.CrashReporting.iOS/ApiDefinitions.cs b/src/DatadogNet.CrashReporting.iOS/ApiDefinitions.cs index 6c9e3d4..bf518f8 100644 --- a/src/DatadogNet.CrashReporting.iOS/ApiDefinitions.cs +++ b/src/DatadogNet.CrashReporting.iOS/ApiDefinitions.cs @@ -6,6 +6,7 @@ namespace DatadogCrashReporting { // @interface DDCrashReporter + /// KSCrash-based crash capture. Enable() after core initialisation; crashes are reported through RUM on the next launch, so RUM must be enabled too. [BaseType (typeof(NSObject))] interface DDCrashReporter { diff --git a/src/DatadogNet.Logs.iOS/ApiDefinitions.cs b/src/DatadogNet.Logs.iOS/ApiDefinitions.cs index 4cb98d6..a8fff60 100644 --- a/src/DatadogNet.Logs.iOS/ApiDefinitions.cs +++ b/src/DatadogNet.Logs.iOS/ApiDefinitions.cs @@ -6,6 +6,7 @@ namespace DatadogLogs { // @interface DDLogEvent + /// One log entry, as the event mapper sees it before upload. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDLogEvent @@ -300,6 +301,15 @@ interface DDLogEventUserInfo } // @interface DDLogger + /// Sends logs to Datadog, one method group per level. The Additions layer's Create(...) is the ergonomic factory, and its Log(level, message, exception, attributes) accepts where these raw methods, faithful to upstream, do not. + // + // The error/attributes parameters on the per-level methods below carry no [NullAllowed], and + // that is deliberate, not an omission: upstream declares them _Nonnull, and the Swift + // implementation takes a non-optional [String: Any] (Logs+objc.swift), so a nil smuggled + // through a [NullAllowed] binding would trap in the bridging thunk - a native crash instead of + // today's managed ArgumentNullException. A caller with no attributes uses the message-only + // overload, or the Additions layer's Log(level, message, exception?, attributes?), which + // accepts null for both and converts. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDLogger @@ -386,6 +396,7 @@ interface DDLogger } // @interface DDLoggerConfiguration + /// How one logger reports: service, name, network info, RUM and Trace bundling, remote sampling and threshold, console echo. [BaseType (typeof(NSObject))] interface DDLoggerConfiguration { @@ -418,6 +429,7 @@ interface DDLoggerConfiguration } // @interface DDLogs + /// Enables log collection. EnableWith once, after core initialisation and before creating loggers; the static attribute methods apply to every logger. [BaseType (typeof(NSObject))] interface DDLogs { @@ -447,6 +459,7 @@ interface DDLogs } // @interface DDLogsConfiguration + /// Options for DDLogs.EnableWith: a custom endpoint, and SetEventMapper - the on-device hook that can redact a log or return to drop it. [BaseType (typeof(NSObject))] interface DDLogsConfiguration { diff --git a/src/DatadogNet.RUM.iOS/ApiDefinitions.cs b/src/DatadogNet.RUM.iOS/ApiDefinitions.cs index e2cc4f7..32ae041 100644 --- a/src/DatadogNet.RUM.iOS/ApiDefinitions.cs +++ b/src/DatadogNet.RUM.iOS/ApiDefinitions.cs @@ -120,6 +120,7 @@ interface DDDefaultSwiftUIRUMViewsPredicate : DDSwiftUIRUMViewsPredicate } // @interface DDDefaultUIKitRUMActionsPredicate + /// The default UIKit action-tracking predicate: taps become RUM actions named after the control. [BaseType (typeof(NSObject))] interface DDDefaultUIKitRUMActionsPredicate : DDUIKitRUMActionsPredicate { @@ -128,6 +129,7 @@ interface DDDefaultUIKitRUMActionsPredicate : DDUIKitRUMActionsPredicate } // @interface DDDefaultUIKitRUMViewsPredicate + /// The default UIKit view-tracking predicate: roughly one RUM view per view controller. [BaseType (typeof(NSObject))] interface DDDefaultUIKitRUMViewsPredicate : DDUIKitRUMViewsPredicate { @@ -170,6 +172,7 @@ interface DDProfilingOptions } // @interface DDRUM + /// Enables Real User Monitoring. EnableWith(DDRUMConfiguration) once, after core initialisation. Docs: https://docs.datadoghq.com/real_user_monitoring/ [BaseType (typeof(NSObject))] interface DDRUM { @@ -744,6 +747,7 @@ interface DDRUMActionEventView } // @interface DDRUMConfiguration + /// RUM options: the application id, sampling, the UIKit view and action tracking predicates, frustrations, vitals and long tasks. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDRUMConfiguration @@ -2132,6 +2136,7 @@ interface DDRUMLongTaskEventView } // @interface DDRUMMonitor + /// Reports RUM views, actions, resources and errors; Shared() is the instance. The Additions layer adds using-scoped views, Dictionary<string, object?> attributes, exception overloads and GetCurrentSessionIdAsync. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDRUMMonitor diff --git a/src/DatadogNet.SessionReplay.iOS/ApiDefinitions.cs b/src/DatadogNet.SessionReplay.iOS/ApiDefinitions.cs index 6b83230..e11527b 100644 --- a/src/DatadogNet.SessionReplay.iOS/ApiDefinitions.cs +++ b/src/DatadogNet.SessionReplay.iOS/ApiDefinitions.cs @@ -7,6 +7,7 @@ namespace DatadogSessionReplay { // @interface DDSessionReplay + /// Enables Session Replay. Requires RUM. EnableWith(DDSessionReplayConfiguration); recording control is on the instance. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDSessionReplay @@ -37,6 +38,7 @@ interface DDSessionReplay } // @interface DDSessionReplayConfiguration + /// Replay options: the sample rate and the three on-device privacy levels - required arguments, so what is redacted before upload is always an explicit choice. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDSessionReplayConfiguration @@ -70,6 +72,7 @@ interface DDSessionReplayConfiguration } // @interface DDSessionReplayPrivacyOverrides + /// Per-view overrides of the global privacy levels, reached through a view's GetDdSessionReplayPrivacyOverrides(). [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDSessionReplayPrivacyOverrides diff --git a/src/DatadogNet.Trace.iOS/ApiDefinitions.cs b/src/DatadogNet.Trace.iOS/ApiDefinitions.cs index f40d79c..ea5f986 100644 --- a/src/DatadogNet.Trace.iOS/ApiDefinitions.cs +++ b/src/DatadogNet.Trace.iOS/ApiDefinitions.cs @@ -134,6 +134,7 @@ interface OTTracer } // @interface DDB3HTTPHeadersWriter + /// Writes B3-format distributed-tracing headers. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDB3HTTPHeadersWriter @@ -146,6 +147,7 @@ interface DDB3HTTPHeadersWriter } // @interface DDHTTPHeadersWriter + /// Writes Datadog-format distributed-tracing headers, for continuing a trace into your backend by hand. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDHTTPHeadersWriter @@ -158,6 +160,7 @@ interface DDHTTPHeadersWriter } // @interface DDTrace + /// Enables APM distributed tracing. EnableWith(DDTraceConfiguration) once, after core initialisation. [BaseType (typeof(NSObject))] interface DDTrace { @@ -171,6 +174,7 @@ interface DDTrace } // @interface DDTraceConfiguration + /// Trace options: sampling, network info, RUM bundling and URLSession first-party tracing. [BaseType (typeof(NSObject))] interface DDTraceConfiguration { @@ -230,6 +234,7 @@ interface DDTraceURLSessionTracking } // @interface DDTracer + /// The OpenTracing-style tracer: build spans, inject and extract trace headers. The Additions layer adds SetError(Exception), one-call header injection and trace/span-id reading. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDTracer @@ -268,6 +273,7 @@ interface DDTracer } // @interface DDW3CHTTPHeadersWriter + /// Writes W3C tracecontext distributed-tracing headers. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDW3CHTTPHeadersWriter diff --git a/src/DatadogNet.WebViewTracking.iOS/ApiDefinitions.cs b/src/DatadogNet.WebViewTracking.iOS/ApiDefinitions.cs index c7ebd32..b8f541d 100644 --- a/src/DatadogNet.WebViewTracking.iOS/ApiDefinitions.cs +++ b/src/DatadogNet.WebViewTracking.iOS/ApiDefinitions.cs @@ -7,6 +7,7 @@ namespace DatadogWebViewTracking { // @interface DDWebViewTracking + /// Bridges RUM events and logs out of a WKWebView whose page runs the Datadog Browser SDK, into the surrounding native session. [BaseType (typeof(NSObject))] [DisableDefaultCtor] interface DDWebViewTracking From bf6c3f449fded41993521484ebd43e30f3489fde Mon Sep 17 00:00:00 2001 From: Siarhei Bakatsiuk Date: Fri, 24 Jul 2026 11:38:25 +0300 Subject: [PATCH 2/3] Prepare 3.14.0.3 Version bump, README pins to match (now CI-enforced), and the curated release notes - which this release is the first to surface on nuget.org through PackageReleaseNotes. Co-Authored-By: Claude Fable 5 --- Directory.Build.props | 2 +- README.md | 12 +++---- docs/release-notes/3.14.0.3.md | 60 ++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 docs/release-notes/3.14.0.3.md diff --git a/Directory.Build.props b/Directory.Build.props index 66942d0..6e9cfa1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,7 +16,7 @@ impossible to tell which Datadog release a given CrashReporter package belonged to. --> 3.14.0 - 2 + 3 $(DatadogNativeVersion).$(DatadogBindingRevision) s.bokatuk diff --git a/README.md b/README.md index d576e8b..036d778 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 - - + + ``` @@ -483,7 +483,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.2 net9.0-ios18.0 +./.github/scripts/run-simulator-tests.sh 3.14.0.3 net9.0-ios18.0 ``` Build and run the sample: @@ -536,7 +536,7 @@ dropping a package — update the `FRAMEWORKS` list, add or remove the binding p ## Releasing -Tag it. `v3.14.0.2` builds, tests, publishes every package to nuget.org via trusted publishing, and +Tag it. `v3.14.0.3` builds, tests, publishes every package to nuget.org via trusted publishing, and creates a GitHub release. The tag drives which native SDK is bound, so an older line can be released by tagging it. diff --git a/docs/release-notes/3.14.0.3.md b/docs/release-notes/3.14.0.3.md new file mode 100644 index 0000000..0e4e682 --- /dev/null +++ b/docs/release-notes/3.14.0.3.md @@ -0,0 +1,60 @@ +## 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, which came +out of a full ergonomics review of the four DatadogNet repositories. + +## IntelliSense on the generated tier + +The main entry types now carry doc summaries — 26 of them, written in the `ApiDefinitions.cs` +files and carried by the binding generator into the packages' XML docs (measured before relying +on it, not assumed): `DDDatadog`, `DDConfiguration`, `DDSite`, `DDTrackingConsent`, +`DDURLSessionInstrumentation`, every module's `Enable`/configuration types, `DDRUMMonitor`, the +UIKit tracking predicates, `DDLogger`, `DDLogs`, `DDLogEvent`, `DDTracer`, the three header +writers, `DDSessionReplay` and its privacy overrides, `DDWebViewTracking` and `DDCrashReporter`. +The long tail of generated members — the 377 RUM model types, mostly — stays bare; the C# names +map 1:1 onto the Objective-C ones, so upstream's reference covers them. + +## The `DDLogger` null-attributes papercut: resolved by documentation, deliberately + +`3.14.0.2`'s notes flagged that `Info(message, attributes)` throws on a `null` dictionary, and +suggested a missing `[NullAllowed]`. Investigated properly: upstream declares those parameters +`_Nonnull`, and the Swift implementation takes a non-optional `[String: Any]` — so a +`[NullAllowed]` "fix" would have traded today's managed `ArgumentNullException` for a native +crash in the bridging thunk. The binding stays faithful. The rationale now lives as a comment on +the interface in `ApiDefinitions.cs` (so a future regeneration does not "fix" it wrongly) and in +the README's troubleshooting section; the Additions layer's +`Log(level, message, exception?, attributes?)` remains the null-tolerant path. + +## Trim and AOT analyzers, permanently on + +The whole surface — generated tier and Additions — analyses clean, so an unsafe reflection +pattern introduced later surfaces at build time. `IsTrimmable` is deliberately **not** set: it +would opt the assemblies into consumer-side trimming the device tests do not exercise; +`src/Datadog.Binding.props` records the reasoning. + +## Release pipeline + +- **nuget.org now shows these notes.** `docs/release-notes/.md` is packed into + `PackageReleaseNotes`, with the GitHub releases page as fallback for versions without a + curated file. +- **README versions can no longer go stale silently** — the install snippets sat at `3.14.0.1` + while `3.14.0.2` shipped. `build/CheckReadmeVersions.sh` runs in CI and fails when the README's + pins disagree with `Directory.Build.props`. +- **The upgrade path while Objective Sharpie is broken is now a tool, not archaeology.** + `./build/DiffSwiftHeaders.sh ` writes one diff per framework whose generated + `-Swift.h` changed — the porting work list — and prints the archive's SHA-256 for + `build/checksums.txt` from the same bytes. `docs/regenerating-bindings.md` explains how to + read a diff, including why a `_Nonnull` in the header must *not* become `[NullAllowed]`. + +## net8 sunset + +Stated policy, so the trade-off does not persist by inertia: the `net8.0-ios18.0` head — past +its platform support window since MAUI 8 left support on 14 May 2025, though still verified by +the simulator checks — is dropped in the first release after .NET 8 itself leaves support on +**10 November 2026**. + +## Upgrading from 3.14.0.2 + +Nothing to change: same native SDK, same API. The new doc summaries appear on restore. From 864dfbc88f62a481c407a0ada831d8f2b87e3f2d Mon Sep 17 00:00:00 2001 From: Siarhei Bakatsiuk Date: Fri, 24 Jul 2026 12:16:43 +0300 Subject: [PATCH 3/3] Sample: cover the full usable surface DatadogNet.WebViewTracking.iOS joins the sample - the one package with a callable API that nothing exercised - with a tracked WKWebView page and a README usage section to match. A button makes an instrumented NSUrlSession request through the generic Enable(), the automatic path the sample only ever showed manually, and another toggles a per-view Session Replay privacy override. Release notes extended accordingly. Co-Authored-By: Claude Fable 5 --- README.md | 15 ++++ docs/release-notes/3.14.0.3.md | 8 ++ .../DatadogNetExample.csproj | 1 + samples/DatadogNet.iOS.Example/MainPage.xaml | 21 +++++ .../DatadogNet.iOS.Example/MainPage.xaml.cs | 72 ++++++++++++++++++ samples/DatadogNet.iOS.Example/WebViewPage.cs | 76 +++++++++++++++++++ 6 files changed, 193 insertions(+) create mode 100644 samples/DatadogNet.iOS.Example/WebViewPage.cs diff --git a/README.md b/README.md index 036d778..16ac598 100644 --- a/README.md +++ b/README.md @@ -270,6 +270,21 @@ overrides.TextAndInputPrivacy = DDTextAndInputPrivacyLevelOverride.MaskAll; overrides.Hide = new NSNumber(true); ``` +### WebView tracking + +```csharp +using DatadogWebViewTracking; +using Foundation; + +DDWebViewTracking.EnableWithWebView( + webView, new NSSet(new NSString("example.com")), logsSampleRate: 100); +// ...and when the web view goes away - the bridge holds a reference to it: +DDWebViewTracking.DisableWithWebView(webView); +``` + +The page inside must run the Datadog Browser SDK, and its host must be on the allowlist — +which is an allowlist because the bridge lets page JavaScript write into your RUM session. + ### Crash reporting Add `DatadogNet.CrashReporting.iOS`, then enable it **after** initializing the SDK: diff --git a/docs/release-notes/3.14.0.3.md b/docs/release-notes/3.14.0.3.md index 0e4e682..0191b48 100644 --- a/docs/release-notes/3.14.0.3.md +++ b/docs/release-notes/3.14.0.3.md @@ -48,6 +48,14 @@ would opt the assemblies into consumer-side trimming the device tests do not exe `build/checksums.txt` from the same bytes. `docs/regenerating-bindings.md` explains how to read a diff, including why a `_Nonnull` in the header must *not* become `[NullAllowed]`. +## Sample + +The sample now exercises the whole usable surface: `DatadogNet.WebViewTracking.iOS` joins its +references with a tracked `WKWebView` page, a button makes an instrumented `NSUrlSession` request +through the generic `Enable()` — the one automatic-instrumentation path nothing +demonstrated — and a per-view Session Replay privacy override shows the granular control the +global levels cannot give. + ## net8 sunset Stated policy, so the trade-off does not persist by inertia: the `net8.0-ios18.0` head — past diff --git a/samples/DatadogNet.iOS.Example/DatadogNetExample.csproj b/samples/DatadogNet.iOS.Example/DatadogNetExample.csproj index 9dbb2ab..0df89d6 100644 --- a/samples/DatadogNet.iOS.Example/DatadogNetExample.csproj +++ b/samples/DatadogNet.iOS.Example/DatadogNetExample.csproj @@ -64,6 +64,7 @@ + diff --git a/samples/DatadogNet.iOS.Example/MainPage.xaml b/samples/DatadogNet.iOS.Example/MainPage.xaml index c93cb1c..a58ec38 100644 --- a/samples/DatadogNet.iOS.Example/MainPage.xaml +++ b/samples/DatadogNet.iOS.Example/MainPage.xaml @@ -53,6 +53,27 @@ +