refactor(rokt): move Rokt API out of core into the Rokt/Payments kits - #49
Conversation
Remove the Rokt managed contract from the core mParticle.Maui.Sdk package: RoktApi, RoktEvent (+ subtypes), RoktConfig, RoktColorMode, RoktEmbeddedView, the MParticleSDK.Rokt accessor, the platform wrappers and the no-op. The Rokt surface now lives entirely in mParticle.Maui.Kits.Rokt and is exposed via a C# 14 extension property, so MParticle.Instance.Rokt only exists when the Rokt kit is referenced. SelectShoppableAds moves further out into mParticle.Maui.Kits.Rokt.Payments as an extension method on RoktApi, since it requires a natively registered payment extension. The core assembly still contains the low-level generated native Rokt bindings (iOSBinding/AndroidBinding); those are plumbing mirroring the Apple/Android native SDKs and cannot be removed on iOS. Samples updated to import mParticle.MAUI.Rokt (+ Payments) and to resolve RoktEmbeddedView from the kit assembly.
PR SummaryHigh Risk Overview Core also forwards CI workflows bump Xcode 26.0 → 26.2 and pin MAUI workload install to Reviewed by Cursor Bugbot for commit 857e8a9. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c55493d. Configure here.
The GitHub macOS runner no longer ships the iOS 26.0 platform, so the default `dotnet workload install maui` (Microsoft.iOS.Sdk 26.0.11017) fails the iOS binding build with "iOS 26.0 is not installed". Pin Xcode to 26.2 and the MAUI workload set to 10.0.103 (Microsoft.NET.Sdk.iOS 26.2.x), which are mutually compatible on the current runner image. Mirrors the fix already applied on the native-sdk-6 branch.
SelectShoppableAds ignored its receiver and reached for the global MParticle.SharedInstance.Rokt, so it never emitted the SDK-not-initialized warning that the sibling RoktApi methods do. Add an internal NativeHandle hook on RoktApi (exposed to the Payments kit via InternalsVisibleTo) so the Payments extension dispatches through the receiver and mirrors the init semantics on both platforms. Public API is unchanged. Also fix the base Rokt kit README, which still listed SelectShoppableAds even though it now ships only in the Payments kit.
The managed MParticleOptions.LogLevel was never mapped to the native builders, so both platforms stayed at their default log level and VERBOSE diagnostics (including batch upload logs) were never emitted. - Android: add ConvertToMpLogLevel and call builder.LogLevel(...) in ConvertToMpOptions. - iOS: expose logLevel on the MParticleOptions binding and assign mpOptions.LogLevel in ConvertToMpOptions.

Background
Today the Rokt managed contract lives in the core
mParticle.Maui.Sdkpackage: the
RoktApitype, allRoktEventsubtypes,RoktConfig,RoktEmbeddedView, and theMParticleSDK.Roktaccessor. It was added there in#4 ("Refactor to single SDK with common API interface") as a side effect of the
single‑SDK consolidation, not as a deliberate core API decision.
This is architecturally wrong: the core mParticle package should not expose any
Rokt surface. Rokt is an optional integration and should only appear when its
kit is referenced — the same way it works natively and in the Flutter/React
Native wrappers.
What Has Changed
mParticle.Maui.Sdk) — removed the entire Rokt managed contract:RoktApi,RoktEvent(+ ~20 subtypes),RoktConfig,RoktColorMode,RoktEmbeddedView,MParticleSDK.Rokt, both platformRoktApiWrappers, bothRoktEmbeddedViewHandlers, theNoOpRoktApiWrapper, and the Rokt converters inUtils.cs. Core no longer knows about Rokt.mParticle.Maui.Kits.Rokt) — now owns the Rokt contract. Theaccessor is a C# 14 extension property on
MParticleSDK, soMParticle.Instance.Roktonly compiles when this package is referenced. Theplatform
RoktApiimplementations reach the native Rokt through the core'spublic generated bindings (
iOSBinding/AndroidBinding) via the existingProjectReference.mParticle.Maui.Kits.Rokt.Payments) —SelectShoppableAdsmoves here as an extension method on
RoktApi, since it depends on anatively registered payment extension. It appears on
MParticle.Instance.Roktonly when the Payments package is referenced.
mParticle.MAUI.Rokt(and.Payments) and resolveRoktEmbeddedViewfrom the kit assembly (XAMLclr-namespaceupdated).MParticleOptions.LogLevelfix (both platforms) — the managedLogLevelwas never forwarded to the native builders, so the native SDKs stayed at their
default level and
VERBOSEdiagnostics (including batch‑upload logs) were neveremitted. Android now maps and calls
builder.LogLevel(...); iOS exposeslogLevelon the binding and assignsmpOptions.LogLevel. This was required toverify event/upload/Rokt behaviour during runtime testing below.
Resulting layering:
mParticle.Maui.Sdk(core)mParticle.Maui.Kits.RoktMParticle.Instance.Rokt+SelectPlacements/Events/GlobalEventsmParticle.Maui.Kits.Rokt.PaymentsRokt.SelectShoppableAds(...)Why this path (and why it isn't wasted work)
We deliberately base this on
main(native mParticle 5.x, where Rokt stilllives in
android-core) rather than on the native‑SDK‑6 branch. This is a pureMAUI‑layer API redesign and is independent of the native version: core keeps
binding native Rokt from
android-core, and the kit reaches it through itsProjectReferenceto core.The key point: had we gone straight to the native SDK 6 upgrade, we would have
been forced to do this exact relocation anyway. In native Android 6.0 Rokt was
removed from
android-coreand moved intoandroid-rokt-kit, so the core MAUIbinding can no longer expose Rokt at all (the old
com.mparticle.Roktimportstops resolving). On the native‑6 branch we worked around that with
provider‑hooks + a no‑op in core, but the managed
RoktApicontract was left incore purely for backwards compatibility — i.e. core advertised a Rokt API it
could no longer back natively.
So the correct end‑state is the same regardless of the native bump: the Rokt
managed API belongs in the kit. Doing it now, decoupled from the native
upgrade, means:
scaffolding in core — the kit already owns everything;
Testing
Build validation
Rokt SampleApp, plus NuGet pack of core.
maui-iosworkload) — relies on CI. The iOScode is structurally symmetric to Android and uses the same core
iOSBindingtypes already exercised by core/samples.
Package‑isolation check (Rokt vs Payments)
Verified the new layering actually enforces the split by temporarily referencing
only
mParticle.Maui.Kits.Roktfrom the Rokt SampleApp (Payments removed):RoktPaymentExtension.Register(...)andRoktApi.SelectShoppableAds(...)—confirming Shoppable Ads is exclusive to the Payments kit.
the app runs normally, confirming the base Rokt kit is self‑contained.
kit (its native side is iOS‑only), so the two references are equivalent on
Android.
Runtime validation (Android emulator, Production workspace,
LogLevel=VERBOSE)Log Basic Events→batches upload (HTTP
202), verified via VERBOSE logcat (the LogLevel fix aboveis what makes these logs visible).
mParticle.Maui.Kits.Roktonly): Initialize → Rokt kitregisters → Show Rokt Overlay launches the native
com.rokt.roktsdk.ui.overlay.RoktModalActivity, the experience response isparsed, and the placement renders (
Layout ready,Rokt overlay placement loaded,RoktPlacementInteractive). No “Rokt Kit is not available”.Screenshots/Video
Verified locally: the native Rokt overlay renders on Android with only the
base
mParticle.Maui.Kits.Roktkit referenced (Payments removed) — a Roktplacement (“Purchase complete… You've unlocked 60% off…”, “Powered by Rokt”)
displayed inside
RoktModalActivity.Checklist
Additional Notes
version bump at release time. Versions are intentionally left untouched on this
branch.