Skip to content

Add SetCustomerUserId/ClearCustomerUserId for the post-Configure() case - #8

Merged
jsantelys-as merged 2 commits into
mainfrom
feat/jsantelys/set-customer-user-id
Aug 12, 2026
Merged

Add SetCustomerUserId/ClearCustomerUserId for the post-Configure() case#8
jsantelys-as merged 2 commits into
mainfrom
feat/jsantelys/set-customer-user-id

Conversation

@jsantelys-as

@jsantelys-as jsantelys-as commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Bridges the native setCustomerUserId setter (iOS AppstackAttributionSdk.shared.setCustomerUserId(_:), Android AppstackAttributionSdk.setCustomerUserId) so the customer user ID can be set or cleared after Configure(). That is the common case — a login usually reveals the ID — and a second Configure() is not an alternative: it is a no-op and ignores its customerUserId.

AppstackSDK.SetCustomerUserId("user-123"); // on login
AppstackSDK.ClearCustomerUserId();         // on logout

The clear-path decision

C# cannot marshal a null string cleanly, and "" already means "not provided" on the Configure path (customerUserId?.Trim() ?? ""emptyToNull), so a Unity clear needed an explicit decision.

"" is the clear marker, but only on the new setCustomerUserId native entry points. It is unambiguous there because those are separate native functions from configure, so no sentinel gymnastics and no null-string marshalling: C# normalizes null/blank to "", and the existing emptyToNull (Java) and string(from:) (Swift) helpers already map it to null/nil, which the native setters treat as the clear.

ClearCustomerUserId() is a Unity-only spelling of SetCustomerUserId(null) — added because C# logout call sites read better with it. It adds no behavior the other wrappers lack, and DEVELOPMENT.md records it as an explicit cross-wrapper contract decision (that file's rule asks for one). Easy to drop if you would rather keep the surface identical across wrappers.

Changes

AppstackSDK.cs, IAppstackNativeBridge.cs, AppstackSDKNative.cs (all three nested bridges, including the unsupported-platform no-op), AppstackAndroidBridge.cs, AppstackIOSBridge.cs, and both native bridges. Tests: the API snapshot, the bridge seam, the unsupported-platform silence check, plus the Tests~/Native contract fixtures on both platforms and the iOS runner's expected-C-symbol list. README/USAGE/CHANGELOG/DEVELOPMENT updated.

Native requirements

Requires iOS SDK 4.5.0 and Android SDK 1.7.0. Both are pinned on main (#9), and the iOS contract fixture resolves and checksum-verifies the pinned release artifact (#10). main is merged into this branch.

Verification

  • 112 editor tests, 36 Android contract tests, and 12 iOS Swift contract tests pass.
  • real-artifact compiles the production Java bridge, including the new setCustomerUserId call, against tech.appstack.android-sdk:appstack-android-sdk:1.7.0 resolved from Maven Central.
  • AppstackUnityBridge.swift compiles against the checksum-verified 4.5.0 release artifact, and all eight expected C symbols are present, including the new AppstackUnitySetCustomerUserId.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 48d296b9-1b34-48d5-888c-dc7ee043b5cc

📥 Commits

Reviewing files that changed from the base of the PR and between 9497906 and 2f1cf60.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • DEVELOPMENT.md
  • Tests~/Native/iOS/run-tests.sh
  • USAGE.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • USAGE.md
  • Tests~/Native/iOS/run-tests.sh
  • CHANGELOG.md
  • DEVELOPMENT.md

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added APIs to set, update, and clear the customer user ID after SDK configuration.
    • Added support for propagating customer IDs across Android and iOS integrations.
    • Blank or null values clear the stored customer ID, with the latest update taking effect.
  • Documentation

    • Added guidance for configuration, login/logout flows, clearing behavior, timing, and event association.
  • Tests

    • Added coverage for forwarding, clearing, ordering, error handling, and unsupported platforms.

Walkthrough

The Unity SDK adds public methods to set and clear customer user IDs after configuration. Android and iOS bridges forward values and clear markers to native SDKs. Editor, Android, and iOS tests validate normalization, ordering, errors, and platform behavior.

Changes

Customer user ID lifecycle

Layer / File(s) Summary
Public API and contract
Runtime/AppstackSDK.cs, Runtime/AppstackSDKNative.cs, Runtime/IAppstackNativeBridge.cs, README.md, DEVELOPMENT.md, USAGE.md, CHANGELOG.md
AppstackSDK adds SetCustomerUserId and ClearCustomerUserId. The setter trims values, maps null or blank inputs to the empty clear marker, and forwards native errors. Documentation defines configuration, clearing, ordering, and event behavior.
Android and iOS bridge forwarding
Runtime/AppstackAndroidBridge.cs, Runtime/AppstackIOSBridge.cs, Runtime/Plugins/Android/..., Runtime/Plugins/iOS/...
The platform bridges forward customer user IDs. Android maps blank values to null. iOS converts empty C strings to nil. Unsupported platforms ignore updates.
API and native bridge validation
Tests/Editor/*, Tests~/Native/Android/*, Tests~/Native/iOS/*
Tests verify forwarding, clearing, call ordering, pre-configuration use, error propagation, unsupported-platform behavior, native call recording, and the exported iOS symbol.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Unity as AppstackSDK
  participant Native as AppstackSDKNative
  participant Bridge as Android/iOS bridge
  participant SDK as Native attribution SDK
  Unity->>Native: SetCustomerUserId(trimmed ID or empty marker)
  Native->>Bridge: Forward customer user ID
  Bridge->>SDK: Set value or clear with null/nil
Loading

Possibly related PRs

Suggested labels: enhancement

Poem

I’m a rabbit with an ID to send,
Set it, clear it, update it again.
Android hops, iOS follows through,
Blank means nil when bridges do.
Tests record each gentle hop.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: adding customer user ID setters for use after Configure().
Description check ✅ Passed The description directly explains the new APIs, native bridges, clear behavior, tests, documentation, and verification.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/jsantelys/set-customer-user-id
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/jsantelys/set-customer-user-id

Comment @coderabbitai help to get the list of available commands.

@jsantelys-as
jsantelys-as marked this pull request as ready for review August 6, 2026 12:11
Bridges the native iOS/Android setCustomerUserId setter through the C# facade,
the bridge seam, and both native bridges, so the id can be set or cleared after
Configure() — a repeat Configure() is a no-op that ignores its customerUserId.

Clear semantics: C# cannot marshal a null string cleanly, so the empty string
is the clear marker on the setCustomerUserId entry points, where it is
unambiguous (on Configure it means "not provided" instead, and never clears).
Both native bridges map empty to null/nil, which the native setters treat as
the clear. ClearCustomerUserId() is a Unity-only spelling of
SetCustomerUserId(null) for readable logout call sites; it adds no behavior the
other wrappers lack, and the contract note in DEVELOPMENT.md records the
decision.
@jsantelys-as
jsantelys-as force-pushed the feat/jsantelys/set-customer-user-id branch from a81e22a to 9497906 Compare August 6, 2026 12:11
@coderabbitai coderabbitai Bot added the enhancement New feature or request label Aug 6, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Runtime/AppstackSDK.cs`:
- Around line 103-110: Update the class-level AppstackSDK summary to state that
ClearCustomerUserId is a Unity-specific convenience wrapper, while preserving
the existing Flutter and React Native API parity description where applicable.
- Around line 79-85: Align the Configure lifecycle documentation with the
supported pre-configuration behavior: update Configure’s documentation to
explicitly exempt SetCustomerUserId and ClearCustomerUserId, while preserving
their safe-at-any-time semantics and the existing README.md and USAGE.md
guidance.

In `@Runtime/Plugins/Android/com/appstack/unity/AppstackUnityBridge.java`:
- Around line 57-60: Keep the iOS bridge blocked until the production SDK
exposes the customer-user-ID setter: in
Runtime/Plugins/iOS/AppstackUnityBridge.swift:40-47, update the dependency to a
compatible SDK or defer AppstackUnitySetCustomerUserId; no direct change is
required in
Runtime/Plugins/Android/com/appstack/unity/AppstackUnityBridge.java:57-60
because the resolved Android AAR already provides
AppstackAttributionSdk.setCustomerUserId(String).

In `@Tests`~/Native/iOS/run-tests.sh:
- Line 54: Update the iOS test archive reference and production AppstackSDK
package version together to a revision that exposes
AppstackAttributionSdk.setCustomerUserId, ensuring the
AppstackUnitySetCustomerUserId bridge compiles before the symbol check runs.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 796a390f-b1ba-4e0b-b98a-c974b3c6ae1f

📥 Commits

Reviewing files that changed from the base of the PR and between 2007fb0 and 9497906.

📒 Files selected for processing (19)
  • CHANGELOG.md
  • DEVELOPMENT.md
  • README.md
  • Runtime/AppstackAndroidBridge.cs
  • Runtime/AppstackIOSBridge.cs
  • Runtime/AppstackSDK.cs
  • Runtime/AppstackSDKNative.cs
  • Runtime/IAppstackNativeBridge.cs
  • Runtime/Plugins/Android/com/appstack/unity/AppstackUnityBridge.java
  • Runtime/Plugins/iOS/AppstackUnityBridge.swift
  • Tests/Editor/AppstackNativeBridgeSeamTests.cs
  • Tests/Editor/AppstackPublicApiSnapshotTests.cs
  • Tests/Editor/AppstackUnsupportedPlatformTests.cs
  • Tests~/Native/Android/contract-tests/src/main/kotlin/com/appstack/attribution/AppstackAttributionSdk.kt
  • Tests~/Native/Android/contract-tests/src/test/kotlin/com/appstack/unity/AppstackUnityBridgeContractTest.kt
  • Tests~/Native/iOS/Stubs/AppstackSDK/AppstackSDK.swift
  • Tests~/Native/iOS/Tests/AppstackUnityBridgeTests/AppstackUnityBridgeTests.swift
  • Tests~/Native/iOS/run-tests.sh
  • USAGE.md
📜 Review details
🧰 Additional context used
🪛 LanguageTool
README.md

[locale-violation] ~117-~117: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ... startup. More often a login reveals it afterwards, so set it whenever it becomes known: ...

(AFTERWARDS_US)

🔇 Additional comments (16)
Tests/Editor/AppstackNativeBridgeSeamTests.cs (1)

119-179: LGTM!

Also applies to: 354-355, 382-387

Tests/Editor/AppstackPublicApiSnapshotTests.cs (1)

15-16: LGTM!

Tests/Editor/AppstackUnsupportedPlatformTests.cs (1)

20-21: LGTM!

Tests~/Native/Android/contract-tests/src/main/kotlin/com/appstack/attribution/AppstackAttributionSdk.kt (1)

62-65: LGTM!

Also applies to: 95-95, 129-133

Tests~/Native/Android/contract-tests/src/test/kotlin/com/appstack/unity/AppstackUnityBridgeContractTest.kt (1)

75-103: LGTM!

Tests~/Native/iOS/Stubs/AppstackSDK/AppstackSDK.swift (1)

47-48: LGTM!

Also applies to: 60-60, 89-92

Tests~/Native/iOS/Tests/AppstackUnityBridgeTests/AppstackUnityBridgeTests.swift (1)

80-116: LGTM!

Also applies to: 258-263

Runtime/AppstackSDK.cs (2)

16-18: LGTM!


86-94: 🗄️ Data Integrity & Integration

Verify the any-thread and last-write-wins contract before shipping.

The Unity layer forwards each call without synchronization. The public documentation promises arbitrary-thread calls and deterministic ordering.

  • Runtime/AppstackSDK.cs#L86-L94: serialize setter, clear, and event operations, or prove native ordering with concurrent tests on both platforms.
  • USAGE.md#L56-L59: retain the any-thread and last-write-wins wording only after those tests pass.
Runtime/AppstackSDKNative.cs (1)

21-24: LGTM!

Also applies to: 115-119, 161-165, 203-207

Runtime/IAppstackNativeBridge.cs (1)

15-17: LGTM!

DEVELOPMENT.md (1)

29-30: LGTM!

Also applies to: 50-56

README.md (1)

129-132: 🔒 Security & Privacy

Verify the buffered-event identity claim.

The Unity setter changes native SDK state. It cannot directly rewrite events already buffered by the native SDK. Verify whether the native SDK snapshots customerUserId when it queues an event or resolves it at flush. If it snapshots the value, this statement is false and logout can misattribute queued events. Add Android and iOS contract tests for events queued before setting or clearing the ID.

CHANGELOG.md (1)

19-26: LGTM!

Runtime/AppstackAndroidBridge.cs (1)

51-57: LGTM!

Runtime/AppstackIOSBridge.cs (1)

30-31: LGTM!

Also applies to: 65-69

Comment thread Runtime/AppstackSDK.cs
Comment on lines +79 to +85
/// <summary>
/// Set — or clear — the customer user ID after <see cref="Configure"/>, e.g. once a
/// login reveals it. A repeat <see cref="Configure"/> is a no-op, so it cannot be
/// used to change the ID. Safe to call at any time; last write wins.
/// </summary>
/// <param name="customerUserId">Your identifier for the signed-in user. `null`, an
/// empty string, or whitespace clears it — see <see cref="ClearCustomerUserId"/>.</param>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Align the Configure lifecycle contract.

Configure still says it must run before any other SDK method. This method says it is safe at any time. README.md and USAGE.md also allow calls before Configure. Choose one contract. If pre-configuration calls are supported, document SetCustomerUserId and ClearCustomerUserId as exceptions in the Configure documentation.

Proposed documentation fix
-        /// Must be called before any other SDK methods.
+        /// Must be called before event, status, and attribution methods.
+        /// SetCustomerUserId and ClearCustomerUserId may be called before or after Configure.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Runtime/AppstackSDK.cs` around lines 79 - 85, Align the Configure lifecycle
documentation with the supported pre-configuration behavior: update Configure’s
documentation to explicitly exempt SetCustomerUserId and ClearCustomerUserId,
while preserving their safe-at-any-time semantics and the existing README.md and
USAGE.md guidance.

Comment thread Runtime/AppstackSDK.cs
Comment on lines +103 to +110
/// <summary>
/// Clear the stored customer user ID — call this on logout, otherwise the previous
/// user's ID stays attached to every later event. Equivalent to
/// <c>SetCustomerUserId(null)</c>.
/// </summary>
public static void ClearCustomerUserId()
{
SetCustomerUserId(null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the API parity description.

The class-level documentation says that AppstackSDK has the same API surface as Flutter and React Native. ClearCustomerUserId is documented as Unity-only in DEVELOPMENT.md. Update the class summary to describe the Unity-specific convenience wrapper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Runtime/AppstackSDK.cs` around lines 103 - 110, Update the class-level
AppstackSDK summary to state that ClearCustomerUserId is a Unity-specific
convenience wrapper, while preserving the existing Flutter and React Native API
parity description where applicable.

Comment thread Runtime/Plugins/Android/com/appstack/unity/AppstackUnityBridge.java
Comment thread Tests~/Native/iOS/run-tests.sh
@jsantelys-as

jsantelys-as commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator Author

main is merged in (2f1cf60). Both blockers listed in the original description are resolved.

Android pin. #9 moved the pin past #7's 1.7.0-SNAPSHOT to the stable 1.7.0 on Maven Central, so real-artifact compiles against a plain release rather than a mutable, expiring coordinate.

iOS artifact. No re-cut is required. The 4.5.0-rc0 finding was based on the AppstackSDK.xcframework directory committed in ios-appstack-sdk, which is vestigial after the move to binaryTarget(url:checksum:) and is byte-identical across 4.4.0 through 4.5.0. SPM downloads the release zip, and the stable 4.5.0 zip exports setCustomerUserId at the checksum Package.swift declares. #9 pins iOS to 4.5.0, and #10 changes the iOS fixture to resolve and checksum that artifact instead of reading the stale directory, which is what makes this PR's new expected symbol a meaningful check.

Verification on the merge: 112 editor tests, 36 Android contract tests, and 12 iOS Swift contract tests pass. real-artifact compiles the Java bridge, including the new setCustomerUserId call, against the real 1.7.0. The Swift bridge compiles against the checksum-verified 4.5.0 artifact with all eight expected C symbols present, including AppstackUnitySetCustomerUserId.

main was merged rather than rebased, to avoid a force-push that would strand the existing review anchors.

@jsantelys-as
jsantelys-as merged commit d71b0c9 into main Aug 12, 2026
1 check passed
@jsantelys-as
jsantelys-as deleted the feat/jsantelys/set-customer-user-id branch August 12, 2026 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant