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: user is not asked to enable analytics after login - WPB-11332 #2032

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions wire-ios/Wire-iOS Tests/ExtensionSettingsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,14 @@ final class ExtensionSettingsTests: XCTestCase {
super.tearDown()
}

func testThatItDisablesAnalyticsReportByDefault() {
XCTAssertTrue(settings.disableAnalyticsSharing)
}

func testThatItHandlesAnalyticsPreferenceChange() {
XCTAssertTrue(settings.disableAnalyticsSharing)
XCTAssertNil(settings.disableAnalyticsSharing)

settings.disableAnalyticsSharing = false
XCTAssertFalse(settings.disableAnalyticsSharing)
XCTAssertEqual(settings.disableAnalyticsSharing, false)

settings.disableAnalyticsSharing = true
XCTAssertTrue(settings.disableAnalyticsSharing)
XCTAssertEqual(settings.disableAnalyticsSharing, true)
samwyndham marked this conversation as resolved.
Show resolved Hide resolved
}

func testThatItEnablesLinkPreviewsByDefault() {
Expand Down
19 changes: 15 additions & 4 deletions wire-ios/Wire-iOS Tests/Settings/SettingsPropertyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,23 @@ final class ZMMockAVSMediaManager: AVSMediaManagerInterface {
}

final class ZMMockTracking: TrackingInterface {
func disableAnalyticsSharing(isDisabled: Bool, resultHandler: @escaping (Result<Void, any Error>) -> Void) {
// no - op
}

var disableAnalyticsSharing: Bool = true
var isAnalyticsDisabled: Bool = true
var disableCrashAndAnalyticsSharing: Bool = false

func requestAnalyticsConsent() async throws -> Bool {
// no op
return false
}

func disableAnalytics() throws {
// no op
}

func enableAnalytics() async throws {
// no op
}

}

final class SettingsPropertyTests: XCTestCase {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,75 +23,60 @@ import WireSyncEngine

final class TrackingManager: NSObject, TrackingInterface {

private let flowManagerObserver: NSObjectProtocol
private let sessionManager: SessionManager
private var observerToken: NSObjectProtocol?

init(sessionManager: SessionManager) {
self.sessionManager = sessionManager

AVSFlowManager.getInstance()?.setEnableMetrics(!ExtensionSettings.shared.disableAnalyticsSharing)

flowManagerObserver = NotificationCenter.default.addObserver(
super.init()
AVSFlowManager.getInstance()?.setEnableMetrics(!isAnalyticsDisabled)
observerToken = NotificationCenter.default.addObserver(
forName: FlowManager.AVSFlowManagerCreatedNotification,
object: nil,
queue: OperationQueue.main,
using: { _ in
AVSFlowManager.getInstance()?.setEnableMetrics(!ExtensionSettings.shared.disableAnalyticsSharing)
using: { [weak self] _ in
guard let self else { return }
AVSFlowManager.getInstance()?.setEnableMetrics(!self.isAnalyticsDisabled)
}
)
}

super.init()
var doesUserConsentPreferenceExist: Bool {
ExtensionSettings.shared.disableAnalyticsSharing != nil
}

var disableAnalyticsSharing: Bool {
ExtensionSettings.shared.disableAnalyticsSharing
var isAnalyticsDisabled: Bool {
ExtensionSettings.shared.disableAnalyticsSharing ?? true
}

func disableAnalyticsSharing(
isDisabled: Bool,
resultHandler: @escaping (Result<Void, any Error>) -> Void
) {
Task { @MainActor in
if isDisabled {
await self.updateAnalyticsSharing(disabled: true)
resultHandler(.success(()))
} else {
// User wants to enable.
do {
let isConsentGiven = try await self.requestAnalyticsConsent()
@MainActor
func firstTimeRequestToEnableAnalytics() async throws {
// Only ask if user has not given a preference yet.
guard !doesUserConsentPreferenceExist else {
return
}

if isConsentGiven {
await self.updateAnalyticsSharing(disabled: false)
resultHandler(.success(()))
} else {
// User rejected, keep analytics disabled.
await self.updateAnalyticsSharing(disabled: true)
resultHandler(.failure(TrackingManagerError.userConsentDenied))
}
} catch {
WireLogger.analytics.error("failed to enable analytics: \(error)")
resultHandler(.failure(error))
}
}
WireLogger.analytics.debug("requesting first time analytics content")
let didConsent = try await requestAnalyticsConsent()
WireLogger.analytics.debug("user did consent: \(didConsent)")

if didConsent {
try await enableAnalytics()
} else {
try disableAnalytics()
}
}

private func updateAnalyticsSharing(disabled: Bool) async {
do {
if disabled {
let disableUseCase = try sessionManager.makeDisableAnalyticsUseCase()
try disableUseCase.invoke()
} else {
let enableUseCase = try await sessionManager.makeEnableAnalyticsUseCase()
try await enableUseCase.invoke()
}
} catch {
WireLogger.analytics.error("Failed to toggle analytics sharing: \(error)")
return
}
func enableAnalytics() async throws {
try await sessionManager.makeEnableAnalyticsUseCase().invoke()
ExtensionSettings.shared.disableAnalyticsSharing = false
AVSFlowManager.getInstance()?.setEnableMetrics(true)
}

ExtensionSettings.shared.disableAnalyticsSharing = disabled
AVSFlowManager.getInstance()?.setEnableMetrics(!disabled)
func disableAnalytics() throws {
try sessionManager.makeDisableAnalyticsUseCase().invoke()
ExtensionSettings.shared.disableAnalyticsSharing = true
AVSFlowManager.getInstance()?.setEnableMetrics(false)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ struct AnalyticsServiceConfigurationBuilder {
return AnalyticsServiceConfiguration(
secretKey: secretKey,
serverHost: countlyURL,
didUserGiveTrackingConsent: !ExtensionSettings.shared.disableAnalyticsSharing
didUserGiveTrackingConsent: !(ExtensionSettings.shared.disableAnalyticsSharing ?? true)
johnxnguyen marked this conversation as resolved.
Show resolved Hide resolved
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ import WireSyncEngine
import WireUtilities

protocol TrackingInterface {
var disableAnalyticsSharing: Bool { get }
func disableAnalyticsSharing(isDisabled: Bool, resultHandler: @escaping (Result<Void, any Error>) -> Void)

var isAnalyticsDisabled: Bool { get }
func requestAnalyticsConsent() async throws -> Bool
func disableAnalytics() throws
func enableAnalytics() async throws

}

protocol AVSMediaManagerInterface {
Expand Down Expand Up @@ -226,20 +230,37 @@ final class SettingsPropertyFactory {
case .disableAnalyticsSharing:
let getAction: GetAction = { [unowned self] _ in
if let tracking = self.tracking {
return SettingsPropertyValue(tracking.disableAnalyticsSharing)
return SettingsPropertyValue(tracking.isAnalyticsDisabled)
} else {
return SettingsPropertyValue(false)
}
}

let setAction: SetAction = { [unowned self] _, value, resultHandler in
if var tracking = self.tracking {
switch value {
case .number(let number):
tracking.disableAnalyticsSharing(isDisabled: number.boolValue, resultHandler: resultHandler)
default:
throw SettingsPropertyError.WrongValue("Incorrect type \(value) for key \(propertyName)")
guard let tracking else {
return
samwyndham marked this conversation as resolved.
Show resolved Hide resolved
}

guard case .number(let shouldDisable) = value else {
throw SettingsPropertyError.WrongValue("Incorrect type \(value) for key \(propertyName)")
}

Task { @MainActor in
do {
if shouldDisable.boolValue {
try tracking.disableAnalytics()
} else {
guard try await tracking.requestAnalyticsConsent() else {
throw TrackingManagerError.userConsentDenied
}

try await tracking.enableAnalytics()
}
} catch {
resultHandler(.failure(error))
}

resultHandler(.success(()))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,21 @@ final class ZClientViewController: UIViewController {
setUpConferenceCallingUnavailableObserver()
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
firstTimeRequestToEnableAnalytics()
}

private func firstTimeRequestToEnableAnalytics() {
Task {
do {
try await trackingManager?.firstTimeRequestToEnableAnalytics()
} catch {
WireLogger.analytics.error("failed to first time enable analytics: \(error)")
}
johnxnguyen marked this conversation as resolved.
Show resolved Hide resolved
}
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return wr_supportedInterfaceOrientations
}
Expand Down
8 changes: 4 additions & 4 deletions wire-ios/WireCommonComponents/ExtensionSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ private enum ExtensionSettingsKey: String, CaseIterable {

private var defaultValue: Any? {
switch self {
// Always disable analytics by default.
case .disableAnalyticsSharing:
return true
// No default value because the user needs to decide.
return nil
case .disableLinkPreviews:
return false
}
Expand Down Expand Up @@ -63,8 +63,8 @@ public final class ExtensionSettings: NSObject {
}
}

public var disableAnalyticsSharing: Bool {
get { defaults.object(forKey: ExtensionSettingsKey.disableAnalyticsSharing.rawValue) as? Bool ?? true }
public var disableAnalyticsSharing: Bool? {
get { defaults.object(forKey: ExtensionSettingsKey.disableAnalyticsSharing.rawValue) as? Bool }
set { defaults.set(newValue, forKey: ExtensionSettingsKey.disableAnalyticsSharing.rawValue) }
}

Expand Down
Loading