SDK-412 UUA Naming inconsistencies#1060
Conversation
Codecov Report❌ Patch coverage is 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. 🚀 New features to boost your workflow:
|
sumeruchat
left a comment
There was a problem hiding this comment.
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
.standardgets 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.swift → testSessionsBlobMigratesFromLegacyUserDefaultsKey
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.
- 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.
24b5343 to
0af342f
Compare
sumeruchat
left a comment
There was a problem hiding this comment.
Thanks for the quick turnaround on the review notes. Re-reviewed commit 0af342f and verified each point:
- 🔴 #1, #2 (
UserDefaults.standardvs 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.unknownUserManagerretyped to the concrete class so internal call sites keep the new names. Clean. - 🟡 #4 (missing
unknownUserUpdatetest):testUnknownUserUpdateRewritesLegacyDataTypeKeyOnReadadded. - 🟢 #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 thecriteriaDataTypevseventTypeinvarianttestUnknownUserEventsLeavesModernEventsUntouched— idempotency on modern data- Deprecated-forwarder tests on every renamed
UnknownUserManagermethod UUANormalizationMigrationTestswired into the xcodeproj (was orphaned)
Ran ./agent_test.sh UUANormalizationMigrationTests locally — all 17 tests green.
🟢 LGTM.
|
@joaodordio one important pre-merge check I want to flag separately from the approval: The inner Before this merges, can you confirm the Iterable backend already accepts the new field names? Either:
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. |
|
One more housekeeping item @joaodordio — please add a note to the release notes (or
Same goes for the |
🔹 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:trackUnknownUserEventtotrackUnknownEvent,trackUnknownUserUpdateUsertotrackUnknownUpdateUser,trackUnknownUserPurchaseEventtotrackUnknownPurchaseEvent,trackUnknownUserUpdateCarttotrackUnknownUpdateCart,trackUnknownUserTokenRegistrationtotrackUnknownTokenRegistration,updateUnknownUserSessiontoupdateUnknownSession,getUnknownUserCriteriatogetUnknownCriteria. Same renames mirrored onUnknownUserManagerProtocol.IterableIdentityResolution.mergeOnUnknownUserToKnownis nowmergeOnUnknownToKnown. Deprecated computed alias and a deprecated convenience init keep the old call sites working.Internal renames
Const.Path.trackUnknownUserSessiontotrackUnknownSession.ApiClientProtocol.trackUnknownUserSessiontotrackUnknownSession.RequestCreator.createTrackUnknownUserSessionRequesttocreateTrackUnknownSessionRequest.Storage migrations
itbl_userid_unknown_usertoitbl_userid_unknown. Legacy key still readable via a one-shot migration in theuserIdUnknownUsergetter.itbl_unknown_user_sessionstoitbl_unknown_sessions. Legacy blob is decoded, re-encoded under the new key, then removed.IterableUnknownUserSessionsWrapperandIterableUnknownUserSessionsnow useCodingKeysto 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.JsonKey.eventTypeis now"eventType"(was"dataType").unknownUserEventsandunknownUserUpdategetters normalize legacy entries on read and persist them back.Tests
UUANormalizationMigrationTests.swiftcovers the keychain migration, UserDefaults sessions blob migration, sessions wrapper round-trip (decodes legacy and modern, encodes only modern), thedataTypetoeventTyperewrite on stored events, and theIterableIdentityResolutiondeprecated init alias.