What's changed
Binding-only release. The native SDK is unchanged — still
dd-sdk-ios 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.0andnet10.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
-<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.
Full changelog: v3.14.0.4...v3.14.0.5
Packages
Bound against dd-sdk-ios 3.14.0, targeting net8.0-ios18.0, net9.0-ios18.0 and net10.0-ios26.0.
The first three components of
3.14.0.5are the dd-sdk-ios version; the fourth is this
repository's binding revision, which advances when the bindings or packaging change while
the native binaries stay put.
| Package | Wraps | NuGet |
|---|---|---|
DatadogNet.Core.iOS |
DatadogCore |
3.14.0.5 |
DatadogNet.RUM.iOS |
DatadogRUM |
3.14.0.5 |
DatadogNet.Logs.iOS |
DatadogLogs |
3.14.0.5 |
DatadogNet.Trace.iOS |
DatadogTrace |
3.14.0.5 |
DatadogNet.SessionReplay.iOS |
DatadogSessionReplay |
3.14.0.5 |
DatadogNet.WebViewTracking.iOS |
DatadogWebViewTracking |
3.14.0.5 |
DatadogNet.CrashReporting.iOS |
DatadogCrashReporting |
3.14.0.5 |
DatadogNet.Flags.iOS |
DatadogFlags |
3.14.0.5 |
DatadogNet.Profiling.iOS |
DatadogProfiling |
3.14.0.5 |
DatadogNet.Internal.iOS |
DatadogInternal |
3.14.0.5 |
DatadogNet.OpenTelemetryApi.iOS |
OpenTelemetryApi |
3.14.0.5 |
DatadogNet.Objc.iOS |
(compatibility meta-package) |
3.14.0.5 |
dotnet add package DatadogNet.Core.iOS --version 3.14.0.5
dotnet add package DatadogNet.RUM.iOS --version 3.14.0.5
Licensing
The binding code is MIT; the bundled native binaries are Apache-2.0, as built and
published by Datadog. Each package ships both texts under licenses/.