Skip to content

SDK-412 UUA Naming inconsistencies#1060

Open
joaodordio wants to merge 3 commits into
masterfrom
fix/SDK-412-UUA-Naming-inconsistencies
Open

SDK-412 UUA Naming inconsistencies#1060
joaodordio wants to merge 3 commits into
masterfrom
fix/SDK-412-UUA-Naming-inconsistencies

Conversation

@joaodordio
Copy link
Copy Markdown
Member

@joaodordio joaodordio commented Apr 28, 2026

🔹 Jira Ticket(s)

✏️ Description

Brings the iOS SDK in line with the cross-SDK Unknown User Activation (UUA) naming convention. All public renames ship with @available(*, deprecated, renamed:) forwarders, so source-compatible. On-disk formats migrate read-old/write-new on first access, so existing users keep their unknown sessions across the upgrade.

Public API renames (deprecated aliases kept)

  • UnknownUserManager: trackUnknownUserEvent to trackUnknownEvent, trackUnknownUserUpdateUser to trackUnknownUpdateUser, trackUnknownUserPurchaseEvent to trackUnknownPurchaseEvent, trackUnknownUserUpdateCart to trackUnknownUpdateCart, trackUnknownUserTokenRegistration to trackUnknownTokenRegistration, updateUnknownUserSession to updateUnknownSession, getUnknownUserCriteria to getUnknownCriteria. Same renames mirrored on UnknownUserManagerProtocol.
  • IterableIdentityResolution.mergeOnUnknownUserToKnown is now mergeOnUnknownToKnown. Deprecated computed alias and a deprecated convenience init keep the old call sites working.

Internal renames

  • Const.Path.trackUnknownUserSession to trackUnknownSession.
  • ApiClientProtocol.trackUnknownUserSession to trackUnknownSession.
  • RequestCreator.createTrackUnknownUserSessionRequest to createTrackUnknownSessionRequest.

Storage migrations

  • Keychain: itbl_userid_unknown_user to itbl_userid_unknown. Legacy key still readable via a one-shot migration in the userIdUnknownUser getter.
  • UserDefaults: itbl_unknown_user_sessions to itbl_unknown_sessions. Legacy blob is decoded, re-encoded under the new key, then removed.
  • IterableUnknownUserSessionsWrapper and IterableUnknownUserSessions now use CodingKeys to encode the new schema (itbl_unknown_sessions / totalUnknownSessionCount / lastUnknownSession / firstUnknownSession) while still decoding both old and new payloads. Swift property names stayed put to keep the diff small.
  • Stored event discriminator: JsonKey.eventType is now "eventType" (was "dataType"). unknownUserEvents and unknownUserUpdate getters normalize legacy entries on read and persist them back.

Tests

  • New UUANormalizationMigrationTests.swift covers the keychain migration, UserDefaults sessions blob migration, sessions wrapper round-trip (decodes legacy and modern, encodes only modern), the dataType to eventType rewrite on stored events, and the IterableIdentityResolution deprecated init alias.
  • Existing UUA tests updated to reference the canonical names.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 28, 2026

Codecov Report

❌ Patch coverage is 92.66055% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.65%. Comparing base (e901170) to head (0af342f).

Files with missing lines Patch % Lines
swift-sdk/Internal/InternalIterableAPI.swift 72.72% 3 Missing ⚠️
swift-sdk/Internal/UnknownUserManager.swift 90.32% 3 Missing ⚠️
swift-sdk/SDK/IterableAPI.swift 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1060      +/-   ##
==========================================
+ Coverage   71.11%   71.65%   +0.53%     
==========================================
  Files         112      112              
  Lines        9328     9399      +71     
==========================================
+ Hits         6634     6735     +101     
+ Misses       2694     2664      -30     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@joaodordio joaodordio marked this pull request as ready for review April 29, 2026 00:55
@joaodordio joaodordio requested a review from sumeruchat April 29, 2026 00:56
Copy link
Copy Markdown
Contributor

@sumeruchat sumeruchat left a comment

Choose a reason for hiding this comment

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

Review notes

A few things worth addressing before merge. Ordered by severity.

🔴 1. IterableUserDefaults migration reads from the wrong store

swift-sdk/Internal/IterableUserDefaults.swift:165–175

Both the new-key and legacy-key reads use UserDefaults.standard, but writes/removals use the injected userDefaults. For any consumer on a non-default suite this means:

  • Legacy data in the configured suite is invisible to the migration → silent data loss
  • Unrelated data at the legacy key in .standard gets pulled into the configured suite → cross-store contamination

Fix: use userDefaults for the reads. Worth grepping the rest of the file — the pre-existing reads on this same path look like they have the same shape.

🔴 2. The new sessions migration test isn't exercising the migration path

tests/unit-tests/UUANormalizationMigrationTests.swifttestSessionsBlobMigratesFromLegacyUserDefaultsKey

The test writes to a suite-scoped UserDefaults, but production reads from .standard. It's passing by coincidence, not because the migration runs. Falls out of fixing #1, but please confirm the test actually fails without the fix.

🟡 3. Public UnknownUserManagerProtocol breaks external conformers

swift-sdk/Internal/UnknownUserManagerProtocol.swift:9–17

Adding the renamed methods as required members of an @objc public protocol breaks any downstream conformer (test doubles, wrappers) — they now have to implement both old and new spellings.

Suggested fix: drop the new methods from the protocol for this release. Keep only the deprecated names there; put the renamed methods on the concrete UnknownUserManager only. Internal call sites talk to the class anyway. Revisit at the next major.

🟡 4. Missing migration test for unknownUserUpdate

tests/unit-tests/UUANormalizationMigrationTests.swift

Only the array variant unknownUserEvents has a dataType → eventType test. The single-dict unknownUserUpdate getter runs through a separate path. One small mirror test.

🟢 5. Question: do we actually need to rename the keychain key?

swift-sdk/Internal/Utilities/Keychain/IterableKeychain.swift

The keychain key is internal-only — no backend, no host app, no other SDK reads it. Renaming itbl_userid_unknown_user → itbl_userid_unknown buys cosmetic alignment, but costs:

  • A permanent migration path
  • An extra keychain syscall on every set, forever
  • A downgrade footgun (revert to old build → unknown-user identity disappears)

Same argument arguably applies to the itbl_unknown_user_sessions UserDefaults key. The public API renames and the dataType → eventType discriminator change have clear justification — these two on-disk renames don't. Suggest reverting just those.


TL;DR: #1 is a real bug (silent data loss on non-default suites); #2 falls out of fixing it; #3 and #4 are strong recommends; #5 is a design question worth discussing before merge.

joaodordio added 3 commits May 6, 2026 18:50
- Drop new methods from `UnknownUserManagerProtocol` and retype
  `InternalIterableAPI.unknownUserManager` to the concrete
  `UnknownUserManager` so internal call sites keep the new names
  without breaking external conformers (review #3).
- Revert the keychain key (`itbl_userid_unknown_user`) and the
  UserDefaults sessions wrapper key (`itbl_unknown_user_sessions`)
  back to their pre-PR values; both are local-only with no backend
  contract, so the perpetual migration cost and downgrade footgun
  weren't worth the cosmetic alignment (review #5). Removes the
  associated migrations in `IterableKeychain` / `IterableUserDefaults`
  and the `UserDefaults.standard`-vs-injected-store bug they
  introduced (review #1, #2).
- Keep the inner `IterableUnknownUserSessions` field rename to align
  with Android per ticket #3 — these names are sent to the backend in
  `unknownSessionContext`, so cross-SDK alignment matters here. Uses
  `CodingKeys` for the new payload + a backward-compatible decoder
  for existing on-disk blobs.
- Keep the `dataType → eventType` discriminator rename with one-shot
  read normalization (ticket #5).
- Keep the public `UnknownUserManager` / `IterableIdentityResolution`
  renames with deprecated forwarders.
Tests: wire `UUANormalizationMigrationTests` into the Xcode project
(was orphaned), add the missing `unknownUserUpdate` migration test
(review #4), add deprecated-forwarder coverage for every renamed
`UnknownUserManager` method, and assert the encoded sessions payload
uses the Android-aligned field names while still decoding legacy
blobs.
@joaodordio joaodordio force-pushed the fix/SDK-412-UUA-Naming-inconsistencies branch from 24b5343 to 0af342f Compare May 6, 2026 16:50
@joaodordio joaodordio requested a review from sumeruchat May 7, 2026 12:43
@joaodordio joaodordio self-assigned this May 11, 2026
Copy link
Copy Markdown
Contributor

@sumeruchat sumeruchat left a comment

Choose a reason for hiding this comment

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

Thanks for the quick turnaround on the review notes. Re-reviewed commit 0af342f and verified each point:

  • 🔴 #1, #2 (UserDefaults.standard vs injected store + non-load-bearing test): resolved by deleting the migration entirely. Buggy code is gone, not patched.
  • 🟡 #3 (public protocol break): protocol surface reverted to pre-PR; InternalIterableAPI.unknownUserManager retyped to the concrete class so internal call sites keep the new names. Clean.
  • 🟡 #4 (missing unknownUserUpdate test): testUnknownUserUpdateRewritesLegacyDataTypeKeyOnRead added.
  • 🟢 #5 (keychain / sessions wrapper key renames): both reverted to original strings. Nice distinction between the on-disk wrapper key (reverted, internal-only) and the inner sessions field names (kept — those go to the backend in unknownSessionContext, so cross-SDK alignment matters there).

Bonus coverage I noticed beyond what was asked for:

  • testCriteriaCheckerNormalizesLegacyDataTypeOnInit — guards the criteriaDataType vs eventType invariant
  • testUnknownUserEventsLeavesModernEventsUntouched — idempotency on modern data
  • Deprecated-forwarder tests on every renamed UnknownUserManager method
  • UUANormalizationMigrationTests wired into the xcodeproj (was orphaned)

Ran ./agent_test.sh UUANormalizationMigrationTests locally — all 17 tests green.

🟢 LGTM.

@sumeruchat
Copy link
Copy Markdown
Contributor

sumeruchat commented May 13, 2026

@joaodordio one important pre-merge check I want to flag separately from the approval:

The inner IterableUnknownUserSessions field rename (totalUnknownUserSessionCounttotalUnknownSessionCount, lastUnknownUserSessionlastUnknownSession, firstUnknownUserSessionfirstUnknownSession) changes the JSON keys that get sent to the backend in the unknownSessionContext body on /unknownuser/events/session.

Before this merges, can you confirm the Iterable backend already accepts the new field names? Either:

  • The endpoint accepts both old and new keys today (preferred — safe rollout), or
  • The backend rolled out support for the new names ahead of this SDK release

If neither is true, every customer's unknown-user session tracking degrades silently the moment they pick up this SDK version — that's a quiet production regression with no client-side error.

@sumeruchat
Copy link
Copy Markdown
Contributor

One more housekeeping item @joaodordio — please add a note to the release notes (or CHANGELOG.md) calling out the upgrade-then-downgrade hazard on the sessions blob:

IterableUnknownUserSessions now encodes with the new field names (totalUnknownSessionCount, lastUnknownSession, firstUnknownSession). A user who installs an SDK version with this change and later downgrades to a pre-SDK-412 build will hit a decode failure on their stored sessions blob, since the older SDK's synthesized Codable expects the old key names. The blast radius is small — unknown-user session counter resets to zero — but it's a real behavior change worth flagging so customers who pin or roll back SDK versions aren't surprised.

Same goes for the dataType → eventType discriminator on unknownUserEvents/unknownUserUpdate — once the new SDK rewrites those entries, an older SDK reading them won't find the dataType key and will skip the events.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants