Support for posthog super properties (#2774)

* Support for posthog super properties

* Use typed SuperProperties and add tests

* update superProperty in AppCoordinator
This commit is contained in:
Valere
2024-05-02 17:21:09 +02:00
committed by GitHub
parent 69c6e25f37
commit 65c7713f5b
7 changed files with 405 additions and 11 deletions

View File

@@ -461,6 +461,7 @@
6F26CBC84AE87EB4068D398B /* LRUCache in Frameworks */ = {isa = PBXBuildFile; productRef = 78B28D75FF7AF8E6146DEE2A /* LRUCache */; };
6F2AB43A1EFAD8A97AF41A15 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = 9C73F37731C9FDED1BB24C1C /* Collections */; };
6F2D5D4F2590310DFAE973E4 /* WaitingDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6D698BFD68B061350553930 /* WaitingDialog.swift */; };
6F86349BDEAF4495EAE38931 /* PHGPostHogMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2AD8A56CD37E23071A2F4BF /* PHGPostHogMock.swift */; };
6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */; };
6FD8053301C5FEFA82D2F246 /* URLComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BFDCA5A09EE70BC17F2EFA7 /* URLComponents.swift */; };
6FF51EB400DBA0668FC38B97 /* TimelineStartRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9ED8E731E21055F728E5FED /* TimelineStartRoomTimelineView.swift */; };
@@ -1036,6 +1037,7 @@
F777C6FEE7D106136E2ED2B2 /* MessageForwardingScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F6E6EDC4BBF962B2ED595A4 /* MessageForwardingScreenViewModelTests.swift */; };
F78BAD28482A467287A9A5A3 /* EventBasedMessageTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0900BBF0A5D5D775E917C70 /* EventBasedMessageTimelineItemProtocol.swift */; };
F7BC744FFA7FE248FAE7F570 /* UserIndicatorToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57C8022B8A871A1DCD1750A /* UserIndicatorToastView.swift */; };
F7D709D7ECABE46641BB8B6B /* PHGPostHogProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEEAE1BFAACD6C96B6DB731 /* PHGPostHogProtocol.swift */; };
F833D5B5BE6707F961FA88DB /* SecureBackupController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A1119E9C63AE530252640D2 /* SecureBackupController.swift */; };
F86102DC2C68BBBB0521BAAE /* SoftLogoutScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BB385E148DE55C85C0A02D6 /* SoftLogoutScreenModels.swift */; };
F8C87130FD999F7F1076208C /* RoomChangePermissionsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AAEA70CFF3284920811941 /* RoomChangePermissionsScreen.swift */; };
@@ -1497,6 +1499,7 @@
5AEA0B743847CFA5B3C38EE4 /* RoomMembersListScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenCoordinator.swift; sourceTree = "<group>"; };
5B8F0ED874DF8C9A51B0AB6F /* SettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenCoordinator.swift; sourceTree = "<group>"; };
5C7C7CFA6B2A62A685FF6CE3 /* DeveloperOptionsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenCoordinator.swift; sourceTree = "<group>"; };
5CEEAE1BFAACD6C96B6DB731 /* PHGPostHogProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogProtocol.swift; sourceTree = "<group>"; };
5D26A086A8278D39B5756D6F /* project.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = project.yml; sourceTree = "<group>"; };
5D82F234B3576BD6268C7950 /* ScaledFrameModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaledFrameModifier.swift; sourceTree = "<group>"; };
5D99730313BEBF08CDE81EE3 /* EmojiDetection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiDetection.swift; sourceTree = "<group>"; };
@@ -1810,6 +1813,7 @@
B172057567E049007A5C4D92 /* Strings+SAS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Strings+SAS.swift"; sourceTree = "<group>"; };
B1E227F34BE43B08E098796E /* TestablePreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestablePreview.swift; sourceTree = "<group>"; };
B251F5B4511D1CA0BA8361FE /* CoordinatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoordinatorProtocol.swift; sourceTree = "<group>"; };
B2AD8A56CD37E23071A2F4BF /* PHGPostHogMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogMock.swift; sourceTree = "<group>"; };
B2B1DC3B3FB40A7F4AE9B7BF /* RoomRolesAndPermissionsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomRolesAndPermissionsScreen.swift; sourceTree = "<group>"; };
B2B5EDCD05D50BA9B815C66C /* ImageRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRoomTimelineItemContent.swift; sourceTree = "<group>"; };
B2E7C987AE5DC9087BB19F7D /* MediaUploadPreviewScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenModels.swift; sourceTree = "<group>"; };
@@ -2673,6 +2677,7 @@
3BAC027034248429A438886B /* AppMediatorMock.swift */,
E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */,
382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */,
B2AD8A56CD37E23071A2F4BF /* PHGPostHogMock.swift */,
D38391154120264910D19528 /* PollMock.swift */,
90B4ED923603F6110D4960C5 /* QRCodeLoginServiceMock.swift */,
894EE8F5B399A165BA2A6634 /* RoomDirectorySearchMock.swift */,
@@ -3132,6 +3137,7 @@
57B6B383F1FD04CC0E7B60C6 /* AnalyticsConsentState.swift */,
5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */,
A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */,
5CEEAE1BFAACD6C96B6DB731 /* PHGPostHogProtocol.swift */,
1715E3D7F53C0748AA50C91C /* PostHogAnalyticsClient.swift */,
752A0EB49BF5BCEA37EDF7A3 /* Signposter.swift */,
3A304097A59704AC9B869EC6 /* Helpers */,
@@ -6206,6 +6212,8 @@
AA5924D3B67F7ACD98BBEFDC /* OrientationManagerProtocol.swift in Sources */,
804C15D8ADE0EA7A5268F58A /* OverridableAvatarImage.swift in Sources */,
CD6A72B65D3B6076F4045C30 /* PHGPostHogConfiguration.swift in Sources */,
6F86349BDEAF4495EAE38931 /* PHGPostHogMock.swift in Sources */,
F7D709D7ECABE46641BB8B6B /* PHGPostHogProtocol.swift in Sources */,
847DE3A7EB9FCA2C429C6E85 /* PINTextField.swift in Sources */,
7501442D52A65F73DF79FFD4 /* PaginationIndicatorRoomTimelineItem.swift in Sources */,
764AFCC225B044CF5F9B41E5 /* PaginationIndicatorRoomTimelineView.swift in Sources */,

View File

@@ -14,6 +14,7 @@
// limitations under the License.
//
import AnalyticsEvents
import BackgroundTasks
import Combine
import MatrixRustSDK
@@ -313,7 +314,9 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
applicationId: appSettings.bugReportApplicationId,
sdkGitSHA: sdkGitSha(),
maxUploadSize: appSettings.bugReportMaxUploadSize))
ServiceLocator.shared.register(analytics: AnalyticsService(client: PostHogAnalyticsClient(),
let posthogAnalyticsClient = PostHogAnalyticsClient()
posthogAnalyticsClient.updateSuperProperties(AnalyticsEvent.SuperProperties(appPlatform: nil, cryptoSDK: .Rust, cryptoSDKVersion: sdkGitSha()))
ServiceLocator.shared.register(analytics: AnalyticsService(client: posthogAnalyticsClient,
appSettings: appSettings,
bugReportService: ServiceLocator.shared.bugReportService))
}

View File

@@ -1,4 +1,4 @@
// Generated using Sourcery 2.2.2 https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 2.2.4 https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
// swiftlint:disable all
@@ -6585,6 +6585,197 @@ class OrientationManagerMock: OrientationManagerProtocol {
lockOrientationClosure?(orientation)
}
}
class PHGPostHogMock: PHGPostHogProtocol {
var enabled: Bool {
get { return underlyingEnabled }
set(value) { underlyingEnabled = value }
}
var underlyingEnabled: Bool!
//MARK: - enable
var enableUnderlyingCallsCount = 0
var enableCallsCount: Int {
get {
if Thread.isMainThread {
return enableUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = enableUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
enableUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
enableUnderlyingCallsCount = newValue
}
}
}
}
var enableCalled: Bool {
return enableCallsCount > 0
}
var enableClosure: (() -> Void)?
func enable() {
enableCallsCount += 1
enableClosure?()
}
//MARK: - disable
var disableUnderlyingCallsCount = 0
var disableCallsCount: Int {
get {
if Thread.isMainThread {
return disableUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = disableUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
disableUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
disableUnderlyingCallsCount = newValue
}
}
}
}
var disableCalled: Bool {
return disableCallsCount > 0
}
var disableClosure: (() -> Void)?
func disable() {
disableCallsCount += 1
disableClosure?()
}
//MARK: - reset
var resetUnderlyingCallsCount = 0
var resetCallsCount: Int {
get {
if Thread.isMainThread {
return resetUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = resetUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
resetUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
resetUnderlyingCallsCount = newValue
}
}
}
}
var resetCalled: Bool {
return resetCallsCount > 0
}
var resetClosure: (() -> Void)?
func reset() {
resetCallsCount += 1
resetClosure?()
}
//MARK: - capture
var capturePropertiesUnderlyingCallsCount = 0
var capturePropertiesCallsCount: Int {
get {
if Thread.isMainThread {
return capturePropertiesUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = capturePropertiesUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
capturePropertiesUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
capturePropertiesUnderlyingCallsCount = newValue
}
}
}
}
var capturePropertiesCalled: Bool {
return capturePropertiesCallsCount > 0
}
var capturePropertiesReceivedArguments: (event: String, properties: [String: Any]?)?
var capturePropertiesReceivedInvocations: [(event: String, properties: [String: Any]?)] = []
var capturePropertiesClosure: ((String, [String: Any]?) -> Void)?
func capture(_ event: String, properties: [String: Any]?) {
capturePropertiesCallsCount += 1
capturePropertiesReceivedArguments = (event: event, properties: properties)
capturePropertiesReceivedInvocations.append((event: event, properties: properties))
capturePropertiesClosure?(event, properties)
}
//MARK: - screen
var screenPropertiesUnderlyingCallsCount = 0
var screenPropertiesCallsCount: Int {
get {
if Thread.isMainThread {
return screenPropertiesUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = screenPropertiesUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
screenPropertiesUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
screenPropertiesUnderlyingCallsCount = newValue
}
}
}
}
var screenPropertiesCalled: Bool {
return screenPropertiesCallsCount > 0
}
var screenPropertiesReceivedArguments: (screenTitle: String, properties: [String: Any]?)?
var screenPropertiesReceivedInvocations: [(screenTitle: String, properties: [String: Any]?)] = []
var screenPropertiesClosure: ((String, [String: Any]?) -> Void)?
func screen(_ screenTitle: String, properties: [String: Any]?) {
screenPropertiesCallsCount += 1
screenPropertiesReceivedArguments = (screenTitle: screenTitle, properties: properties)
screenPropertiesReceivedInvocations.append((screenTitle: screenTitle, properties: properties))
screenPropertiesClosure?(screenTitle, properties)
}
}
class PollInteractionHandlerMock: PollInteractionHandlerProtocol {
//MARK: - sendPollResponse

View File

@@ -0,0 +1,38 @@
//
// Copyright 2024 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import PostHog
extension PHGPostHogMock {
func configureMockBehavior() {
enableClosure = {
self.enabled = true
}
}
}
class MockPostHogFactory: PostHogFactory {
var mock: PHGPostHogProtocol!
init(mock: PHGPostHogProtocol!) {
self.mock = mock
}
func createPostHog(config: PHGPostHogConfiguration) -> ElementX.PHGPostHogProtocol {
mock
}
}

View File

@@ -0,0 +1,45 @@
//
// Copyright 2024 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import PostHog
// sourcery: AutoMockable
protocol PHGPostHogProtocol {
var enabled: Bool { get }
func enable()
func disable()
func reset()
func capture(_ event: String, properties: [String: Any]?)
func screen(_ screenTitle: String, properties: [String: Any]?)
}
protocol PostHogFactory {
func createPostHog(config: PHGPostHogConfiguration) -> PHGPostHogProtocol
}
class DefaultPostHogFactory: PostHogFactory {
func createPostHog(config: PHGPostHogConfiguration) -> PHGPostHogProtocol {
PHGPostHog(configuration: config)
}
}
extension PHGPostHog: PHGPostHogProtocol { }

View File

@@ -19,12 +19,25 @@ import PostHog
/// An analytics client that reports events to a PostHog server.
class PostHogAnalyticsClient: AnalyticsClientProtocol {
private var posthogFactory: PostHogFactory = DefaultPostHogFactory()
init(posthogFactory: PostHogFactory? = nil) {
if let factory = posthogFactory {
self.posthogFactory = factory
}
}
/// The PHGPostHog object used to report events.
private var postHog: PHGPostHog?
private var postHog: PHGPostHogProtocol?
/// Any user properties to be included with the next captured event.
private(set) var pendingUserProperties: AnalyticsEvent.UserProperties?
/// Super Properties are properties associated with events that are set once and then sent with every capture call, be it a $screen, an autocaptured button click, or anything else.
/// It is different from user properties that will be attached to the user and not events.
/// Not persisted for now, should be set on start.
private var superProperties: AnalyticsEvent.SuperProperties?
var isRunning: Bool { postHog?.enabled ?? false }
func start(analyticsConfiguration: AnalyticsConfiguration) {
@@ -32,9 +45,11 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol {
guard let configuration = PHGPostHogConfiguration.standard(analyticsConfiguration: analyticsConfiguration) else { return }
if postHog == nil {
postHog = PHGPostHog(configuration: configuration)
postHog = posthogFactory.createPostHog(config: configuration)
}
// Add super property cryptoSDK to the captured events, to allow easy
// filtering of events across different client by using same filter.
superProperties = AnalyticsEvent.SuperProperties(appPlatform: nil, cryptoSDK: .Rust, cryptoSDKVersion: nil)
postHog?.enable()
}
@@ -52,12 +67,12 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol {
func capture(_ event: AnalyticsEventProtocol) {
guard isRunning else { return }
postHog?.capture(event.eventName, properties: attachUserProperties(to: event.properties))
postHog?.capture(event.eventName, properties: attachUserProperties(to: attachSuperProperties(to: event.properties)))
}
func screen(_ event: AnalyticsScreenProtocol) {
guard isRunning else { return }
postHog?.screen(event.screenName.rawValue, properties: attachUserProperties(to: event.properties))
postHog?.screen(event.screenName.rawValue, properties: attachUserProperties(to: attachSuperProperties(to: event.properties)))
}
func updateUserProperties(_ userProperties: AnalyticsEvent.UserProperties) {
@@ -73,6 +88,20 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol {
numSpaces: userProperties.numSpaces ?? pendingUserProperties.numSpaces)
}
func updateSuperProperties(_ updatedProperties: AnalyticsEvent.SuperProperties) {
guard let currentProperties = superProperties else {
superProperties = updatedProperties
return
}
superProperties = AnalyticsEvent.SuperProperties(appPlatform: updatedProperties.appPlatform ??
currentProperties.appPlatform,
cryptoSDK: updatedProperties.cryptoSDK ??
currentProperties.cryptoSDK,
cryptoSDKVersion: updatedProperties.cryptoSDKVersion ??
currentProperties.cryptoSDKVersion)
}
// MARK: - Private
/// Given a dictionary containing properties from an event, this method will return those properties
@@ -89,4 +118,19 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol {
pendingUserProperties = nil
return properties
}
/// Attach super properties to events.
/// 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] {
guard isRunning, let superProperties else { return properties }
var properties = properties
superProperties.properties.forEach { (key: String, value: Any) in
if properties[key] == nil {
properties[key] = value
}
}
return properties
}
}

View File

@@ -16,12 +16,14 @@
import AnalyticsEvents
@testable import ElementX
import PostHog
import XCTest
class AnalyticsTests: XCTestCase {
private var appSettings: AppSettings!
private var analyticsClient: AnalyticsClientMock!
private var bugReportService: BugReportServiceMock!
private var posthogMock: PHGPostHogMock!
override func setUp() {
AppSettings.resetAllSettings()
@@ -35,6 +37,9 @@ class AnalyticsTests: XCTestCase {
ServiceLocator.shared.register(analytics: AnalyticsService(client: analyticsClient,
appSettings: appSettings,
bugReportService: ServiceLocator.shared.bugReportService))
posthogMock = PHGPostHogMock()
posthogMock.configureMockBehavior()
}
override func tearDown() {
@@ -80,7 +85,7 @@ class AnalyticsTests: XCTestCase {
XCTAssertFalse(analyticsClient.startAnalyticsConfigurationCalled)
XCTAssertFalse(bugReportService.startCalled)
}
func testAnalyticsOptOut() {
// Given a fresh install of the app (without PostHog analytics having been set).
// When analytics is opt-out
@@ -95,7 +100,7 @@ class AnalyticsTests: XCTestCase {
XCTAssertTrue(analyticsClient.stopCalled)
XCTAssertTrue(bugReportService.stopCalled)
}
func testAnalyticsOptIn() {
// Given a fresh install of the app (without PostHog analytics having been set).
// When analytics is opt-in
@@ -107,7 +112,7 @@ class AnalyticsTests: XCTestCase {
XCTAssertTrue(analyticsClient.startAnalyticsConfigurationCalled)
XCTAssertTrue(bugReportService.startCalled)
}
func testAnalyticsStartIfNotEnabled() {
// Given an existing install of the app where the user previously declined the tracking
appSettings.analyticsConsentState = .optedOut
@@ -192,7 +197,7 @@ class AnalyticsTests: XCTestCase {
// Given an existing install of the app where the user previously accpeted the tracking
appSettings.analyticsConsentState = .optedIn
XCTAssertFalse(ServiceLocator.shared.analytics.shouldShowAnalyticsPrompt)
// When forgetting analytics consents
ServiceLocator.shared.analytics.resetConsentState()
@@ -200,4 +205,64 @@ class AnalyticsTests: XCTestCase {
XCTAssertEqual(appSettings.analyticsConsentState, .unknown)
XCTAssertTrue(ServiceLocator.shared.analytics.shouldShowAnalyticsPrompt)
}
func testSendingAndUpdatingSuperProperties() {
// Given a client with user properties set
let client = PostHogAnalyticsClient(posthogFactory: MockPostHogFactory(mock: posthogMock))
client.start(analyticsConfiguration: appSettings.analyticsConfiguration)
client.updateSuperProperties(
AnalyticsEvent.SuperProperties(appPlatform: "A thing",
cryptoSDK: .Rust,
cryptoSDKVersion: "000")
)
// 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 screenEvent = posthogMock.screenPropertiesReceivedArguments
XCTAssertEqual(screenEvent?.screenTitle, AnalyticsEvent.MobileScreen.ScreenName.Home.rawValue)
// All the super properties should have been added
XCTAssertEqual(screenEvent?.properties?["cryptoSDK"] as? String, AnalyticsEvent.SuperProperties.CryptoSDK.Rust.rawValue)
XCTAssertEqual(screenEvent?.properties?["appPlatform"] as? String, "A thing")
XCTAssertEqual(screenEvent?.properties?["cryptoSDKVersion"] as? String, "000")
// It should be the same for any event
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.capturePropertiesReceivedArguments
// All the super properties should have been added
XCTAssertEqual(capturedEvent?.properties?["cryptoSDK"] as? String, AnalyticsEvent.SuperProperties.CryptoSDK.Rust.rawValue)
XCTAssertEqual(capturedEvent?.properties?["appPlatform"] as? String, "A thing")
XCTAssertEqual(capturedEvent?.properties?["cryptoSDKVersion"] as? String, "000")
// Updating should keep the previously set properties
client.updateSuperProperties(
AnalyticsEvent.SuperProperties(appPlatform: nil,
cryptoSDK: nil,
cryptoSDKVersion: "001")
)
client.capture(someEvent)
let capturedEvent2 = posthogMock.capturePropertiesReceivedArguments
// 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?["appPlatform"] as? String, "A thing")
XCTAssertEqual(capturedEvent2?.properties?["cryptoSDKVersion"] as? String, "001")
}
}