Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,22 @@ struct OfflineRequestProcessor: RequestProcessorProtocol {
taskRunner.stop()
}

@discardableResult
func register(registerTokenInfo: RegisterTokenInfo,
notificationsEnabled: Bool,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError> {
let requestGenerator = { (requestCreator: RequestCreator) in
requestCreator.createRegisterTokenRequest(registerTokenInfo: registerTokenInfo,
notificationsEnabled: notificationsEnabled)
}

return sendIterableRequest(requestGenerator: requestGenerator,
successHandler: onSuccess,
failureHandler: onFailure,
identifier: RequestIdentifier.registerToken)
}

@discardableResult
func disableDeviceForCurrentUser(hexToken: String,
identitySnapshot: UserIdentitySnapshot?,
Expand Down
30 changes: 9 additions & 21 deletions swift-sdk/Internal/api-client/Request/OnlineRequestProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ struct OnlineRequestProcessor: RequestProcessorProtocol {
dateProvider: dateProvider)
}

@discardableResult
func register(registerTokenInfo: RegisterTokenInfo,
notificationStateProvider: NotificationStateProviderProtocol,
notificationsEnabled: Bool,
onSuccess: OnSuccessHandler? = nil,
onFailure: OnFailureHandler? = nil) {
notificationStateProvider.isNotificationsEnabled { enabled in
self.register(registerTokenInfo: registerTokenInfo,
notificationsEnabled: enabled,
onSuccess: onSuccess,
onFailure: onFailure)
}
onFailure: OnFailureHandler? = nil) -> Pending<SendRequestValue, SendRequestError> {
sendRequest(requestProvider: { apiClient.register(registerTokenInfo: registerTokenInfo,
notificationsEnabled: notificationsEnabled) },
successHandler: onSuccess,
failureHandler: onFailure,
requestIdentifier: RequestIdentifier.registerToken)
}

@discardableResult
func disableDeviceForCurrentUser(hexToken: String,
identitySnapshot: UserIdentitySnapshot?,
Expand Down Expand Up @@ -309,18 +309,6 @@ struct OnlineRequestProcessor: RequestProcessorProtocol {
private let apiClient: ApiClientProtocol
private weak var authManager: IterableAuthManagerProtocol?

@discardableResult
private func register(registerTokenInfo: RegisterTokenInfo,
notificationsEnabled: Bool,
onSuccess: OnSuccessHandler? = nil,
onFailure: OnFailureHandler? = nil) -> Pending<SendRequestValue, SendRequestError> {
sendRequest(requestProvider: { apiClient.register(registerTokenInfo: registerTokenInfo,
notificationsEnabled: notificationsEnabled) },
successHandler: onSuccess,
failureHandler: onFailure,
requestIdentifier: "registerToken")
}

@discardableResult
private func disableDevice(forAllUsers allUsers: Bool,
hexToken: String,
Expand Down
9 changes: 7 additions & 2 deletions swift-sdk/Internal/api-client/Request/RequestCreator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct RequestCreator {

func createRegisterTokenRequest(registerTokenInfo: RegisterTokenInfo,
notificationsEnabled: Bool) -> Result<IterableRequest, IterableError> {
let auth = registerTokenInfo.auth ?? self.auth
if case .none = auth.emailOrUserId {
ITBError(Self.authMissingMessage)
return .failure(IterableError.general(description: Self.authMissingMessage))
Expand All @@ -60,8 +61,8 @@ struct RequestCreator {

body[JsonKey.device] = deviceDictionary

setCurrentUser(inDict: &body)
setCurrentUser(auth: auth, inDict: &body)

if auth.email == nil, (auth.userId != nil || auth.userIdUnknownUser != nil) {
body[JsonKey.preferUserId] = true
}
Expand Down Expand Up @@ -737,6 +738,10 @@ struct RequestCreator {
}

private func setCurrentUser(inDict dict: inout [AnyHashable: Any]) {
setCurrentUser(auth: auth, inDict: &dict)
}

private func setCurrentUser(auth: Auth, inDict dict: inout [AnyHashable: Any]) {
switch auth.emailOrUserId {
case let .email(email):
dict.setValue(for: JsonKey.email, value: email)
Expand Down
28 changes: 24 additions & 4 deletions swift-sdk/Internal/api-client/Request/RequestHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,30 @@ class RequestHandler: RequestHandlerProtocol {
notificationStateProvider: NotificationStateProviderProtocol,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) {
onlineProcessor.register(registerTokenInfo: registerTokenInfo,
notificationStateProvider: notificationStateProvider,
onSuccess: onSuccess,
onFailure: onFailure)
// Snapshot identity before the async notification-state callback so deferred
// request construction still targets the call-time user.
let missingIdentityReason = "register(token:) called without a current user identity"
guard let auth = authProvider?.auth else {
ITBError(missingIdentityReason)
onFailure?(missingIdentityReason, nil)
return
}
if case .none = auth.emailOrUserId {
ITBError(missingIdentityReason)
onFailure?(missingIdentityReason, nil)
return
}
var registerTokenInfo = registerTokenInfo
registerTokenInfo.auth = auth

notificationStateProvider.isNotificationsEnabled { notificationsEnabled in
_ = self.sendUsingRequestProcessor { processor in

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.

On master, register went straight to OnlineRequestProcessor and built the body synchronously after isNotificationsEnabled, so setCurrentUser read auth essentially at call time. After this PR, the build runs inside sendUsingRequestProcessor's deferred closure (after canSchedule() + a queue hop), and createRegisterTokenRequest still reads live authProvider.auth rather than a captured UserIdentitySnapshot like disableDeviceForCurrentUser does. Intentional, or should register mirror the SDK-297 snapshot pattern?

@sumeruchat sumeruchat Jun 1, 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.

Mirrored the snapshot pattern in fb361ff. register now captures UserIdentitySnapshot at call time (before the async notification hop) and threads it into createRegisterTokenRequest, same as disableDeviceForCurrentUser. Tests cover offline-replay and online-fallback keeping the call-time identity. 36/0.

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.

Optional UserIdentitySnapshot? on six signatures + a forked branch in RequestCreator feels heavy for a race that only exists in RequestHandler.register's async hop. The disableDevice symmetry is also partial, those tasks survive handleLogout(), register doesn't. Can we fix it where the gap actually is instead of threading a parameter through every layer below it?

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.

Simplified in eed546e. Dropped the cross-layer snapshot threading: capture call-time auth once in register, carry it on RegisterTokenInfo.auth, and RequestCreator resolves it through the existing single body path. ApiClient, both processors, and the protocol no longer carry the param. You were right on handleLogout, register tasks are purged on logout (only disableDevice is preserved), so the full symmetry was not needed. Kept a small identity guard in register because dropping it regresses onFailure on the offline no-user path. 36/0.

processor.register(registerTokenInfo: registerTokenInfo,
notificationsEnabled: notificationsEnabled,
onSuccess: onSuccess,
onFailure: onFailure)
}
}
}

@discardableResult
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ struct RegisterTokenInfo {
let deviceAttributes: [String: String]
let sdkVersion: String?
let mobileFrameworkInfo: IterableAPIMobileFrameworkInfo
var auth: Auth? = nil
}

struct UpdateSubscriptionsInfo {
Expand All @@ -27,10 +28,17 @@ struct UpdateSubscriptionsInfo {

enum RequestIdentifier {
static let disableDevice = "disableDevice"
static let registerToken = "registerToken"
}

/// `RequestHandler` will delegate network related calls to this protocol.
protocol RequestProcessorProtocol {
@discardableResult
func register(registerTokenInfo: RegisterTokenInfo,
notificationsEnabled: Bool,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError>

@discardableResult
func disableDeviceForCurrentUser(hexToken: String,
identitySnapshot: UserIdentitySnapshot?,
Expand Down
Loading
Loading