Skip to content

SDK-300 Add OnSuccess/OnFailure handlers to logoutUser#1069

Merged
sumeruchat merged 8 commits into
masterfrom
fix/SDK-300-logout-success-failure-handlers
Jun 2, 2026
Merged

SDK-300 Add OnSuccess/OnFailure handlers to logoutUser#1069
sumeruchat merged 8 commits into
masterfrom
fix/SDK-300-logout-success-failure-handlers

Conversation

@sumeruchat

@sumeruchat sumeruchat commented May 27, 2026

Copy link
Copy Markdown
Contributor

What

Adds a public IterableAPI.logoutUser(withOnSuccess:onFailure:) overload so SDK consumers can observe the logout sequence. The handlers signal that the local logout sequence completed and surface the triggered disableDeviceForCurrentUser result — they are an observability primitive, not a retry mechanism (the disable already auto-retries offline via SDK-297). Ticket: SDK-300.

Changes

  • Public API (IterableAPI.swift): New logoutUser(withOnSuccess:onFailure:) overload. Parameterless logoutUser() now delegates with nil/nil handlers, preserving Obj-C and source compatibility. Mirrors disableDeviceForCurrentUser's two-overload precedent.
  • Internal (InternalIterableAPI.swift): New logoutUser(withOnSuccess:onFailure:). Not-initialized → onFailure. autoPushRegistration on → handlers piped through the existing disableDeviceForCurrentUser(withOnSuccess:onFailure:) before identity cleanup (so the disable request goes out with the still-set userId/email), then local cleanup runs. autoPushRegistration off → local cleanup, then onSuccess(nil) synchronously.
  • logoutPreviousUser() untouched — internal user-switch paths keep their existing fire-and-forget contract.
  • Tests (AuthTests.swift): 5 new tests covering autoPush-on success, autoPush-on failure (verifies local cleanup STILL happens despite network error), autoPush-off synchronous success with no network, not-initialized failure, and the parameterless overload regression.

Impact

  • Breaking changes: None. The existing parameterless logoutUser() is unchanged; only adds a new overload.
  • Dependencies: None.
  • Performance: No-op — handlers are only invoked at the existing completion points.

Testing

How to test:

  1. Run ./agent_test.sh AuthTests — all 42 tests pass (5 new).
  2. Call IterableAPI.logoutUser(withOnSuccess:onFailure:) from a sample app with autoPushRegistration = true and a logged-in user; verify onSuccess fires after the disable network call returns.
  3. With autoPushRegistration = false, verify onSuccess fires synchronously and no network request is made.
  4. Without initializing the SDK (or without a logged-in user), verify onFailure fires with Iterable SDK is not initialized.
  5. Confirm the existing parameterless logoutUser() still logs out without regression.

Adds a public `IterableAPI.logoutUser(withOnSuccess:onFailure:)` overload
for observability around the logout sequence. logoutUser/logoutPreviousUser
are local-only operations -- the only network side-effect is the
disableDeviceForCurrentUser triggered when autoPushRegistration is on, and
that already auto-retries offline via SDK-297. The handlers signal that the
local sequence completed and surface the triggered disableDevice's result;
they are not a retry mechanism.

- Public: new `logoutUser(withOnSuccess:onFailure:)` on IterableAPI; the
  existing parameterless `logoutUser()` delegates to it with nil/nil
  handlers, preserving Obj-C and source compatibility. Mirrors the
  precedent set by disableDeviceForCurrentUser's two-overload shape.
- Internal: new InternalIterableAPI.logoutUser(withOnSuccess:onFailure:).
  Not-initialized -> onFailure. autoPushRegistration on -> handlers piped
  through the existing disableDeviceForCurrentUser(withOnSuccess:onFailure:)
  *before* clearing identity (so the disable request goes out with the
  still-set userId/email), then local cleanup runs. autoPushRegistration
  off -> local cleanup, then onSuccess(nil) synchronously.
- logoutPreviousUser() left untouched -- still used on internal user-switch
  paths with its existing fire-and-forget contract.
- Tests in AuthTests cover: autoPush-on success, autoPush-on failure
  (local cleanup STILL happens despite network error), autoPush-off
  synchronous success with no network, not-initialized failure, and the
  parameterless overload regression.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented May 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.11765% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.31%. Comparing base (481f525) to head (dc2302a).

Files with missing lines Patch % Lines
swift-sdk/SDK/IterableAPI.swift 83.33% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1069      +/-   ##
==========================================
+ Coverage   71.27%   71.31%   +0.03%     
==========================================
  Files         112      112              
  Lines        9369     9389      +20     
==========================================
+ Hits         6678     6696      +18     
- Misses       2691     2693       +2     

☔ 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.

}

func logoutUser() {
logoutPreviousUser()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Before this PR, logoutUser() delegated to logoutPreviousUser() (line 251 on master), keeping a single source of truth for the eight-step logout cleanup sequence. After this PR, that body lives in two places:

  • logoutPreviousUser() (line 877), used by setEmail/setUserId user-switch paths.
  • logoutUser(withOnSuccess:onFailure:) (line 254), used by both public entry points.

@sumeruchat sumeruchat May 29, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@franco-zalamena-iterable Good catch and appreciate the attention to detail. This is fixed in c0193b7. logoutPreviousUser() now delegates to logoutUser(withOnSuccess:onFailure:) with nil handlers instead of carrying its own copy of the eight-step cleanup, so the sequence has a single source of truth again.

Behavior is unchanged for the setEmail/setUserId user-switch paths: a nil onFailure keeps the not-initialized guard a silent no-op, and a nil onSuccess makes the auto-push-off completion a no-op — exactly matching the old logoutPreviousUser() body.

Verified: build green, AuthTests 42/0, and UserMergeScenariosTests 21/0 (which exercises the user-switch paths through logoutPreviousUser).

Per Franco's review, collapse the duplicated logout cleanup sequence.
logoutPreviousUser() now delegates to logoutUser(withOnSuccess:onFailure:)
with nil handlers instead of carrying its own copy of the eight-step
cleanup. The full sequence lives in exactly one place.

Behavior is unchanged for the setEmail/setUserId user-switch paths: a nil
onFailure keeps the not-initialized guard a silent no-op, and a nil
onSuccess makes the auto-push-off completion a no-op.

Verified: ./agent_build.sh green; AuthTests 42/0; UserMergeScenariosTests
21/0 (exercises the user-switch paths through logoutPreviousUser).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@franco-zalamena-iterable franco-zalamena-iterable left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just one thing to address but non blocker anymore

Comment thread swift-sdk/Internal/InternalIterableAPI.swift
sumeruchat and others added 3 commits May 29, 2026 10:17
Per Franco's review follow-up: now that logoutPreviousUser() delegates to
logoutUser(withOnSuccess:onFailure:), the parameterless logoutUser() can
keep calling logoutPreviousUser() as it did on master instead of calling
the handler overload directly. Minimizes the diff and preserves the
original delegation; behavior is unchanged (logoutUser() ->
logoutPreviousUser() -> logoutUser(withOnSuccess:nil,onFailure:nil)).

Build green; AuthTests 42/0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Following a detailed handler-correctness review (no functional bug found),
clarify the observability contract and close the one test gap:

- Doc comment on the public logoutUser(withOnSuccess:onFailure:) spelling
  out that logout is local-only and the handlers reflect the triggered
  disableDevice result when autoPushRegistration is on. Notably, an
  onFailure (e.g. "no token present", or a failed disable request) does
  NOT mean the local logout failed. Also documents that handlers are
  in-process only (not persisted across app termination) and not
  guaranteed to be delivered on the main thread.
- Test: autoPush ON with no registered token now asserts onFailure fires
  with "no token present", that local cleanup STILL completes, and that no
  disableDevice request is sent.

Build green; AuthTests 43/0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…success-failure-handlers

* origin/master:
  SDK-493: Prepare for Release 6.7.2 (#1070)
Comment thread swift-sdk/SDK/IterableAPI.swift Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this be updated with the same level of error as we have on logoutUser?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch, addressed in 7708de1. Both disableDeviceForCurrentUser and disableDeviceForAllUsers completion overloads now fire onFailure("Iterable SDK is not initialized", nil) on the not-initialized guard, matching logoutUser. The parameterless overloads pass nil handlers, so they are unchanged. Added a test, AuthTests 44/0.

sumeruchat and others added 3 commits June 2, 2026 11:44
Per Joao's review, make the disableDeviceForCurrentUser and
disableDeviceForAllUsers completion overloads call
onFailure("Iterable SDK is not initialized", nil) on the not-initialized
guard, matching logoutUser. The parameterless overloads pass nil handlers,
so they are unchanged.

AuthTests 44/0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…success-failure-handlers

* origin/master:
  SDK-469 Fix CodeQL string length conflation in deep link check (#1071)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sumeruchat sumeruchat merged commit 62a33d3 into master Jun 2, 2026
15 of 16 checks passed
@sumeruchat sumeruchat deleted the fix/SDK-300-logout-success-failure-handlers branch June 2, 2026 13:35
sumeruchat added a commit that referenced this pull request Jun 2, 2026
…r-token-offline-retry

* origin/master:
  SDK-300 Add OnSuccess/OnFailure handlers to logoutUser (#1069)
  SDK-469 Fix CodeQL string length conflation in deep link check (#1071)
  SDK-493: Prepare for Release 6.7.2 (#1070)
sumeruchat added a commit that referenced this pull request Jun 2, 2026
…ush-async-await

* origin/master:
  SDK-108 Route registerToken through OfflineRequestProcessor for network-failure retry (#1068)
  SDK-300 Add OnSuccess/OnFailure handlers to logoutUser (#1069)
  SDK-469 Fix CodeQL string length conflation in deep link check (#1071)
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.

3 participants