Posthog: use correct API for user properties (#2793)

This commit is contained in:
Valere 2024-05-07 12:08:56 +02:00 committed by GitHub
parent 6ca92c272a
commit 0eb1fb257c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 45 additions and 41 deletions

View File

@ -6733,15 +6733,15 @@ class PHGPostHogMock: PHGPostHogProtocol {
} }
//MARK: - capture //MARK: - capture
var capturePropertiesUnderlyingCallsCount = 0 var capturePropertiesUserPropertiesUnderlyingCallsCount = 0
var capturePropertiesCallsCount: Int { var capturePropertiesUserPropertiesCallsCount: Int {
get { get {
if Thread.isMainThread { if Thread.isMainThread {
return capturePropertiesUnderlyingCallsCount return capturePropertiesUserPropertiesUnderlyingCallsCount
} else { } else {
var returnValue: Int? = nil var returnValue: Int? = nil
DispatchQueue.main.sync { DispatchQueue.main.sync {
returnValue = capturePropertiesUnderlyingCallsCount returnValue = capturePropertiesUserPropertiesUnderlyingCallsCount
} }
return returnValue! return returnValue!
@ -6749,26 +6749,26 @@ class PHGPostHogMock: PHGPostHogProtocol {
} }
set { set {
if Thread.isMainThread { if Thread.isMainThread {
capturePropertiesUnderlyingCallsCount = newValue capturePropertiesUserPropertiesUnderlyingCallsCount = newValue
} else { } else {
DispatchQueue.main.sync { DispatchQueue.main.sync {
capturePropertiesUnderlyingCallsCount = newValue capturePropertiesUserPropertiesUnderlyingCallsCount = newValue
} }
} }
} }
} }
var capturePropertiesCalled: Bool { var capturePropertiesUserPropertiesCalled: Bool {
return capturePropertiesCallsCount > 0 return capturePropertiesUserPropertiesCallsCount > 0
} }
var capturePropertiesReceivedArguments: (event: String, properties: [String: Any]?)? var capturePropertiesUserPropertiesReceivedArguments: (event: String, properties: [String: Any]?, userProperties: [String: Any]?)?
var capturePropertiesReceivedInvocations: [(event: String, properties: [String: Any]?)] = [] var capturePropertiesUserPropertiesReceivedInvocations: [(event: String, properties: [String: Any]?, userProperties: [String: Any]?)] = []
var capturePropertiesClosure: ((String, [String: Any]?) -> Void)? var capturePropertiesUserPropertiesClosure: ((String, [String: Any]?, [String: Any]?) -> Void)?
func capture(_ event: String, properties: [String: Any]?) { func capture(_ event: String, properties: [String: Any]?, userProperties: [String: Any]?) {
capturePropertiesCallsCount += 1 capturePropertiesUserPropertiesCallsCount += 1
capturePropertiesReceivedArguments = (event: event, properties: properties) capturePropertiesUserPropertiesReceivedArguments = (event: event, properties: properties, userProperties: userProperties)
capturePropertiesReceivedInvocations.append((event: event, properties: properties)) capturePropertiesUserPropertiesReceivedInvocations.append((event: event, properties: properties, userProperties: userProperties))
capturePropertiesClosure?(event, properties) capturePropertiesUserPropertiesClosure?(event, properties, userProperties)
} }
//MARK: - screen //MARK: - screen

View File

@ -25,7 +25,7 @@ protocol PHGPostHogProtocol {
func reset() func reset()
func capture(_ event: String, properties: [String: Any]?) func capture(_ event: String, properties: [String: Any]?, userProperties: [String: Any]?)
func screen(_ screenTitle: String, properties: [String: Any]?) func screen(_ screenTitle: String, properties: [String: Any]?)
} }

View File

@ -69,12 +69,13 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol {
func capture(_ event: AnalyticsEventProtocol) { func capture(_ event: AnalyticsEventProtocol) {
guard isRunning else { return } guard isRunning else { return }
postHog?.capture(event.eventName, properties: attachUserProperties(to: attachSuperProperties(to: event.properties))) postHog?.capture(event.eventName, properties: attachSuperProperties(to: event.properties), userProperties: pendingUserProperties?.properties.compactMapValues { $0 })
pendingUserProperties = nil
} }
func screen(_ event: AnalyticsScreenProtocol) { func screen(_ event: AnalyticsScreenProtocol) {
guard isRunning else { return } guard isRunning else { return }
postHog?.screen(event.screenName.rawValue, properties: attachUserProperties(to: attachSuperProperties(to: event.properties))) postHog?.screen(event.screenName.rawValue, properties: attachSuperProperties(to: event.properties))
} }
func updateUserProperties(_ userProperties: AnalyticsEvent.UserProperties) { func updateUserProperties(_ userProperties: AnalyticsEvent.UserProperties) {
@ -106,21 +107,6 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol {
// MARK: - Private // MARK: - Private
/// Given a dictionary containing properties from an event, this method will return those properties
/// with any pending user properties included under the `$set` key.
/// - Parameter properties: A dictionary of properties from an event.
/// - Returns: The `properties` dictionary with any user properties included.
private func attachUserProperties(to properties: [String: Any]) -> [String: Any] {
guard isRunning, let userProperties = pendingUserProperties else { return properties }
var properties = properties
// As user properties overwrite old ones via $set, compactMap the dictionary to avoid resetting any missing properties
properties["$set"] = userProperties.properties.compactMapValues { $0 }
pendingUserProperties = nil
return properties
}
/// Attach super properties to events. /// Attach super properties to events.
/// If the property is already set on the event, the already set value will be kept. /// If the property is already set on the event, the already set value will be kept.
private func attachSuperProperties(to properties: [String: Any]) -> [String: Any] { private func attachSuperProperties(to properties: [String: Any]) -> [String: Any] {

View File

@ -177,17 +177,35 @@ class AnalyticsTests: XCTestCase {
func testSendingUserProperties() { func testSendingUserProperties() {
// Given a client with user properties set // Given a client with user properties set
let client = PostHogAnalyticsClient()
let client = PostHogAnalyticsClient(posthogFactory: MockPostHogFactory(mock: posthogMock))
client.start(analyticsConfiguration: appSettings.analyticsConfiguration)
client.updateUserProperties(AnalyticsEvent.UserProperties(allChatsActiveFilter: nil, ftueUseCaseSelection: .PersonalMessaging, client.updateUserProperties(AnalyticsEvent.UserProperties(allChatsActiveFilter: nil, ftueUseCaseSelection: .PersonalMessaging,
numFavouriteRooms: nil, numFavouriteRooms: nil,
numSpaces: nil)) numSpaces: nil))
client.start(analyticsConfiguration: appSettings.analyticsConfiguration)
XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.") XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.")
XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection should match.") XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection should match.")
// When sending an event (tests run under Debug configuration so this is sent to the development instance) // When sending an event (tests run under Debug configuration so this is sent to the development instance)
client.screen(AnalyticsEvent.MobileScreen(durationMs: nil, screenName: .Home)) let someEvent = AnalyticsEvent.Error(context: nil,
cryptoModule: .Rust,
cryptoSDK: .Rust,
domain: .E2EE,
eventLocalAgeMillis: nil,
isFederated: nil,
isMatrixDotOrg: nil,
name: .OlmKeysNotSentError,
timeToDecryptMillis: nil,
userTrustsOwnIdentity: nil,
wasVisibleToUser: nil)
client.capture(someEvent)
let capturedEvent = posthogMock.capturePropertiesUserPropertiesReceivedArguments
// The user properties should have been added
XCTAssertEqual(capturedEvent?.userProperties?["ftueUseCaseSelection"] as? String, AnalyticsEvent.UserProperties.FtueUseCaseSelection.PersonalMessaging.rawValue)
// Then the properties should be cleared // Then the properties should be cleared
XCTAssertNil(client.pendingUserProperties, "The user properties should be cleared.") XCTAssertNil(client.pendingUserProperties, "The user properties should be cleared.")
@ -243,7 +261,7 @@ class AnalyticsTests: XCTestCase {
wasVisibleToUser: nil) wasVisibleToUser: nil)
client.capture(someEvent) client.capture(someEvent)
let capturedEvent = posthogMock.capturePropertiesReceivedArguments let capturedEvent = posthogMock.capturePropertiesUserPropertiesReceivedArguments
// All the super properties should have been added // All the super properties should have been added
XCTAssertEqual(capturedEvent?.properties?["cryptoSDK"] as? String, AnalyticsEvent.SuperProperties.CryptoSDK.Rust.rawValue) XCTAssertEqual(capturedEvent?.properties?["cryptoSDK"] as? String, AnalyticsEvent.SuperProperties.CryptoSDK.Rust.rawValue)
@ -258,7 +276,7 @@ class AnalyticsTests: XCTestCase {
) )
client.capture(someEvent) client.capture(someEvent)
let capturedEvent2 = posthogMock.capturePropertiesReceivedArguments let capturedEvent2 = posthogMock.capturePropertiesUserPropertiesReceivedArguments
// All the super properties should have been added, with the one udpated // All the super properties should have been added, with the one udpated
XCTAssertEqual(capturedEvent2?.properties?["cryptoSDK"] as? String, AnalyticsEvent.SuperProperties.CryptoSDK.Rust.rawValue) XCTAssertEqual(capturedEvent2?.properties?["cryptoSDK"] as? String, AnalyticsEvent.SuperProperties.CryptoSDK.Rust.rawValue)
@ -290,13 +308,13 @@ class AnalyticsTests: XCTestCase {
wasVisibleToUser: nil) wasVisibleToUser: nil)
client.capture(someEvent) client.capture(someEvent)
XCTAssertEqual(posthogMock.capturePropertiesCalled, false) XCTAssertEqual(posthogMock.capturePropertiesUserPropertiesCalled, false)
// start now // start now
client.start(analyticsConfiguration: appSettings.analyticsConfiguration) client.start(analyticsConfiguration: appSettings.analyticsConfiguration)
XCTAssertEqual(posthogMock.optInCalled, true) XCTAssertEqual(posthogMock.optInCalled, true)
client.capture(someEvent) client.capture(someEvent)
XCTAssertEqual(posthogMock.capturePropertiesCalled, true) XCTAssertEqual(posthogMock.capturePropertiesUserPropertiesCalled, true)
} }
} }