mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Add an AnalyticsService to handle account data.
This commit is contained in:
parent
1df8514fcd
commit
919fd0ee3a
3 changed files with 89 additions and 25 deletions
|
@ -89,23 +89,17 @@ import AnalyticsEvents
|
|||
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.
|
||||
!RiotSettings.shared.isIdentifiedForAnalytics
|
||||
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.")
|
||||
let service = AnalyticsService(session: session)
|
||||
service.settings { result in
|
||||
switch result {
|
||||
case .success(let settings):
|
||||
self.identify(with: settings)
|
||||
} failure: { error in
|
||||
MXLog.error("[Analytics] Failed to update analytics settings.")
|
||||
case .failure:
|
||||
MXLog.error("[Analytics] Failed to use analytics settings. Will continue to run without analytics ID.")
|
||||
}
|
||||
} else {
|
||||
self.identify(with: settings)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,7 +129,7 @@ import AnalyticsEvents
|
|||
/// - Parameter settings: The settings to use for identification. The ID must be set *before* calling this method.
|
||||
private func identify(with settings: AnalyticsSettings) {
|
||||
guard let id = settings.id else {
|
||||
MXLog.warning("[Analytics] identify(with:) called before an ID has been generated.")
|
||||
MXLog.error("[Analytics] identify(with:) called before an ID has been generated.")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
72
Riot/Managers/Analytics/AnalyticsService.swift
Normal file
72
Riot/Managers/Analytics/AnalyticsService.swift
Normal file
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// Copyright 2021 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
|
||||
|
||||
enum AnalyticsServiceError: Error {
|
||||
/// The session supplied to the service does not have a state of `MXSessionStateRunning`.
|
||||
case sessionIsNotRunning
|
||||
/// An error occurred but the session did not report what it was.
|
||||
case unknown
|
||||
}
|
||||
|
||||
/// A service responsible for handling the `im.vector.analytics` event from the user's account data.
|
||||
class AnalyticsService {
|
||||
let session: MXSession
|
||||
|
||||
/// Creates an analytics service with the supplied session.
|
||||
/// - Parameter session: The session to use when reading analytics settings from account data.
|
||||
init(session: MXSession) {
|
||||
self.session = session
|
||||
}
|
||||
|
||||
/// The analytics settings for the current user. Calling this method will check whether the settings already
|
||||
/// contain an `id` property and if not, will add one to the account data before calling the completion.
|
||||
/// - Parameter completion: A completion handler that will be called when the request completes.
|
||||
///
|
||||
/// The request will fail if the service's session does not have the `MXSessionStateRunning` state.
|
||||
func settings(completion: @escaping (Result<AnalyticsSettings, Error>) -> Void) {
|
||||
// Only use the session if it is running otherwise we could wipe out an existing analytics ID.
|
||||
guard session.state == .running else {
|
||||
MXLog.warning("[AnalyticsService] Aborting attempt to read analytics settings. The session may not be up-to-date.")
|
||||
completion(.failure(AnalyticsServiceError.sessionIsNotRunning))
|
||||
return
|
||||
}
|
||||
|
||||
let settings = AnalyticsSettings(accountData: session.accountData)
|
||||
|
||||
// The id has already be set so we are done here.
|
||||
if settings.id != nil {
|
||||
completion(.success(settings))
|
||||
return
|
||||
}
|
||||
|
||||
// Create a new ID and modify the event dictionary.
|
||||
let id = UUID().uuidString
|
||||
|
||||
var eventDictionary = settings.dictionary
|
||||
eventDictionary[AnalyticsSettings.Constants.idKey] = id
|
||||
|
||||
session.setAccountData(eventDictionary, forType: AnalyticsSettings.eventType) {
|
||||
MXLog.debug("[AnalyticsService] Successfully updated analytics settings in account data.")
|
||||
let settings = AnalyticsSettings(accountData: self.session.accountData)
|
||||
completion(.success(settings))
|
||||
} failure: { error in
|
||||
MXLog.warning("[AnalyticsService] Failed to update analytics settings.")
|
||||
completion(.failure(error ?? AnalyticsServiceError.unknown))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,17 +16,18 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
/// An analytics settings event from the user's account data.
|
||||
struct AnalyticsSettings {
|
||||
static let eventType = "im.vector.analytics"
|
||||
|
||||
private enum Constants {
|
||||
enum Constants {
|
||||
static let idKey = "id"
|
||||
static let webOptInKey = "pseudonymousAnalyticsOptIn"
|
||||
}
|
||||
|
||||
/// A randomly generated analytics token for this user.
|
||||
/// This is suggested to be a 128-bit hex encoded string.
|
||||
private(set) var id: String?
|
||||
/// This is suggested to be a UUID string.
|
||||
let id: String?
|
||||
|
||||
/// Whether the user has opted in on web or not. This is unused on iOS but necessary
|
||||
/// to store here so that it's value is preserved when updating the account data if we
|
||||
|
@ -34,12 +35,6 @@ struct AnalyticsSettings {
|
|||
///
|
||||
/// `true` if opted in on web, `false` if opted out on web and `nil` if the web prompt is not yet seen.
|
||||
private let webOptIn: Bool?
|
||||
|
||||
/// Generate a new random analytics ID. This method has no effect if an ID already exists.
|
||||
mutating func generateID() {
|
||||
guard id == nil else { return }
|
||||
id = UUID().uuidString
|
||||
}
|
||||
}
|
||||
|
||||
extension AnalyticsSettings {
|
||||
|
@ -49,6 +44,7 @@ extension AnalyticsSettings {
|
|||
self.webOptIn = dictionary?[Constants.webOptInKey] as? Bool
|
||||
}
|
||||
|
||||
/// A dictionary representation of the settings.
|
||||
var dictionary: Dictionary<AnyHashable, Any> {
|
||||
var dictionary = [AnyHashable: Any]()
|
||||
dictionary[Constants.idKey] = id
|
||||
|
@ -61,7 +57,9 @@ extension AnalyticsSettings {
|
|||
// MARK: - Public initializer
|
||||
|
||||
extension AnalyticsSettings {
|
||||
init(session: MXSession) {
|
||||
self.init(dictionary: session.accountData.accountData(forEventType: AnalyticsSettings.eventType))
|
||||
/// Create the analytics settings from account data.
|
||||
/// - Parameter accountData: The account data to read the event from.
|
||||
init(accountData: MXAccountData) {
|
||||
self.init(dictionary: accountData.accountData(forEventType: AnalyticsSettings.eventType))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue