DatadogNet.RUM.iOS
3.14.0.4
See the version list below for details.
dotnet add package DatadogNet.RUM.iOS --version 3.14.0.4
NuGet\Install-Package DatadogNet.RUM.iOS -Version 3.14.0.4
<PackageReference Include="DatadogNet.RUM.iOS" Version="3.14.0.4" />
<PackageVersion Include="DatadogNet.RUM.iOS" Version="3.14.0.4" />
<PackageReference Include="DatadogNet.RUM.iOS" />
paket add DatadogNet.RUM.iOS --version 3.14.0.4
#r "nuget: DatadogNet.RUM.iOS, 3.14.0.4"
#:package DatadogNet.RUM.iOS@3.14.0.4
#addin nuget:?package=DatadogNet.RUM.iOS&version=3.14.0.4
#tool nuget:?package=DatadogNet.RUM.iOS&version=3.14.0.4
DatadogNet.iOS
.NET for iOS and .NET MAUI bindings for the native
Datadog iOS SDK (dd-sdk-ios).
Enable Datadog Real User Monitoring, Logs, Trace, Session Replay, WebView tracking and crash
reporting from C#, in a net8.0-ios, net9.0-ios or net10.0-ios app.
dotnet add package DatadogNet.Core.iOS
dotnet add package DatadogNet.RUM.iOS
using DatadogCore;
using DatadogRUM;
var configuration = new DDConfiguration(clientToken: "<CLIENT_TOKEN>", env: "production")
{
Service = "my-app",
Site = DDSite.Us1(),
};
DDDatadog.InitializeWithConfiguration(configuration, DDTrackingConsent.Granted());
DDRUM.EnableWith(new DDRUMConfiguration(applicationID: "<RUM_APPLICATION_ID>"));
Built against dd-sdk-ios 3.14.0. Coming from the 2.x packages? See Migrating from 2.x — the types moved namespace, so it is not a version bump alone.
Contents
- Packages
- Installing
- Usage
- Convenience API
- Migrating from 2.x
- How this repository works
- Building locally
- Upgrading the Datadog SDK
- Releasing
- Troubleshooting
- Licence
Packages
Twelve packages, one per native framework, plus a compatibility meta-package. Versions are
<dd-sdk-ios version>.<binding revision> — 3.14.0.1 is dd-sdk-ios 3.14.0, binding revision
1. The fourth component belongs to this repository and advances when the bindings or packaging
change while the native binaries stay put.
| Package | Wraps | Depends on | What it is for |
|---|---|---|---|
DatadogNet.Core.iOS |
DatadogCore |
Internal | SDK init, consent, user/account info, URLSession instrumentation. Always needed. |
DatadogNet.RUM.iOS |
DatadogRUM |
Core, Internal | Real User Monitoring — the whole DDRUM* API. |
DatadogNet.Logs.iOS |
DatadogLogs |
Core, Internal | Log collection. |
DatadogNet.Trace.iOS |
DatadogTrace |
Core, Internal, OpenTelemetryApi | Distributed tracing (APM). |
DatadogNet.SessionReplay.iOS |
DatadogSessionReplay |
Core, Internal | Session Replay. Requires RUM. |
DatadogNet.WebViewTracking.iOS |
DatadogWebViewTracking |
Core, Internal | Bridges RUM/Logs out of a WKWebView. |
DatadogNet.CrashReporting.iOS |
DatadogCrashReporting |
Core, Internal | Native crash capture (KSCrash). Reports through RUM. |
DatadogNet.Flags.iOS |
DatadogFlags |
Core, Internal | Feature flags. No C# API yet — see below. |
DatadogNet.Profiling.iOS |
DatadogProfiling |
Core, Internal | Continuous profiling. No C# API yet — see below. |
DatadogNet.Internal.iOS |
DatadogInternal |
— | Shared foundation. Pulled in transitively. |
DatadogNet.OpenTelemetryApi.iOS |
OpenTelemetryApi |
— | OpenTelemetry API types for trace propagation. |
DatadogNet.Objc.iOS |
— (meta-package) | Core, RUM, Logs, Trace, SessionReplay | Compatibility only. Redirects an existing 2.x PackageReference. New apps ignore it. |
Reference the modules you use. A typical app takes DatadogNet.Core.iOS plus DatadogNet.RUM.iOS,
and adds Logs, Trace, SessionReplay, CrashReporting or WebViewTracking as needed.
DatadogNet.Flags.iOSandDatadogNet.Profiling.iOSexpose no callable API. Upstream ships these modules but has not projected them into Objective-C, so there is nothing for C# to bind. The packages exist so the set mirrors the SDK and so adding the API later is a version bump. There is no reason to reference them today.
The dependency graph is the real one, read off the frameworks' Mach-O load commands.
DatadogInternalis the root; every product module depends on it, andDatadogTracealso onOpenTelemetryApi.
Target frameworks: net8.0-ios18.0, net9.0-ios18.0, net10.0-ios26.0.
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
<ItemGroup>
<PackageReference Include="DatadogNet.Core.iOS" Version="3.14.0.4" />
<PackageReference Include="DatadogNet.RUM.iOS" Version="3.14.0.4" />
</ItemGroup>
In a multi-targeted MAUI project, guard the references so an Android or Windows head does not try to restore them:
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
<PackageReference Include="DatadogNet.Core.iOS" Version="3.14.0.4" />
<PackageReference Include="DatadogNet.RUM.iOS" Version="3.14.0.4" />
</ItemGroup>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">12.2</SupportedOSPlatformVersion>
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
is a working MAUI app that does all of the below;
Datadog.cs
is the setup in one file.
Initialize
Once, as early as possible — in MAUI, in MauiProgram.CreateMauiApp before the builder runs, so
crash reporting and startup metrics cover the whole launch.
using DatadogCore;
using DatadogInternal; // DDCoreLoggerLevel
var configuration = new DDConfiguration(clientToken: "<CLIENT_TOKEN>", env: "production")
{
Service = "my-app",
Site = DDSite.Us1(), // Us1, Us3, Us5, Eu1, Ap1, Ap2, Us1_fed
};
DDDatadog.InitializeWithConfiguration(configuration, DDTrackingConsent.Granted());
DDDatadog.SetVerbosityLevel(DDCoreLoggerLevel.Warn); // SDK problems to the Xcode console
DDSite and DDTrackingConsent are classes of static factory methods, so the values are called:
DDSite.Us1(), DDTrackingConsent.Granted().
RUM
using DatadogRUM;
var rum = new DDRUMConfiguration(applicationID: "<RUM_APPLICATION_ID>")
{
SessionSampleRate = 100,
TrackFrustrations = true,
TrackMemoryWarnings = true, // new in 3.0
UiKitViewsPredicate = new DDDefaultUIKitRUMViewsPredicate(),
UiKitActionsPredicate = new DDDefaultUIKitRUMActionsPredicate(),
};
DDRUM.EnableWith(rum);
Manual reporting through DDRUMMonitor.Shared():
var monitor = DDRUMMonitor.Shared();
using (monitor.StartView("checkout", "Checkout"))
{
// View-level attributes propagate to the actions, resources and errors recorded here - a 3.0
// change that removes the need to repeat them on every call.
monitor.AddViewAttributes(new Dictionary<string, object?> { ["cart.total"] = 42.50m });
monitor.AddAction(DDRUMActionType.Tap, "pay");
try { /* ... */ }
catch (Exception exception) { monitor.AddError(exception); }
}
Logs
using DatadogLogs;
DDLogs.Enable();
var logger = DDLogger.Create(name: "app", bundleWithRumEnabled: true);
logger.Log(DDLogLevel.Info, "user signed in");
logger.Log(DDLogLevel.Error, "payment failed", exception, new Dictionary<string, object?>
{
["order.id"] = orderId,
});
Redact or drop logs before upload with an event mapper:
var configuration = new DDLogsConfiguration(customEndpoint: null);
configuration.SetEventMapper(logEvent =>
{
logEvent.Message = Redact(logEvent.Message);
return logEvent; // or return null to drop the event
});
DDLogs.EnableWith(configuration);
Trace
using DatadogTrace;
DDTrace.EnableWith(new DDTraceConfiguration
{
SampleRate = 100,
NetworkInfoEnabled = true,
BundleWithRumEnabled = true,
});
Header writers for distributed tracing — DDHTTPHeadersWriter (Datadog), DDB3HTTPHeadersWriter
(B3) and DDW3CHTTPHeadersWriter (W3C tracecontext). None takes a sampling argument in 3.x;
sampling is derived from the RUM session.id.
Network instrumentation
Automatic RUM resource collection and trace propagation, from 3.0 through the unified
DDURLSessionInstrumentation. This binding resolves the delegate class for you:
using DatadogCore;
DDURLSessionInstrumentation.Enable<MySessionDelegate>();
// ...
DDURLSessionInstrumentation.Disable<MySessionDelegate>();
MySessionDelegate must be an NSObject implementing INSUrlSessionDataDelegate with a
[Register] attribute. Attach it to your NSUrlSession as usual.
Session Replay
using DatadogSessionReplay;
DDSessionReplay.EnableWith(new DDSessionReplayConfiguration(
replaySampleRate: 100,
textAndInputPrivacyLevel: DDTextAndInputPrivacyLevel.MaskAll,
imagePrivacyLevel: DDImagePrivacyLevel.MaskAll,
touchPrivacyLevel: DDTouchPrivacyLevel.Hide));
Requires RUM. The three levels are required arguments, so what gets redacted on the device is always an explicit choice. A single view can override them:
var overrides = myView.GetDdSessionReplayPrivacyOverrides();
overrides.TextAndInputPrivacy = DDTextAndInputPrivacyLevelOverride.MaskAll;
overrides.Hide = new NSNumber(true);
WebView tracking
using DatadogWebViewTracking;
using Foundation;
DDWebViewTracking.EnableWithWebView(
webView, new NSSet<NSString>(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:
using DatadogCrashReporting;
DDCrashReporter.Enable();
The engine is KSCrash. Crashes are reported on the next launch as RUM errors — enable RUM too, or they are not delivered. Upload your app's dSYMs to Datadog for symbolication.
User, account and consent
DDDatadog.SetUserInfo("user-123", "Ada Lovelace", "ada@example.com");
DDDatadog.SetAccountInfo("acct-42", "Acme Corp");
DDDatadog.SetTrackingConsent(TrackingConsent.Pending);
Convenience API
The generated binding is a faithful projection of Objective-C, which makes some things clumsy in C#. Each product module adds a small hand-written layer over it. The generated members are all still there; nothing is hidden or renamed.
| Instead of | Write |
|---|---|
monitor.StartViewWithKey(...) … StopViewWithKey(...) matched by key |
using (monitor.StartView("k", "Name")) |
new NSDictionary<NSString, NSObject>(...) with hand-wrapped values |
new Dictionary<string, object?> { ["k"] = 42 } on any overload below |
monitor.AddActionWithType(type, name, attributes) |
monitor.AddAction(type, name, attributes?) |
monitor.AddErrorWithMessage(...) with an NSError you do not have |
monitor.AddError(exception) |
monitor.AddViewAttributes(nsDictionary) |
monitor.AddViewAttributes(dictionary) / RemoveViewAttributes(params keys) |
six log methods per level, plus an NSError you do not have |
logger.Log(level, message, exception?, attributes?) |
new DDLoggerConfiguration(service, name, bool, bool, bool, float, level, bool) |
DDLogger.Create(name: "app") |
DDDatadog.SetUserInfoWithUserId(id, null, null, empty) |
DDDatadog.SetUserInfo(id) |
DDTrackingConsent.Granted() (a factory, not an enum) |
DDDatadog.SetTrackingConsent(TrackingConsent.Granted) |
DDURLSessionInstrumentation.EnableWithConfiguration(...) with a raw Class handle |
DDURLSessionInstrumentation.Enable<TDelegate>() |
| a one-element dictionary round-trip to convert one value | DatadogAttributes.ToNSObject(value, key) |
monitor.AddAttributeForKey(k, nsObject) |
monitor.AddAttribute(k, value) / AddViewAttribute / AddFeatureFlagEvaluation |
monitor.CurrentSessionIDWithCompletion(block) |
await monitor.GetCurrentSessionIdAsync() |
| construct a writer, pass it as the carrier, read headers back off it — once per format | span.InjectHeaders(tracer) |
| no way at all to read a span's ids | span.GetTraceId(tracer) / span.GetSpanId(tracer) |
span.SetErrorWithKind(kind, message, stack) from an exception you must decompose |
span.SetError(exception) |
Attribute values may be strings, any numeric type, bool, DateTime, DateTimeOffset, Guid,
enums, NSObjects, 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.
The asymmetry is not a choice — it is Datadog's wire format, and matching it is what makes a RUM
resource correlate with its APM trace. dd-sdk-android's own DatadogInterceptor writes
_dd.trace_id as DatadogTraceId.toHexString() and _dd.span_id as String.valueOf(long).
Reading the ids takes work because OTSpanContext exposes none: they are recovered by injecting
into a Datadog-format writer, and the trace id arrives in two pieces — the decimal low 64 bits in
x-datadog-trace-id, and the high 64 as _dd.p.tid inside x-datadog-tags. Using only the former
yields a decimal string naming half of a different-looking id, which is a mistake that reached a
release of a consumer of this package before it was caught.
API coverage
Measured by diffing each framework's generated -Swift.h against ApiDefinitions.cs, not estimated.
| Framework | Objective-C types | Bound |
|---|---|---|
DatadogRUM |
377 | 377 |
DatadogLogs |
16 | 16 |
DatadogCore |
12 | 12 |
DatadogTrace |
12 | 12 |
DatadogSessionReplay |
4 | 4 |
DatadogInternal |
2 | 2 |
DatadogCrashReporting |
1 | 1 |
DatadogWebViewTracking |
1 | 1 |
Member coverage is the same story: of 61 selectors and properties on DatadogCore, 59 are exported,
and the two that are not are init and new, which [DisableDefaultCtor] removes on purpose.
What is missing is missing upstream. Three parts of dd-sdk-ios have no Objective-C projection at all, so there is nothing for a binding to bind:
| Swift types | ObjC types | Consequence | |
|---|---|---|---|
DatadogFlags |
15 | 0 | Feature Flags are unreachable from C#. The API leans on generics (FlagDetails<T>) and enums with associated values (AnyValue), neither of which Swift projects into Objective-C. |
DatadogProfiling |
2 | 0 | Profiling is unreachable. |
OpenTelemetryApi |
— | no -Swift.h at all |
A pure-Swift module. DatadogNet.OpenTelemetryApi.iOS can only ever be a link-time dependency of DatadogNet.Trace.iOS. |
DatadogTrace is additionally 24 public Swift types projected down to 12, and the casualty is
OTelTracerProvider — so OpenTelemetry tracing is unreachable even though OpenTracing is not.
DatadogNet.Flags.iOS and DatadogNet.Profiling.iOS therefore ship the frameworks and expose no
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/.
Nothing ships yet.
Migrating from 2.x
The 2.x packages (2.30.2.1 and earlier) bound dd-sdk-ios 2.x, where every DD* type lived in a
single DatadogObjc namespace. dd-sdk-ios 3.0 deleted that framework and moved the types into the
module each belongs to, so migration is a code change, not a version bump.
Namespaces. Replace using DatadogObjc; with a per-feature set:
-using DatadogObjc;
+using DatadogCore; // DDDatadog, DDConfiguration, DDSite, DDTrackingConsent
+using DatadogRUM; // DDRUM, DDRUMMonitor, DDRUMConfiguration, RUM event models
+using DatadogLogs; // DDLogs, DDLogger, DDLoggerConfiguration
+using DatadogTrace; // DDTrace, DDTracer, header writers
+using DatadogInternal; // DDCoreLoggerLevel, DDTracingHeaderType
Packages. If you referenced DatadogNet.Objc.iOS, it still resolves — it is now a
dependency-only meta-package pulling Core, RUM, Logs, Trace and SessionReplay — but prefer moving to
the product modules directly. DatadogNet.CrashReporter.iOS is retired (KSCrash replaced
PLCrashReporter and is part of DatadogNet.CrashReporting.iOS).
API changes, all upstream:
| 2.x | 3.x |
|---|---|
DDSite.Us1 (property) |
DDSite.Us1() (factory method) |
DDTrackingConsent.Granted |
DDTrackingConsent.Granted() |
DatadogURLSessionDelegate / DDNSURLSessionDelegate |
DDURLSessionInstrumentation.Enable<T>() |
DDOTelHTTPHeadersWriter |
DDW3CHTTPHeadersWriter |
new DDHTTPHeadersWriter(samplingStrategy, injection) |
new DDHTTPHeadersWriter(injection) |
RUMView(path:) |
RUMView(name:) |
| crashes via Logs | crashes via RUM error tracking |
If you cannot make these changes yet, stay on 2.30.2.1 — it remains on nuget.org. Note that
the 2.x line receives no further upstream fixes.
How this repository works
DataDog/dd-sdk-ios release ──► Datadog.xcframework.zip (one archive, eleven frameworks)
│ build/FetchXcFrameworks.sh — download, verify SHA-256, strip to iOS slices
▼
libs/<Framework>.xcframework
│ src/DatadogNet.<Package>.iOS/ — binding project + committed ApiDefinitions.cs
│ build/BuildNugets.sh — pack twice (net9 band, net10 band), then merge
▼
artifacts/*.nupkg ──► nuget.org
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,
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
grafts the net10 assets and dependency groups into the net9 packages.
Layout
| Path | What |
|---|---|
src/DatadogNet.*.iOS/ |
One binding project per framework. ApiDefinitions.cs and StructsAndEnums.cs are committed sources. |
src/Datadog.Binding.props |
Everything the binding projects share, so each .csproj is a dozen lines. |
src/DatadogNet.*.iOS/Additions/ |
The hand-written convenience API, in Core, RUM and Logs. |
src/DatadogNet.Objc.iOS/ |
The compatibility meta-package: dependencies, no assembly. |
build/ |
Fetch, pack, merge and binding-regeneration scripts, plus the checksum pins. |
tests/DatadogNet.iOS.PackageTests/ |
Asserts the shape of the packed .nupkgs — 127 checks. |
tests/DatadogNet.iOS.DeviceTests/ |
An iOS app that consumes the packed packages and drives the real SDK on a simulator. |
samples/DatadogNet.iOS.Example/ |
A MAUI app doing the same, the way an app would. |
The tests consume the packed .nupkgs from artifacts/ rather than referencing the projects, so
they exercise what actually gets published — including the inter-package dependencies, which a
ProjectReference would bypass entirely.
DatadogNet.sln deliberately contains the binding projects and the tests but not the sample. A
solution-wide Release build would AOT-compile the MAUI app for device across both of its target
frameworks, which takes longer than everything else in the repository put together. Build the sample
directly, as the command in the next section does.
Building locally
Requires macOS, Xcode, and the .NET 9 and .NET 10 SDKs with the ios workload.
./build/FetchXcFrameworks.sh # ~200 MB, verified against build/checksums.txt
./build/BuildNugets.sh # packs all twelve into artifacts/
dotnet test tests/DatadogNet.iOS.PackageTests
Run the on-simulator smoke tests against the packed packages:
./.github/scripts/run-simulator-tests.sh 3.14.0.4 net9.0-ios18.0
Build and run the sample:
dotnet build samples/DatadogNet.iOS.Example/DatadogNetExample.csproj -p:RuntimeIdentifier=iossimulator-arm64
Building anything that targets
net10.0-ios26.0needs 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 theselect-xcodeaction.
Upgrading the Datadog SDK
./build/BumpNativeVersion.sh 3.15.0does the mechanical part: bumpsDatadogNativeVersioninDirectory.Build.propsand resetsDatadogBindingRevisionto1; pins the new archive's hash inbuild/checksums.txtfrom the digest GitHub publishes for the release asset, verified against a fresh download of the same bytes (build/UpdateChecksums.sh); pointsPackageValidationBaselineVersionat the version being left behind; rewrites this README's badge, prose and install snippets; and scaffoldsdocs/release-notes/3.15.0.1.md../build/DiffSwiftHeaders.sh 3.15.0— before fetching, whilelibs/still holds the previous release — writes onebuild/<Framework>.Swift.h.diffper framework whose generated header changed, which is the porting work list;docs/regenerating-bindings.mdexplains how to read one. (./build/GenerateBindings.shis the automated alternative for when Objective Sharpie works again — it writes toBinding/, not over the committed sources, and its header lists the fixes the committed files carry that regenerating would otherwise undo.)./build/FetchXcFrameworks.sh— replaceslibs/with the new release's frameworks.- Port the diffs into the committed
ApiDefinitions.csfiles. ./build/BuildNugets.shand run both test suites.- Finish the scaffolded release notes — its TODOs mark what cannot be generated — then tag (see 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
Packages.All in the package tests.
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.hheaders directly;GenerateBindings.shdocuments the problem and honours aSHARPIEoverride for when it is fixed. Until then the header diff is the upgrade path — seedocs/regenerating-bindings.md.
Releasing
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.
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.<pr>.<run> prerelease of the whole set.
Curated notes in docs/release-notes/<version>.md replace the generated commit list when present.
Troubleshooting
DDDatadog/DDRUMMonitor/DDLogger could not be found. The types moved namespace in 3.0. Add
the per-module using — see Migrating from 2.x.
Nothing appears in Datadog. Check Site matches your organisation's region — the most common
cause. Set DDDatadog.SetVerbosityLevel(DDCoreLoggerLevel.Debug) to see the SDK's own diagnostics
in the Xcode console.
Crashes do not show up. Crash reports are delivered through RUM in 3.x, so RUM must be enabled 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.
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.*.
Licence
The binding code in this repository is
MIT. 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/.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0-ios18.0 is compatible. net9.0-ios was computed. net9.0-ios18.0 is compatible. net10.0-ios was computed. net10.0-ios26.0 is compatible. |
-
net10.0-ios26.0
- DatadogNet.Core.iOS (>= 3.14.0.4)
- DatadogNet.Internal.iOS (>= 3.14.0.4)
-
net8.0-ios18.0
- DatadogNet.Core.iOS (>= 3.14.0.4)
- DatadogNet.Internal.iOS (>= 3.14.0.4)
-
net9.0-ios18.0
- DatadogNet.Core.iOS (>= 3.14.0.4)
- DatadogNet.Internal.iOS (>= 3.14.0.4)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on DatadogNet.RUM.iOS:
| Package | Downloads |
|---|---|
|
DatadogNet.Objc.iOS
Dependency-only compatibility meta-package for the Datadog iOS SDK bindings. dd-sdk-ios 3.0 removed the DatadogObjc framework and moved its DD* types into the product modules, so this package now just pulls in DatadogNet.Core.iOS, DatadogNet.RUM.iOS, DatadogNet.Logs.iOS, DatadogNet.Trace.iOS and DatadogNet.SessionReplay.iOS. Namespaces changed with the move, so code needs updating - see the migration table in the README. New apps should reference the product modules directly. |
|
|
DatadogNet
One cross-platform Datadog API for .NET MAUI: RUM, Logs, Trace and Session Replay on Android, iOS and Mac Catalyst from shared code, over the native dd-sdk-android and dd-sdk-ios bindings. Restores and no-ops on Windows, so a multi-headed app needs no conditionals. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.14.0.5 | 118 | 7/28/2026 |
| 3.14.0.5-beta.11.23 | 26 | 7/30/2026 |
| 3.14.0.5-beta.10.21 | 25 | 7/30/2026 |
| 3.14.0.5-beta.9.20 | 35 | 7/29/2026 |
| 3.14.0.5-beta.8.19 | 31 | 7/28/2026 |
| 3.14.0.4 | 89 | 7/26/2026 |
| 3.14.0.4-beta.7.16 | 37 | 7/26/2026 |
| 3.14.0.3 | 130 | 7/24/2026 |
| 3.14.0.3-beta.6.15 | 43 | 7/24/2026 |
| 3.14.0.3-beta.6.14 | 42 | 7/24/2026 |
| 3.14.0.2 | 106 | 7/23/2026 |
| 3.14.0.2-beta.5.12 | 49 | 7/23/2026 |
| 3.14.0.2-beta.5.11 | 43 | 7/23/2026 |
| 3.14.0.2-beta.5.10 | 50 | 7/23/2026 |
| 3.14.0.1 | 99 | 7/23/2026 |
| 3.14.0.1-beta.3.8 | 62 | 7/23/2026 |
| 2.30.2.2 | 106 | 7/23/2026 |
| 2.30.2.1 | 95 | 7/22/2026 |
| 2.30.2.1-beta.2.4 | 58 | 7/22/2026 |
| 2.17.0.1-beta.1.2 | 43 | 7/22/2026 |
## 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
-<PackageReference Include="DatadogNet.RUM.iOS" Version="3.14.0.3" />
+<PackageReference Include="DatadogNet.RUM.iOS" Version="3.14.0.4" />
```
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.