Conversation
Add GPP (Global Privacy Platform) support to the React Native SDK: - Wire getGPPData(), getGPPString(), setGPPConsent() bridge methods for Android and iOS - Add onGppSectionChange event listener via RCTDeviceEventEmitter - Define GppData and GppSectionChangePayload TypeScript models - Serialize GPP data structures on both native platforms - Add GPP testing screen to the sample app - Add unit tests for all GPP methods Made-with: Cursor
|
CodeAnt AI is reviewing your PR. Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
Review Summary by QodoAdd Global Privacy Platform (GPP) support to React Native SDK
WalkthroughsDescription• Add GPP (Global Privacy Platform) support with three bridge methods - getGPPData() returns GPP data structure with string, sections, applicable sections - getGPPString() returns GPP consent string - setGPPConsent() updates GPP consent for specific section and field • Implement onGppSectionChange event listener on both Android and iOS platforms - Android uses DeviceEventManagerModule.RCTDeviceEventEmitter for event emission - iOS extends RCTEventEmitter with startObserving() and stopObserving() lifecycle • Add TypeScript models GppData and GppSectionChangePayload for type safety • Create GPP testing screen in sample app with UI for all GPP operations • Add comprehensive unit tests for all new GPP methods and event handling Diagramflowchart LR
A["Native Layer<br/>Android & iOS"] -->|Bridge Methods| B["RNUsercentricsModule"]
B -->|getGPPData| C["GppData Model"]
B -->|getGPPString| D["GPP String"]
B -->|setGPPConsent| E["Update Consent"]
A -->|Event Emission| F["onGppSectionChange"]
F -->|NativeEventEmitter| G["JavaScript Layer"]
G -->|Callback| H["GppSectionChangePayload"]
I["Sample App"] -->|Testing UI| J["GppTestingScreen"]
File Changes1. android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt
|
Code Review by Qodo
1.
|
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughAdds GPP (Global Privacy Platform) support across platforms: new APIs to get GPP data/string, set GPP consent, and emit onGppSectionChange events. Changes touch Android and iOS native modules, TypeScript models and public API, tests/fakes, and the sample app with a GPP testing screen. Changes
Sequence Diagram(s)sequenceDiagram
participant JS as JavaScript/React
participant Bridge as RN Bridge / Native Module
participant NativeCore as Usercentrics Core
participant EventMgr as UsercentricsEvent
JS->>Bridge: getGPPData()
Bridge->>NativeCore: getGPPData()
NativeCore-->>Bridge: GppData
Bridge-->>JS: Promise resolved (serialized GppData)
JS->>Bridge: setGPPConsent(section, field, value)
Bridge->>NativeCore: setGPPConsent(...)
NativeCore-->>Bridge: ack/void
sequenceDiagram
participant JS as JavaScript/React
participant Bridge as RNUsercentricsModule
participant NativeEvent as UsercentricsEvent
participant JSCallback as JS listener
JS->>Bridge: addListener("onGppSectionChange")
Bridge->>NativeEvent: register onGppSectionChange callback
NativeEvent-->>Bridge: emits GppSectionChangePayload
Bridge-->>JSCallback: emit "onGppSectionChange" via RN emitter
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip You can disable sequence diagrams in the walkthrough.Disable the |
Sequence DiagramThis PR adds React Native support for retrieving GPP data/string and updating GPP consent, wiring these calls through the native bridges to the Usercentrics core and exposing GPP section change notifications back to the app. sequenceDiagram
participant App
participant ReactNativeSDK
participant UsercentricsCore
App->>ReactNativeSDK: Request GPP data or string
ReactNativeSDK->>UsercentricsCore: Get GPP data and string
UsercentricsCore-->>ReactNativeSDK: GPP data and string
ReactNativeSDK-->>App: Resolve promise with GPP data/string
App->>ReactNativeSDK: setGPPConsent for a section
ReactNativeSDK->>UsercentricsCore: Update GPP consent value
UsercentricsCore-->>ReactNativeSDK: Notify GPP section change
ReactNativeSDK-->>App: Invoke onGppSectionChange callback
Generated by CodeAnt AI |
|
PR Summary: Implements GPP (Global Privacy Platform) support: adds GPP data access, GPP consent setters, and a GPP section change event across native Android/iOS and JS. Changes:
Compatibility notes:
|
android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt
Show resolved
Hide resolved
|
CodeAnt AI finished reviewing your PR. |
android/src/main/java/com/usercentrics/reactnative/extensions/GppDataExtensions.kt
Show resolved
Hide resolved
android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt
Show resolved
Hide resolved
|
Reviewed up to commit:a7bea02ac299987074920df6e7a918f60597186f |
android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt
Show resolved
Hide resolved
android/src/main/java/com/usercentrics/reactnative/extensions/GppDataExtensions.kt
Outdated
Show resolved
Hide resolved
android/src/main/java/com/usercentrics/reactnative/extensions/UsercentricsCMPDataExtensions.kt
Show resolved
Hide resolved
CI Feedback 🧐A test triggered by this PR failed. Here is an AI-generated analysis of the failure:
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
sample/src/screens/GppTestingScreen.tsx (1)
20-37: Consider adding error handling to async operations.The async handlers call SDK methods without try-catch blocks. If the SDK isn't properly initialized or an error occurs, the app could display unclear errors to testers.
💡 Proposed fix to add error handling
const fetchGppString = async () => { + try { const value = await Usercentrics.getGPPString(); setGppString(value); + } catch (error) { + setGppString(`Error: ${error}`); + } }; const fetchGppData = async () => { + try { const value = await Usercentrics.getGPPData(); const asJson = JSON.stringify(value, null, 2); setGppDataJson(asJson); + } catch (error) { + setGppDataJson(`Error: ${error}`); + } }; const setUsNatSaleOptOut = async () => { + try { await Usercentrics.setGPPConsent('usnat', 'SaleOptOut', 2); + } catch (error) { + console.error('Failed to set usnat consent:', error); + } }; const setUsFlSaleOptOut = async () => { + try { await Usercentrics.setGPPConsent('usfl', 'SaleOptOut', 2); + } catch (error) { + console.error('Failed to set usfl consent:', error); + } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sample/src/screens/GppTestingScreen.tsx` around lines 20 - 37, The async handlers fetchGppString, fetchGppData, setUsNatSaleOptOut, and setUsFlSaleOptOut lack error handling; wrap each function body in a try-catch, await the SDK call inside try, handle errors in catch by logging the error (e.g., console.error or process logger) and updating UI state (e.g., set an error string or toast) so failures are visible to testers; ensure you preserve existing success behavior (setGppString/setGppDataJson) and do not swallow errors silently.android/src/main/java/com/usercentrics/reactnative/extensions/GppDataExtensions.kt (1)
12-20: Add missing List and Map type handling to GppDataExtensions.The
whenblock only handles primitives (Boolean, Int, Double, String, null). SinceGppData.sectionscontainsMap<String, Any>, fields can hold arrays or nested objects. These will fall back totoString(), corrupting complex data. The codebase already hasList<*>.serialize()andMap<String, Any?>.toWritableMap()extensions that properly handle these types.♻️ Add support for List and Map types
fields.forEach { (fieldName, value) -> when (value) { null -> fieldsMap.putNull(fieldName) is Boolean -> fieldsMap.putBoolean(fieldName, value) is Int -> fieldsMap.putInt(fieldName, value) is Double -> fieldsMap.putDouble(fieldName, value) is String -> fieldsMap.putString(fieldName, value) + is List<*> -> fieldsMap.putArray(fieldName, value.serialize()) + is Map<*, *> -> { + `@Suppress`("UNCHECKED_CAST") + fieldsMap.putMap(fieldName, (value as Map<String, Any>).toWritableMap()) + } else -> fieldsMap.putString(fieldName, value.toString()) } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@android/src/main/java/com/usercentrics/reactnative/extensions/GppDataExtensions.kt` around lines 12 - 20, The when-block in GppDataExtensions.kt only handles primitives and falls back to toString() for complex types; update the fields.forEach handling to detect List and Map values and serialize them using the existing List<*>.serialize() and Map<String, Any?>.toWritableMap() extensions (e.g., add an is List<*> branch to call fieldsMap.putArray(fieldName, value.serialize()) and an is Map<*,*> branch that safely casts to Map<String, Any?> and calls fieldsMap.putMap(fieldName, value.toWritableMap())), ensuring nullability is respected and avoiding the toString() fallback for nested structures.src/models/GppData.tsx (1)
1-15: Consider using an interface instead of a class, though verify alignment with architectural patterns.The native bridge returns plain JavaScript objects, not class instances. Using
export class GppDatais semantically less precise than an interface sinceUsercentrics.getGPPData()returns raw bridge objects without calling this constructor. However, all 45+ other models in src/models/ consistently use theexport classpattern. Changing only GppData to an interface would break this established architectural consistency.This is not blocking and carries minimal risk (no
instanceofchecks are used with this class in the codebase), but the pattern choice should align with a broader decision about how models in this project are standardized.💡 Suggested alternative using interface (if adopting interface pattern project-wide)
-export class GppData { - gppString: string - applicableSections: number[] - sections: Record<string, Record<string, unknown>> - - constructor( - gppString: string, - applicableSections: number[], - sections: Record<string, Record<string, unknown>> - ) { - this.gppString = gppString - this.applicableSections = applicableSections - this.sections = sections - } -} +export interface GppData { + gppString: string + applicableSections: number[] + sections: Record<string, Record<string, unknown>> +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/models/GppData.tsx` around lines 1 - 15, The GppData type is declared as a class but native bridge returns plain objects (Usercentrics.getGPPData()), so either convert GppData to an interface or keep the class for consistency across models; to fix, choose one approach and apply it consistently: if you opt to use interface, change export class GppData to export interface GppData { gppString: string; applicableSections: number[]; sections: Record<string, Record<string, unknown>> } and update any other model files to interfaces, or if you keep the class (recommended for alignment with the 45+ existing models) leave export class GppData as-is but add a short comment near the GppData declaration noting that bridge returns plain objects and instances are not required (reference symbols: GppData and Usercentrics.getGPPData).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt`:
- Around line 141-143: The current setGPPConsent implementation treats both a
missing "value" key and an explicit null value as a no-op because
readableMapValueToAny() returning null causes an early return; change
setGPPConsent to distinguish "no key" from "key present with null" by checking
the ReadableMap for the "value" key instead of simply returning on parsedValue
== null, and when the key is present but null forward an explicit null
placeholder to usercentricsProxy.instance.setGPPConsent (so
readableMapValueToAny, setGPPConsent, and the call to
usercentricsProxy.instance.setGPPConsent are adjusted to propagate an explicit
null value rather than swallowing it).
In `@ios/RNUsercentricsModule.swift`:
- Around line 38-52: The subscription is created inside queue.async which can
race with immediate JS calls like setGPPConsent and miss the first event; change
startObserving to register gppSectionChangeSubscription synchronously (remove
the outer queue.async and create the subscription immediately in startObserving)
while keeping event dispatching onto queue if needed (i.e., inside the
onGppSectionChange handler use queue.async { self.sendEvent(...) }). Keep
stopObserving disposing gppSectionChangeSubscription on the queue (queue.async {
... }) and reference the existing gppSectionChangeSubscription,
usercentricsManager.onGppSectionChange, startObserving, stopObserving,
setGPPConsent, and onGppSectionChangeEvent symbols when making the change.
In `@ios/RNUsercentricsModuleSpec.h`:
- Around line 63-65: The spec declares setGPPConsent with parameter type `id`
for `value` but the implementations (RNUsercentricsModule.mm and
RNUsercentricsModule.swift) expect an NSDictionary; update the
RNUsercentricsModuleSpec declaration for the method `-
(void)setGPPConsent:(NSString *)sectionName fieldName:(NSString *)fieldName
value:(id)value;` to use `NSDictionary *value` instead of `id` so the
Objective-C bridge matches the .mm and Swift implementations.
---
Nitpick comments:
In
`@android/src/main/java/com/usercentrics/reactnative/extensions/GppDataExtensions.kt`:
- Around line 12-20: The when-block in GppDataExtensions.kt only handles
primitives and falls back to toString() for complex types; update the
fields.forEach handling to detect List and Map values and serialize them using
the existing List<*>.serialize() and Map<String, Any?>.toWritableMap()
extensions (e.g., add an is List<*> branch to call fieldsMap.putArray(fieldName,
value.serialize()) and an is Map<*,*> branch that safely casts to Map<String,
Any?> and calls fieldsMap.putMap(fieldName, value.toWritableMap())), ensuring
nullability is respected and avoiding the toString() fallback for nested
structures.
In `@sample/src/screens/GppTestingScreen.tsx`:
- Around line 20-37: The async handlers fetchGppString, fetchGppData,
setUsNatSaleOptOut, and setUsFlSaleOptOut lack error handling; wrap each
function body in a try-catch, await the SDK call inside try, handle errors in
catch by logging the error (e.g., console.error or process logger) and updating
UI state (e.g., set an error string or toast) so failures are visible to
testers; ensure you preserve existing success behavior
(setGppString/setGppDataJson) and do not swallow errors silently.
In `@src/models/GppData.tsx`:
- Around line 1-15: The GppData type is declared as a class but native bridge
returns plain objects (Usercentrics.getGPPData()), so either convert GppData to
an interface or keep the class for consistency across models; to fix, choose one
approach and apply it consistently: if you opt to use interface, change export
class GppData to export interface GppData { gppString: string;
applicableSections: number[]; sections: Record<string, Record<string, unknown>>
} and update any other model files to interfaces, or if you keep the class
(recommended for alignment with the 45+ existing models) leave export class
GppData as-is but add a short comment near the GppData declaration noting that
bridge returns plain objects and instances are not required (reference symbols:
GppData and Usercentrics.getGPPData).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1d60dae1-efab-4668-99bb-2df74c7d6f6c
📒 Files selected for processing (24)
android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.ktandroid/src/main/java/com/usercentrics/reactnative/RNUsercentricsModuleSpec.ktandroid/src/main/java/com/usercentrics/reactnative/extensions/GppDataExtensions.ktandroid/src/main/java/com/usercentrics/reactnative/extensions/UsercentricsCMPDataExtensions.ktexample/ios/exampleTests/Fake/FakeUsercentricsManager.swiftios/Extensions/GppData+Dict.swiftios/Extensions/GppSectionChangePayload+Dict.swiftios/Manager/UsercentricsManager.swiftios/RNUsercentricsModule.mmios/RNUsercentricsModule.swiftios/RNUsercentricsModuleSpec.hsample/ios/sampleTests/Fake/FakeUsercentricsManager.swiftsample/src/App.tsxsample/src/screens/GppTestingScreen.tsxsample/src/screens/Home.tsxsample/src/screens/index.tsxsrc/NativeUsercentrics.tssrc/Usercentrics.tsxsrc/__tests__/index.test.tssrc/__tests__/mocks.tssrc/fabric/NativeUsercentricsModule.tssrc/models/GppData.tsxsrc/models/GppSectionChangePayload.tsxsrc/models/index.tsx
💤 Files with no reviewable changes (1)
- android/src/main/java/com/usercentrics/reactnative/extensions/UsercentricsCMPDataExtensions.kt
android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt
Show resolved
Hide resolved
|
CodeAnt AI is running Incremental review Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
Sequence DiagramThis PR adds Global Privacy Platform support so React Native apps can read GPP string/data, update GPP consent, and receive GPP section change events from the native Usercentrics core. sequenceDiagram
participant App
participant SDK
participant UsercentricsCore
App->>SDK: Request GPP data or GPP string
SDK->>UsercentricsCore: Get GPP data or string
UsercentricsCore-->>SDK: Return GPP information
SDK-->>App: Resolve promise with GPP information
App->>SDK: setGPPConsent(section, field, value)
SDK->>UsercentricsCore: Apply GPP consent update
UsercentricsCore-->>SDK: GPP section change event
SDK-->>App: Trigger onGppSectionChange callback with payload
Generated by CodeAnt AI |
|
CodeAnt AI Incremental review completed. |
There was a problem hiding this comment.
♻️ Duplicate comments (1)
ios/RNUsercentricsModule.swift (1)
38-52:⚠️ Potential issue | 🟠 MajorRegister the GPP observer synchronously in
startObserving.
queue.asyncstill leaves a window where JS can subscribe and immediately callsetGPPConsentbeforegppSectionChangeSubscriptionexists, so the firstonGppSectionChangeevent can be missed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ios/RNUsercentricsModule.swift` around lines 38 - 52, The GPP observer is registered asynchronously which can miss the first event; change startObserving to register the subscription synchronously on queue (replace queue.async with queue.sync) so gppSectionChangeSubscription is set before JS can call setGPPConsent; keep the same weak self guard and disposal logic (stopObserving can remain using queue.async) and reference the startObserving/stopObserving methods, gppSectionChangeSubscription property, and usercentricsManager.onGppSectionChange callback when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@ios/RNUsercentricsModule.swift`:
- Around line 38-52: The GPP observer is registered asynchronously which can
miss the first event; change startObserving to register the subscription
synchronously on queue (replace queue.async with queue.sync) so
gppSectionChangeSubscription is set before JS can call setGPPConsent; keep the
same weak self guard and disposal logic (stopObserving can remain using
queue.async) and reference the startObserving/stopObserving methods,
gppSectionChangeSubscription property, and
usercentricsManager.onGppSectionChange callback when making the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6eb66210-10d3-4980-9a93-defbabbf8d1c
📒 Files selected for processing (2)
ios/Extensions/UsercentricsCMPData+Dict.swiftios/RNUsercentricsModule.swift
💤 Files with no reviewable changes (1)
- ios/Extensions/UsercentricsCMPData+Dict.swift
islameldesoky95
left a comment
There was a problem hiding this comment.
I would suggest a run on the iOS as well just to make sure things working for the new arch and fabric and that they are compatible
Made-with: Cursor
|
CodeAnt AI is running Incremental review Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
|
CodeAnt AI Incremental review completed. |
User description
Add GPP (Global Privacy Platform) support to the React Native SDK:
Android:
iOS:
Summary by CodeRabbit
CodeAnt-AI Description
Add Global Privacy Platform (GPP) APIs and real-time events to the React Native SDK
What Changed
Impact
✅ Can read GPP string and structured sections from JS✅ Can set section-level GPP consent from JS✅ Receives live onGppSectionChange events in the app💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.