diff --git a/Riot/Managers/Analytics/Analytics.swift b/Riot/Managers/Analytics/Analytics.swift index 00135edf4..2fb8ba079 100644 --- a/Riot/Managers/Analytics/Analytics.swift +++ b/Riot/Managers/Analytics/Analytics.swift @@ -27,7 +27,7 @@ import AnalyticsEvents static let shared = Analytics() /// The analytics client to send events with. - private var client = PostHogAnalyticsClient() + private var client: AnalyticsClientProtocol = PostHogAnalyticsClient() /// Whether or not the object is enabled and sending events to the server. var isRunning: Bool { client.isRunning } @@ -46,34 +46,25 @@ import AnalyticsEvents // MARK: - Public /// Opts in to analytics tracking with the supplied session. - /// - Parameter session: The session to use to when reading/generating the analytics ID. + /// - Parameter session: An optional session to use to when reading/generating the analytics ID. + /// The session will be ignored if not running. func optIn(with session: MXSession?) { - guard let session = session else { return } RiotSettings.shared.enableAnalytics = true - - var settings = AnalyticsSettings(session: session) - - if settings.id == nil { - settings.generateID() - - session.setAccountData(settings.dictionary, forType: AnalyticsSettings.eventType) { - MXLog.debug("[Analytics] Successfully updated analytics settings in account data.") - } failure: { error in - MXLog.error("[Analytics] Failed to update analytics settings.") - } - } - startIfEnabled() - if !RiotSettings.shared.isIdentifiedForAnalytics { - identify(with: settings) - } + guard let session = session else { return } + useAnalyticsSettings(from: session) } - /// Opts out of analytics tracking and calls `reset` to clear any IDs and event queues. + /// Stops analytics tracking and calls `reset` to clear any IDs and event queues. func optOut() { RiotSettings.shared.enableAnalytics = false + + // The order is important here. PostHog ignores the reset if stopped. reset() + client.stop() + + MXLog.debug("[Analytics] Stopped.") } /// Starts the analytics client if the user has opted in, otherwise does nothing. @@ -92,14 +83,39 @@ import AnalyticsEvents MXLogger.setBuildVersion(AppDelegate.theDelegate().build) } - /// Resets the any IDs and event queues in the analytics client. This method - /// can be called on sign-out to remember opt-in status, but ensure the next - /// account used isn't associated with the previous one. - func reset() { - guard isRunning else { return } + /// Use the analytics settings from the supplied session to configure analytics. + /// For now this is only used for (pseudonymous) identification. + /// - Parameter session: The session to read analytics settings from. + func useAnalyticsSettings(from session: MXSession) { + guard + RiotSettings.shared.enableAnalytics, + !RiotSettings.shared.isIdentifiedForAnalytics, + session.state == .running // Only use the session if it is running otherwise we could wipe out an existing analytics ID. + else { return } + var settings = AnalyticsSettings(session: session) + + if settings.id == nil { + settings.generateID() + + session.setAccountData(settings.dictionary, forType: AnalyticsSettings.eventType) { + MXLog.debug("[Analytics] Successfully updated analytics settings in account data.") + self.identify(with: settings) + } failure: { error in + MXLog.error("[Analytics] Failed to update analytics settings.") + } + } else { + self.identify(with: settings) + } + } + + /// Resets the any IDs and event queues in the analytics client. This method should + /// be called on sign-out to maintain opt-in status, whilst ensuring the next + /// account used isn't associated with the previous one. + /// Note: **MUST** be called before stopping PostHog or the reset is ignored. + func reset() { client.reset() - MXLog.debug("[Analytics] Stopped and reset.") + MXLog.debug("[Analytics] Reset.") RiotSettings.shared.isIdentifiedForAnalytics = false // Stop collecting crash logs diff --git a/Riot/Managers/Analytics/AnalyticsClientProtocol.swift b/Riot/Managers/Analytics/AnalyticsClientProtocol.swift index 7ee0ae429..af07a58fb 100644 --- a/Riot/Managers/Analytics/AnalyticsClientProtocol.swift +++ b/Riot/Managers/Analytics/AnalyticsClientProtocol.swift @@ -28,9 +28,13 @@ protocol AnalyticsClientProtocol { /// - Parameter id: The ID to associate with the user. func identify(id: String) - /// Stop the analytics client reporting data and reset all stored properties and events. + /// Reset all stored properties and any event queues on the client. Note that + /// the client will remain active, but in a fresh unidentified state. func reset() + /// Stop the analytics client reporting data. + func stop() + /// Send any queued events immediately. func flush() diff --git a/Riot/Managers/Analytics/PostHogAnalyticsClient.swift b/Riot/Managers/Analytics/PostHogAnalyticsClient.swift index 0552a459c..1c7172112 100644 --- a/Riot/Managers/Analytics/PostHogAnalyticsClient.swift +++ b/Riot/Managers/Analytics/PostHogAnalyticsClient.swift @@ -40,8 +40,11 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol { } func reset() { - postHog?.disable() postHog?.reset() + } + + func stop() { + postHog?.disable() // As of PostHog 1.4.4, setting the client to nil here doesn't release // it. Keep it around to avoid having multiple instances if the user re-enables diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index 946f22b26..fd6354ad3 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -1884,6 +1884,11 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni [self.pushNotificationService checkPushKitPushersInSession:mxSession]; } + else if (mxSession.state == MXSessionStateRunning) + { + // Configure analytics from the session if necessary + [Analytics.shared useAnalyticsSettingsFrom:mxSession]; + } else if (mxSession.state == MXSessionStateClosed) { [self removeMatrixSession:mxSession];