DatadogNet.Internal.iOS
3.14.0.5
dotnet add package DatadogNet.Internal.iOS --version 3.14.0.5
NuGet\Install-Package DatadogNet.Internal.iOS -Version 3.14.0.5
<PackageReference Include="DatadogNet.Internal.iOS" Version="3.14.0.5" />
<PackageVersion Include="DatadogNet.Internal.iOS" Version="3.14.0.5" />
<PackageReference Include="DatadogNet.Internal.iOS" />
paket add DatadogNet.Internal.iOS --version 3.14.0.5
#r "nuget: DatadogNet.Internal.iOS, 3.14.0.5"
#:package DatadogNet.Internal.iOS@3.14.0.5
#addin nuget:?package=DatadogNet.Internal.iOS&version=3.14.0.5
#tool nuget:?package=DatadogNet.Internal.iOS&version=3.14.0.5
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.5" />
<PackageReference Include="DatadogNet.RUM.iOS" Version="3.14.0.5" />
</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.5" />
<PackageReference Include="DatadogNet.RUM.iOS" Version="3.14.0.5" />
</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.5 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.
Undefined symbols for architecture arm64: "_OBJC_CLASS_$_DD…" building for a real device.
Fixed in 3.14.0.5 — update every DatadogNet package to that version or later. The prebuilt
dd-sdk-ios device slices ship without static Objective-C registration for 41 classes (their
deployment target is below iOS 13), which broke every device link while simulator builds worked;
the packages now repair it with generated linker aliases plus up-front class realization. See
docs/release-notes/3.14.0.5.md and build/device-class-aliases/README.md.
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
- No dependencies.
-
net8.0-ios18.0
- No dependencies.
-
net9.0-ios18.0
- No dependencies.
NuGet packages (9)
Showing the top 5 NuGet packages that depend on DatadogNet.Internal.iOS:
| Package | Downloads |
|---|---|
|
DatadogNet.Logs.iOS
.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 3.14.0. The managed DDLogs/DDLogger API ships in this package; DatadogNet.Objc.iOS remains as a dependency-only compatibility meta-package. |
|
|
DatadogNet.CrashReporting.iOS
.NET for iOS / .NET MAUI bindings for the native Datadog iOS SDK's DatadogCrashReporting framework: captures native crashes and reports them as RUM errors on the next launch. From dd-sdk-ios 3.0 the crash engine is KSCrash, linked into this framework, so there is no separate CrashReporter package any more. Built against dd-sdk-ios 3.14.0. Enable it after initializing the SDK; upload your app's dSYMs to Datadog for symbolication. |
|
|
DatadogNet.Core.iOS
.NET for iOS / .NET MAUI bindings for the native Datadog iOS SDK's DatadogCore framework: SDK initialization, tracking consent, user and account info, and the app-launch and exception handlers every feature module reports through. Built against dd-sdk-ios 3.14.0. Enable a feature by also referencing DatadogNet.RUM.iOS, DatadogNet.Logs.iOS or DatadogNet.Trace.iOS - or reference DatadogNet.Objc.iOS for the whole set. |
|
|
DatadogNet.SessionReplay.iOS
.NET for iOS / .NET MAUI bindings for the native Datadog iOS SDK's DatadogSessionReplay framework: records and replays user sessions, with privacy levels that mask text and user input. Built against dd-sdk-ios 3.14.0. Requires RUM to be enabled. |
|
|
DatadogNet.Trace.iOS
.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 3.14.0. The managed DDTrace/DDTracer API ships in this package; DatadogNet.Objc.iOS remains as a dependency-only compatibility meta-package. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.14.0.5 | 73 | 7/28/2026 |
| 3.14.0.5-beta.11.23 | 32 | 7/30/2026 |
| 3.14.0.5-beta.10.21 | 26 | 7/30/2026 |
| 3.14.0.5-beta.9.20 | 35 | 7/29/2026 |
| 3.14.0.5-beta.8.19 | 30 | 7/28/2026 |
| 3.14.0.4 | 89 | 7/26/2026 |
| 3.14.0.4-beta.7.16 | 43 | 7/26/2026 |
| 3.14.0.3 | 130 | 7/24/2026 |
| 3.14.0.3-beta.6.15 | 46 | 7/24/2026 |
| 3.14.0.3-beta.6.14 | 47 | 7/24/2026 |
| 3.14.0.2 | 107 | 7/23/2026 |
| 3.14.0.2-beta.5.12 | 46 | 7/23/2026 |
| 3.14.0.2-beta.5.11 | 48 | 7/23/2026 |
| 3.14.0.2-beta.5.10 | 50 | 7/23/2026 |
| 3.14.0.1 | 91 | 7/23/2026 |
| 3.14.0.1-beta.3.8 | 50 | 7/23/2026 |
| 2.30.2.2 | 102 | 7/23/2026 |
| 2.30.2.1 | 99 | 7/22/2026 |
| 2.30.2.1-beta.2.4 | 45 | 7/22/2026 |
| 2.17.0.1-beta.1.2 | 46 | 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 one reason: **apps using these
packages can now build for a real iPhone.** Every earlier release fails its device link.
## Real-device builds no longer fail to link
Building any consuming app for `ios-arm64` — a physical device, any target framework — failed at
the native link with a wall of:
```
error : Undefined symbols for architecture arm64:
"_OBJC_CLASS_$_DDConfiguration", referenced from: in registrar.o
"_OBJC_CLASS_$_DDRUMConfiguration", referenced from: in registrar.o
...
```
while the identical build for the simulator linked and ran fine.
The defect is upstream, in the prebuilt archives this repository binds. dd-sdk-ios builds its
**device** slices with a 12.0 deployment target and its **simulator** slices with 14.0 (the
arm64-simulator floor) — and below iOS 13, the Swift compiler withholds static Objective-C
registration for `@objc` classes whose metadata needs runtime fix-ups: no `_OBJC_CLASS_$_<Name>`
symbol, no `__objc_classlist` entry. **41 classes** that every simulator slice exports are simply
not there to link against in the device slices: `DDConfiguration`, `DDSite`, `DDTrackingConsent`
and the URLSession instrumentation types in Core; `DDLogEvent`; `DDRUMConfiguration` and all the
RUM event-model classes; `DDTraceConfiguration`; `DDSessionReplayConfiguration`. The .NET static
registrar references bound classes by exactly those names, so any app that reaches one — and
`DDConfiguration` is the front door — cannot link.
Every prebuilt upstream archive checked has the same asymmetry (2.30.2, 3.13.0, 3.14.0, and the
arm64e twin zips), so this is not a 3.14.0 regression and no earlier package release escapes it:
**the 2.x line and 3.14.0.1–3.14.0.4 all fail device links.** It went unnoticed because nothing
on either side ever linked a device build: this repository's CI was simulator-only (no longer —
see below), and upstream's consumers overwhelmingly build from source via SwiftPM/CocoaPods with
their app's own, higher deployment target. The defect is reported upstream with the full
evidence; a corrected upstream build makes this entire mechanism disappear.
## The fix: generated linker aliases, plus up-front realization
The missing classes are not gone from the device binaries. Each class object is present and
exported under its Swift metadata symbol (`_$s…CN`) — the simulator slice exports the very same
object under both that name and the Objective-C one, at the same address. So, per affected class,
the packages now carry one linker flag:
```
-Wl,-alias,_$s11DatadogCore18objc_ConfigurationCN,_OBJC_CLASS_$_DDConfiguration
```
`build/GenerateDeviceClassAliases.sh` derives the table mechanically from the binaries (join the
two names on their shared address in the simulator slice; require the Swift symbol to be exported
by the device slice) and writes one committed file per framework under
`build/device-class-aliases/`. `Datadog.Binding.props` folds each file into the framework's
`NativeReference` as `LinkerFlags`, which travel inside the binding assembly — consuming apps get
the repair automatically, with no project changes.
Linking is half the story. On device those class objects start out *unrealized*, and messaging
unrealized Swift class metadata is not a slow path but a segfault — measured on hardware: a cold
`[DDConfiguration class]` crashes, while the same address realized through the class's Swift
metadata accessor (`_$s…CMa`) becomes a fully working, name-resolvable class. And the first such
message does not wait for app code: the static registrar's `xamarin_create_classes`, called from
`main()` before a single line of managed code, does `[DDConfiguration class]` for every mapped
bound class — so no managed hook (a module initializer included; tried, crashed identically) can
run early enough. The realization therefore ships as native code: each affected framework gets a
generated `<Framework>Realize.xcframework`, a static library whose one dyld initializer calls
the metadata accessor of every aliased class. dyld runs it after the Datadog dylibs load and
before `main` — exactly the window. On the simulator, where the classes are statically
registered anyway, the accessors return immediately — the path is identical on both targets and
exercised by the simulator test suites. Verified end-to-end on a physical iPhone 15 Pro Max:
install, launch, SDK initialization, and runtime name-resolution of the previously missing
classes.
## Guard rails, so this cannot ship broken again
- **A Mach-O symbol audit joins the package tests** (`SymbolAuditTests`): every class the binding
assemblies register must resolve for a device link exactly as it does for a simulator link —
exported by the device slice or covered by a shipped alias; every shipped alias must point at a
symbol the device slice actually exports and must not shadow a class the device slice has
learned to export itself; and a package that ships aliases must ship the realization library
calling exactly those classes' metadata accessors. Symbol-level checking of the native payload
existed nowhere before; the audit is what would have caught this defect at pack time.
- **CI now links for a real device on both bands.** The release link check became a matrix —
`net9.0-ios18.0` and `net10.0-ios26.0` — and the example app gained a net10 band
(`-p:DatadogSdkBand=net10`) so the newest target framework is the one exercised most.
- **`build/BumpNativeVersion.sh`'s checklist regenerates the aliases** right after fetching new
frameworks: the Swift mangled names change with every native release, and the audit fails on
stale ones.
## Upgrading from 3.14.0.4
```diff
-<PackageReference Include="DatadogNet.RUM.iOS" Version="3.14.0.4" />
+<PackageReference Include="DatadogNet.RUM.iOS" Version="3.14.0.5" />
```
Nothing to change beyond the version: same native SDK, same API, same package set, and all
packages move together as they depend on each other at an exact version. If your app only ever
ran on the simulator, nothing visible changes; if you have been unable to build for a device,
this release is the fix.