Skip to content
Open
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
58 changes: 40 additions & 18 deletions DevCycle/DevCycleClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -423,13 +423,11 @@ public class DevCycleClient {
}
}

public func identifyUser(user: DevCycleUser, callback: IdentifyCompletedHandler? = nil) throws {
guard let currentUser = self.user, !currentUser.userId.isEmpty,
!user.userId.isEmpty
else {
throw ClientError.InvalidUser
}
private func _identifyUser(
user: DevCycleUser, currentUser: DevCycleUser, callback: IdentifyCompletedHandler? = nil
) {
self.flushEvents()

var updateUser: DevCycleUser = currentUser
if currentUser.userId == user.userId {
updateUser.update(with: user)
Expand Down Expand Up @@ -481,22 +479,46 @@ public class DevCycleClient {
})
}

@available(*, deprecated, message: "Use the non-throwing or async identifyUser method instead")
public func identifyUser(user: DevCycleUser, callback: IdentifyCompletedHandler? = nil) throws {
guard let currentUser = self.user, !currentUser.userId.isEmpty,
!user.userId.isEmpty
else {
throw ClientError.InvalidUser
}

self._identifyUser(user: user, currentUser: currentUser, callback: callback)
}

public func identifyUser(user: DevCycleUser, completion: IdentifyCompletedHandler? = nil) {
guard let currentUser = self.user, !currentUser.userId.isEmpty,
!user.userId.isEmpty
else {
completion?(ClientError.InvalidUser, nil)
return
}

self._identifyUser(user: user, currentUser: currentUser, callback: completion)
}

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public func identifyUser(user: DevCycleUser) async throws -> [String: Variable]? {
guard let currentUser = self.user, !currentUser.userId.isEmpty,
!user.userId.isEmpty
else {
throw ClientError.InvalidUser
}

return try await withCheckedThrowingContinuation { continuation in
do {
try self.identifyUser(user: user) { error, variables in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: variables)
}
self._identifyUser(user: user, currentUser: currentUser) { error, variables in
if let error = error {
Log.error(
"Error calling identifyUser for user_id \(String(describing: user.userId))",
tags: ["identify"])
continuation.resume(throwing: error)
} else {
continuation.resume(returning: variables)
}
} catch {
Log.error(
"Error calling identifyUser for user_id \(String(describing: user.userId))",
tags: ["identify"])
continuation.resume(throwing: error)
}
}
}
Expand Down
89 changes: 86 additions & 3 deletions DevCycleTests/Models/DevCycleClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -431,13 +431,13 @@ class DevCycleClientTest: XCTestCase {
XCTAssertEqual(service.numberOfConfigCalls, 2)

let user2 = try! DevCycleUser.builder().userId("user2").build()
try! client.identifyUser(user: user2)
try! client.identifyUser(user: user2, callback: nil)
XCTAssertEqual(client.lastIdentifiedUser?.userId, user2.userId)
client.refetchConfig(sse: true, lastModified: 456, etag: "etag")
XCTAssertEqual(service.numberOfConfigCalls, 4)

let user3 = try! DevCycleUser.builder().userId("user3").build()
try! client.identifyUser(user: user3)
try! client.identifyUser(user: user3, callback: nil)
XCTAssertEqual(client.lastIdentifiedUser?.userId, user3.userId)
client.refetchConfig(sse: true, lastModified: 789, etag: "etag")
XCTAssertEqual(service.numberOfConfigCalls, 6)
Expand Down Expand Up @@ -698,7 +698,7 @@ class DevCycleClientTest: XCTestCase {
do {
let user = try DevCycleUser.builder().userId("user1").build()

try client.identifyUser(user: user)
try client.identifyUser(user: user, callback: nil)
try client.resetUser()

client.track(
Expand Down Expand Up @@ -768,6 +768,89 @@ class DevCycleClientTest: XCTestCase {
wait(for: [expectation], timeout: 100.0)
client.close(callback: nil)
}

func testIdentifyUserWithInvalidUserCallsCallbackWithError() {
let expectation = XCTestExpectation(
description: "identifyUser with invalid user should call callback with error")

let client = try! self.builder.user(self.user).sdkKey("my_sdk_key").build(
onInitialized: nil)
client.config?.userConfig = self.userConfig

// Test with empty user ID
let emptyUser = DevCycleUser(userId: "", isAnonymous: false)

client.identifyUser(
user: emptyUser,
callback: { error, variables in
XCTAssertNotNil(error, "identifyUser should return error for empty user ID")
XCTAssertNil(variables, "identifyUser should return nil variables for invalid user")

if let clientError = error as? ClientError {
XCTAssertEqual(
clientError, ClientError.InvalidUser, "Error should be InvalidUser")
} else {
XCTFail("Error should be of type ClientError.InvalidUser")
}

expectation.fulfill()
})

wait(for: [expectation], timeout: 1.0)
client.close(callback: nil)
}

func testIdentifyUserWithCallbackSuccess() {
let expectation = XCTestExpectation(
description: "identifyUser with callback should succeed")

let client = try! self.builder.user(self.user).sdkKey("my_sdk_key").build(
onInitialized: nil)
client.config?.userConfig = self.userConfig

let newUser = try! DevCycleUser.builder().userId("new_user").build()

client.identifyUser(
user: newUser,
callback: { error, variables in
XCTAssertNil(error, "identifyUser should not return error for valid user")
// Variables can be nil or non-nil depending on the config
// The important thing is that no error occurred
expectation.fulfill()
})

wait(for: [expectation], timeout: 1.0)
client.close(callback: nil)
}

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
func testAsyncIdentifyUserWithInvalidUserCallsCallbackWithError() async throws {
let expectation = XCTestExpectation(
description: "identifyUser with invalid user should call callback with error")

let client = try! self.builder.user(self.user).sdkKey("my_sdk_key").build(
onInitialized: nil)
client.config?.userConfig = self.userConfig

// Test with empty user ID
let emptyUser = DevCycleUser(userId: "", isAnonymous: false)

do {
let _ = try await client.identifyUser(user: emptyUser)
XCTFail("identifyUser should throw error for empty user ID")
} catch {
XCTAssertNotNil(error, "identifyUser should return error for empty user ID")
if let clientError = error as? ClientError {
XCTAssertEqual(
clientError, ClientError.InvalidUser, "Error should be InvalidUser")
} else {
XCTFail("Error should be of type ClientError.InvalidUser")
}

expectation.fulfill()
client.close(callback: nil)
}
}
}

extension DevCycleClientTest {
Expand Down
Loading