Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow set person properties after identify #249

Merged
merged 10 commits into from
Nov 13, 2024
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Next

- fix: allow changing person properties after identify ([#249](https://github.com/PostHog/posthog-ios/pull/249))

## 3.15.0 - 2024-11-11

- add autocapture support for UIKit ([#224](https://github.com/PostHog/posthog-ios/pull/224))
Expand Down
56 changes: 35 additions & 21 deletions PostHog/PostHogSDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -468,30 +468,44 @@ let maxRetryDelay = 30.0

let isIdentified = storageManager.isIdentified()

if distinctId != oldDistinctId, !isIdentified {
// We keep the AnonymousId to be used by decide calls and identify to link the previousId
storageManager.setAnonymousId(oldDistinctId)
storageManager.setDistinctId(distinctId)

storageManager.setIdentified(true)

let properties = buildProperties(distinctId: distinctId, properties: [
"distinct_id": distinctId,
"$anon_distinct_id": oldDistinctId,
], userProperties: sanitizeDicionary(userProperties), userPropertiesSetOnce: sanitizeDicionary(userPropertiesSetOnce))
let sanitizedProperties = sanitizeProperties(properties)

queue.add(PostHogEvent(
event: "$identify",
distinctId: distinctId,
properties: sanitizedProperties
))
// if identified -> distinctId == oldDistinctId
// if !identified -> distinctId != oldDistinctId
if (distinctId == oldDistinctId) == isIdentified {
ioannisj marked this conversation as resolved.
Show resolved Hide resolved
if !isIdentified {
// We keep the AnonymousId to be used by decide calls and identify to link the previousId
storageManager.setAnonymousId(oldDistinctId)
storageManager.setDistinctId(distinctId)

storageManager.setIdentified(true)

let properties = buildProperties(distinctId: distinctId, properties: [
"distinct_id": distinctId,
"$anon_distinct_id": oldDistinctId,
], userProperties: sanitizeDicionary(userProperties), userPropertiesSetOnce: sanitizeDicionary(userPropertiesSetOnce))
let sanitizedProperties = sanitizeProperties(properties)

queue.add(PostHogEvent(
event: "$identify",
distinctId: distinctId,
properties: sanitizedProperties
))

if shouldReloadFlagsForTesting {
reloadFeatureFlags()
}
} else if !(userProperties?.isEmpty ?? true) || !(userPropertiesSetOnce?.isEmpty ?? true) {
capture("$set",
distinctId: distinctId,
userProperties: userProperties,
userPropertiesSetOnce: userPropertiesSetOnce)

if shouldReloadFlagsForTesting {
reloadFeatureFlags()
// Note we don't reload flags on property changes as these get processed async
} else {
hedgeLog("already identified with id: \(oldDistinctId)")
}

} else {
hedgeLog("already identified with id: \(oldDistinctId)")
hedgeLog("identified with a different id: \(oldDistinctId). Consider calling reset() before identifying as a new user")
}
}

Expand Down
99 changes: 90 additions & 9 deletions PostHogTests/PostHogSDKTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,43 @@ class PostHogSDKTest: QuickSpec {
}

it("does not capture identify event if already identified") {
let sut = self.getSut()
let sut = self.getSut(
flushAt: 2
)

sut.identify("distinctId",
userProperties: ["userProp": "value"],
userPropertiesSetOnce: ["userPropOnce": "value"])

sut.identify("distinctId")
sut.capture("satisfy_queue")

let events = getBatchedEvents(server)

expect(events.count) == 2

expect(events[0].event) == "$identify"
expect(events[1].event) == "satisfy_queue"

expect(events[0].distinctId) == "distinctId"
let anonId = sut.getAnonymousId()
expect(events[0].properties["$anon_distinct_id"] as? String) == anonId
expect(events[0].properties["$is_identified"] as? Bool) == true

let set = events[0].properties["$set"] as? [String: Any] ?? [:]
expect(set["userProp"] as? String) == "value"

let setOnce = events[0].properties["$set_once"] as? [String: Any] ?? [:]
expect(setOnce["userPropOnce"] as? String) == "value"

sut.reset()
sut.close()
}

it("updates user props if already identified but user properties are set") {
let sut = self.getSut(
flushAt: 2
)

sut.identify("distinctId",
userProperties: ["userProp": "value"],
Expand All @@ -168,20 +204,65 @@ class PostHogSDKTest: QuickSpec {

let events = getBatchedEvents(server)

expect(events.count) == 1
expect(events.count) == 2

let event = events.first!
expect(event.event) == "$identify"
expect(events[0].event) == "$identify"
expect(events[1].event) == "$set"

expect(events[0].distinctId) == "distinctId"
expect(events[1].distinctId) == events[0].distinctId

expect(event.distinctId) == "distinctId"
let anonId = sut.getAnonymousId()
expect(event.properties["$anon_distinct_id"] as? String) == anonId
expect(event.properties["$is_identified"] as? Bool) == true
expect(events[0].properties["$anon_distinct_id"] as? String) == anonId
expect(events[0].properties["$is_identified"] as? Bool) == true

let set = event.properties["$set"] as? [String: Any] ?? [:]
let set0 = events[0].properties["$set"] as? [String: Any] ?? [:]
expect(set0["userProp"] as? String) == "value"

let set1 = events[1].properties["$set"] as? [String: Any] ?? [:]
expect(set1["userProp2"] as? String) == "value2"

let setOnce0 = events[0].properties["$set_once"] as? [String: Any] ?? [:]
expect(setOnce0["userPropOnce"] as? String) == "value"

let setOnce1 = events[1].properties["$set_once"] as? [String: Any] ?? [:]
expect(setOnce1["userPropOnce2"] as? String) == "value2"

sut.reset()
sut.close()
}

it("does not capture user props for another distinctId even if user properties are set") {
let sut = self.getSut(
flushAt: 2
)

sut.identify("distinctId",
userProperties: ["userProp": "value"],
userPropertiesSetOnce: ["userPropOnce": "value"])

sut.identify("distinctId2",
userProperties: ["userProp2": "value2"],
userPropertiesSetOnce: ["userPropOnce2": "value2"])

sut.capture("satisfy_queue")

let events = getBatchedEvents(server)

expect(events.count) == 2

expect(events[0].event) == "$identify"
expect(events[1].event) == "satisfy_queue"

expect(events[0].distinctId) == "distinctId"
let anonId = sut.getAnonymousId()
expect(events[0].properties["$anon_distinct_id"] as? String) == anonId
expect(events[0].properties["$is_identified"] as? Bool) == true

let set = events[0].properties["$set"] as? [String: Any] ?? [:]
expect(set["userProp"] as? String) == "value"

let setOnce = event.properties["$set_once"] as? [String: Any] ?? [:]
let setOnce = events[0].properties["$set_once"] as? [String: Any] ?? [:]
expect(setOnce["userPropOnce"] as? String) == "value"

sut.reset()
Expand Down