Skip to content

Release 1.4.1 - #53

Merged
motasem-userpilot merged 3 commits into
mainfrom
1.4.1
Oct 4, 2026
Merged

motasem-userpilot merged 3 commits into
mainfrom
1.4.1

Conversation

@motasem-userpilot

Copy link
Copy Markdown
Collaborator

🚀 Description

Release Userpilot iOS SDK 1.4.1.

Backend now owns identify de-duplication, so the SDK no longer drops “unchanged” identify calls client-side. Every identify is forwarded; the pending identify is still cached in temporaryUser until the backend acknowledges it.

Affected areas

  • AnalyticsPublisher event routing for identify
  • Internal User comparison helpers (removed)
  • Version bump: 1.4.0 → 1.4.1 (Version.swift, Userpilot.podspec)
  • Unit tests updated for the new identify path

Public API

  • No host-app / @objc surface changes. Removed helpers were internal (isSameIdentifyEvent, ComparisonStrategy, dictionary compare utilities).

📄 Motivation and Context

Client-side identify de-dupe could drop legitimate identifies (and complicated socket replay). With server-side de-duplication in place, the SDK should always send identify and keep only the pending-user cache until ACK.

Commits on 1.4.1 since main:

  • 985e0cf — remove client-side identify de-duplication
  • e4de00d — bump version to 1.4.1

📦 Types of Changes

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Refactor (code change that neither fixes a bug nor adds a feature)
  • Optimization (improves performance without changing behavior)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

🔌 Public API & Wrapper Impact

  • No public API change — nothing below applies
  • Public API changed, and it is additive only
  • Public API changed in a breaking way (requires a major version bump)

If the public API changed, confirm:

  • New/changed symbols are @objc-exposed and Objective-C-bridgeable, and non-throwing
  • Doc comments added on new public symbols
  • Wrapper migration notes written below
Wrapper migration notes (required for any public API change)

None needed — internal analytics/storage helpers only; Flutter / React Native / Capacitor can bump to native 1.4.1 with no bridge API changes.

🧪 How Has This Been Tested?

  • Identify no longer short-circuits when the cached user matches the event; pending user is still written to temporaryUser.

  • Related XCTest coverage updated (AnalyticsPublisherTests, removed obsolete UserTests comparison cases).

  • Unit tests (XCTest)

  • Manual testing on real devices

  • Manual testing on simulators

  • Tested on different iOS versions (please list — deployment target is iOS 13)

  • Tested with different device types (iPhone, iPad)

📷 Screenshots / Screen Recordings (if appropriate)

N/A — analytics routing / internal storage change; no UI.

🛠 Migration Notes (optional)

  1. Upgrade the iOS SDK / CocoaPods dependency to 1.4.1.
  2. No host-app API or config changes.
  3. Expect unchanged identify calls to reach the backend (server de-dupes); no client-side drop.

⚠️ Deprecations (optional)

None. Internal-only removals:

  • User.isSameIdentifyEvent(...)
  • ComparisonStrategy
  • Dictionary deep-compare helpers used only for identify de-dupe
  • AnalyticsPublisher.didHandleIdentifyEvent(_:)

🧹 Cleanup Tasks (optional)

None.

✅ Checklist

  • My code follows the project's coding standards and conventions (AGENTS.md)
  • Environment is .PRODUCTION with placeholder socket URL / token, and no NX- in SocketManager
  • xcodebuild test -scheme Userpilot passes, and I added tests where applicable
  • swiftlint --strict is clean (no new warnings or errors in Xcode)
  • XCFramework still builds (device + simulator)
  • I have updated documentation (docs/, README, DocC) if needed
  • My changes are backwards compatible (unless flagged as breaking above)
  • If I added files or resources, Userpilot.podspec still covers them (pod lib lint)
  • No PII, tokens, or secrets are logged

Made with Cursor

motasem-userpilot and others added 2 commits October 2, 2026 14:02
The backend now de-duplicates identify calls, so the SDK no longer
compares an identify with the cached user and drops it when nothing
changed. Every identify is sent, and the pending identify is still
cached until the backend acknowledges it. Removes the now-unused User
comparison helpers and their tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Changes identify event deduplication logic in the analytics publisher.

The PR does not appear safe to merge while a later identify can replace a newer pending user before ACK.

Findings

  1. P1 Pending user updates disappear ▶
  2. P2 Repeated identifies reload screens ▶
Summary

The SDK now forwards active identify events while caching each as pending, leaving duplicate handling to the backend. It also removes internal comparison code, updates the release version to 1.4.1, and adjusts network monitor test waits.

  • Matching identify events continue through the publisher’s routing path.
  • The SDK and CocoaPods versions now both read 1.4.1.
  • Network monitor tests wait for observed state changes instead of short fixed delays.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A["App calls identify"] --> B["Cache pending user"]
  B --> C["Queue or save identify"]
  C --> D["Backend ACK"]
  D --> E["Update saved user and clear pending user"]
Loading

Reviews (2) · Last reviewed commit: "✅ test(Monitor): Wait for network state ..."

// Drop identify events that carry nothing new. Non-identify events skip this.
if event.isIdentifyEvent, didHandleIdentifyEvent(event) { return }
// Cache the pending user until the backend acknowledges the identify
if event.isIdentifyEvent { storage.temporaryUser = event.toUser().toJson() }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Pending user updates disappear

If an identify with new properties is waiting for an ACK, a later identify with the old properties replaces temporaryUser. If the app closes before either ACK, startup restores only the old properties and loses the pending update. Keep the newer pending data when saving another identify.

// Drop identify events that carry nothing new. Non-identify events skip this.
if event.isIdentifyEvent, didHandleIdentifyEvent(event) { return }
// Cache the pending user until the backend acknowledges the identify
if event.isIdentifyEvent { storage.temporaryUser = event.toUser().toJson() }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Repeated identifies reload screens

When an app calls identify again with unchanged data, its ACK can queue a fake screen reload if a screen is active and the event queue is empty. Backend de-duplication cannot stop this extra client-side request. It sends another screen event and makes later events wait for its ACK. Avoid the reload when nothing about the user changed.

Replace fixed setup delays with expectations for monitor readiness and
interface loss. Keep a slower debounce in the interface-down test to
cover the timing condition that failed the PR quality gate.

SDK behavior is unchanged.
@motasem-userpilot
motasem-userpilot merged commit 52eeb6e into main Oct 4, 2026
6 checks passed
@motasem-userpilot
motasem-userpilot deleted the 1.4.1 branch October 4, 2026 12:15
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