TF-1145 Opt-out for crashlytics

This commit is contained in:
Zbigniew Cisiński 2023-03-29 17:44:25 +02:00
parent 8c33dc66e8
commit 856704ece3
13 changed files with 116 additions and 19 deletions

View File

@ -27,10 +27,10 @@ final class FCM: NSObject, MessagingDelegate, FCMHandlerProtocol {
var FCMTokenObtained: FCMTokenObtainedCompletion?
func initialize() {
func initialize(enableCrashlytics: Bool) {
FirebaseApp.configure()
#if !DEBUG
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true)
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(enableCrashlytics)
#endif
Messaging.messaging().delegate = self

View File

@ -24,5 +24,5 @@ public protocol FCMHandlerProtocol: AnyObject {
var FCMTokenObtained: FCMTokenObtainedCompletion? { get set }
func initialize()
func initialize(enableCrashlytics: Bool)
}

View File

@ -483,7 +483,8 @@ extension InteractorFactory {
func aboutModuleInteractor() -> AboutModuleInteracting {
AboutModuleInteractor(
appInfoInteractor: appInfoInteractor()
appInfoInteractor: appInfoInteractor(),
registerDeviceInteractor: registerDeviceInteractor()
)
}

View File

@ -36,9 +36,11 @@ enum UpdateDeviceError: Error {
protocol RegisterDeviceInteracting: AnyObject {
var isDeviceRegistered: Bool { get }
var isCrashlyticsDisabled: Bool { get }
func initialize()
func registerDevice(completion: @escaping (Result<Void, RegisterDeviceError>) -> Void)
func updateDevice(completion: @escaping (Result<Void, UpdateDeviceError>) -> Void)
func setCrashlyticsDisabled(_ disabled: Bool)
}
final class RegisterDeviceInteractor {
@ -59,9 +61,13 @@ extension RegisterDeviceInteractor: RegisterDeviceInteracting {
mainRepository.isDeviceIDSet
}
var isCrashlyticsDisabled: Bool {
mainRepository.isCrashlyticsDisabled
}
func initialize() {
Log("RegisterDeviceInteractor - Initializing", module: .interactor)
channelState.initialize()
channelState.initialize(enableCrashlytics: !mainRepository.isCrashlyticsDisabled)
}
func registerDevice(completion: @escaping (Result<Void, RegisterDeviceError>) -> Void) {
@ -124,6 +130,10 @@ extension RegisterDeviceInteractor: RegisterDeviceInteracting {
}
}
}
func setCrashlyticsDisabled(_ disabled: Bool) {
mainRepository.setCrashlyticsDisabled(disabled)
}
}
private extension RegisterDeviceInteractor {
func gcmTokenObtained(_ token: String) {

View File

@ -82,6 +82,8 @@ protocol MainRepository: AnyObject {
var currentAppVersion: String { get }
func setIntroductionAsShown()
func introductionWasShown() -> Bool
func setCrashlyticsDisabled(_ disabled: Bool)
var isCrashlyticsDisabled: Bool { get }
// MARK: - Services
var hasServices: Bool { get }

View File

@ -31,4 +31,12 @@ extension MainRepositoryImpl {
func introductionWasShown() -> Bool {
userDefaultsRepository.introductionWasShown()
}
func setCrashlyticsDisabled(_ disabled: Bool) {
userDefaultsRepository.setCrashlyticsDisabled(disabled)
}
var isCrashlyticsDisabled: Bool {
userDefaultsRepository.isCrashlyticsDisabled
}
}

View File

@ -72,4 +72,7 @@ protocol UserDefaultsRepository: AnyObject {
func introductionWasShown() -> Bool
func clearAll()
var isCrashlyticsDisabled: Bool { get }
func setCrashlyticsDisabled(_ disabled: Bool)
}

View File

@ -44,6 +44,7 @@ final class UserDefaultsRepositoryImpl: UserDefaultsRepository {
case isSectionZeroCollapsed = "com.2fas.SectionZeroCollapsedState"
case viewPath = "ViewPathController.ViewStatePath"
case introductionWasShown = "IntroductionWasShown"
case crashlyticsDisabled
}
private let userDefaults = UserDefaults()
private let sharedDefaults = UserDefaults(suiteName: Config.suiteName)!
@ -241,6 +242,17 @@ final class UserDefaultsRepositoryImpl: UserDefaultsRepository {
return (viewPath: node.path, savedAt: node.savedAt)
}
// MARK: - Crashlytics
var isCrashlyticsDisabled: Bool {
userDefaults.bool(forKey: Keys.crashlyticsDisabled.rawValue)
}
func setCrashlyticsDisabled(_ disabled: Bool) {
userDefaults.set(disabled, forKey: Keys.crashlyticsDisabled.rawValue)
userDefaults.synchronize()
}
// MARK: - Clear all
func clearAll() {

View File

@ -21,13 +21,17 @@ import Foundation
protocol AboutModuleInteracting: AnyObject {
var currentAppVersion: String { get }
var isCrashlyticsDisabled: Bool { get }
func setCrashlyticsDisabled(_ disabled: Bool)
}
final class AboutModuleInteractor {
private let appInfoInteractor: AppInfoInteracting
private let registerDeviceInteractor: RegisterDeviceInteracting
init(appInfoInteractor: AppInfoInteracting) {
init(appInfoInteractor: AppInfoInteracting, registerDeviceInteractor: RegisterDeviceInteracting) {
self.appInfoInteractor = appInfoInteractor
self.registerDeviceInteractor = registerDeviceInteractor
}
}
@ -35,4 +39,12 @@ extension AboutModuleInteractor: AboutModuleInteracting {
var currentAppVersion: String {
appInfoInteractor.currentAppVersion
}
var isCrashlyticsDisabled: Bool {
registerDeviceInteractor.isCrashlyticsDisabled
}
func setCrashlyticsDisabled(_ disabled: Bool) {
registerDeviceInteractor.setCrashlyticsDisabled(disabled)
}
}

View File

@ -22,6 +22,13 @@ import UIKit
struct AboutSection: TableViewSection {
let title: String
var cells: [AboutCell]
let footer: String?
init(title: String, cells: [AboutCell], footer: String? = nil) {
self.title = title
self.cells = cells
self.footer = footer
}
}
struct AboutCell: Hashable {
@ -29,6 +36,7 @@ struct AboutCell: Hashable {
case external
case share
case noAccessory
case toggle(isOn: Bool)
}
enum Action: Hashable {
case writeReview
@ -41,7 +49,7 @@ struct AboutCell: Hashable {
let title: String
let accessory: AccessoryKind
let action: Action
let action: Action?
}
extension AboutCell.AccessoryKind {
@ -53,7 +61,7 @@ extension AboutCell.AccessoryKind {
case .external: return Asset.externalLinkIcon.image
.withRenderingMode(.alwaysTemplate)
.withTintColor(Theme.Colors.Icon.theme)
case .noAccessory: return nil
case .noAccessory, .toggle: return nil
}
}
}

View File

@ -33,7 +33,16 @@ extension AboutPresenter {
]),
.init(title: T.Settings.support, cells: [
.init(title: T.Settings.sendLogs, accessory: .noAccessory, action: .sendLogs)
])
]),
.init(title: "Crash info", cells: [
.init(
title: "Opt-out of crash info",
accessory: .toggle(isOn: interactor.isCrashlyticsDisabled),
action: nil
)
],
footer: "Changes will be applied in next app session"
)
]
}
}

View File

@ -23,7 +23,7 @@ final class AboutPresenter {
weak var view: AboutViewControlling?
private let flowController: AboutFlowControlling
private let interactor: AboutModuleInteracting
let interactor: AboutModuleInteracting
init(flowController: AboutFlowControlling, interactor: AboutModuleInteracting) {
self.flowController = flowController
@ -43,12 +43,14 @@ extension AboutPresenter {
func handleSelection(at indexPath: IndexPath) {
let menu = buildMenu()
guard let section = menu[safe: indexPath.section],
let cell = section.cells[safe: indexPath.row] else {
let cell = section.cells[safe: indexPath.row],
let action = cell.action
else {
reload()
return
}
switch cell.action {
switch action {
case .tos:
flowController.toTOS()
case .privacyPolicy:
@ -63,6 +65,12 @@ extension AboutPresenter {
flowController.toAcknowledgements()
}
}
func handleToogle() {
let value = interactor.isCrashlyticsDisabled
interactor.setCrashlyticsDisabled(!value)
reload()
}
}
private extension AboutPresenter {

View File

@ -28,9 +28,9 @@ final class AboutViewController: UIViewController {
private let tableView = SettingsMenuTableView()
private let footer = AboutFooter()
private var tableViewAdapter: TableViewAdapter<AboutSection, AboutCell>!
override func viewDidLoad() {
super.viewDidLoad()
@ -43,6 +43,9 @@ final class AboutViewController: UIViewController {
tableViewAdapter.titleForHeader = { [weak self] _, sectionOffset, snapshot in
self?.titleForHeader(offset: sectionOffset, snapshot: snapshot)
}
tableViewAdapter.titleForFooter = { [weak self] _, sectionOffset, snapshot in
self?.titleForFooter(offset: sectionOffset, snapshot: snapshot)
}
tableViewAdapter.delegatee.didSelectItem = { [weak self] tableView, indexPath, data in
self?.didSelect(tableView: tableView, indexPath: indexPath, data: data)
}
@ -52,7 +55,7 @@ final class AboutViewController: UIViewController {
setupTableViewLayout()
title = T.Settings.about
hidesBottomBarWhenPushed = false
navigationItem.backButtonDisplayMode = .minimal
@ -91,10 +94,18 @@ extension AboutViewController {
) as? SettingsMenuTableViewCell else { return nil }
let accessory: SettingsMenuTableViewCell.AccessoryType = {
guard let image = data.accessory.icon else { return .none }
let imageView = UIImageView(image: image)
imageView.tintColor = Theme.Colors.Icon.theme
return .customView(customView: imageView)
if let image = data.accessory.icon {
let imageView = UIImageView(image: image)
imageView.tintColor = Theme.Colors.Icon.theme
return .customView(customView: imageView)
}
if case AboutCell.AccessoryKind.toggle(let isOn) = data.accessory {
return .toggle(isEnabled: isOn) { [weak self] _, _ in
self?.presenter.handleToogle()
}
}
return .none
}()
let decorateText: SettingsMenuTableViewCell.TextDecoration = {
@ -103,6 +114,11 @@ extension AboutViewController {
}
return .none
}()
if data.action == nil {
cell.selectionStyle = .none
} else {
cell.selectionStyle = .default
}
cell.update(icon: nil, title: data.title, kind: accessory, decorateText: decorateText)
return cell
}
@ -112,6 +128,14 @@ extension AboutViewController {
return section.title
}
func titleForFooter(
offset: Int,
snapshot: TableViewDataSnapshot<AboutSection, AboutCell>
) -> String? {
let section = snapshot.section(at: offset)
return section.footer
}
func didSelect(tableView: UITableView, indexPath: IndexPath, data: AboutCell) {
tableView.deselectRow(at: indexPath, animated: true)
presenter.handleSelection(at: indexPath)