secant-ios-wallet/modules/Sources/Dependencies/SupportDataGenerator/SupportDataGenerator.swift

189 lines
6.4 KiB
Swift

//
// SupportDataGenerator.swift
// secant
//
// Created by Michal Fousek on 28.02.2023.
//
import AVFoundation
import Foundation
import LocalAuthentication
import UIKit
import Generated
import Utils
public struct SupportData: Equatable {
public let toAddress: String
public let subject: String
public let message: String
}
public enum SupportDataGenerator {
public static func generate() -> SupportData {
let items: [SupportDataGeneratorItem] = [
TimeItem(),
AppVersionItem(),
SystemVersionItem(),
DeviceModelItem(),
LocaleItem(),
FreeDiskSpaceItem(),
PermissionsItems()
]
let message = items
.map { $0.generate() }
.flatMap { $0 }
.map { "\($0.0): \($0.1)" }
.joined(separator: "\n")
return SupportData(toAddress: "support@electriccoin.co", subject: "Zashi", message: message)
}
}
private protocol SupportDataGeneratorItem {
func generate() -> [(String, String)]
}
private struct TimeItem: SupportDataGeneratorItem {
private enum Constants {
static let timeKey = L10n.SupportData.TimeItem.time
}
let dateFormatter: DateFormatter
init() {
dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss a ZZZZ"
dateFormatter.locale = Locale(identifier: "en_US")
}
func generate() -> [(String, String)] {
return [(Constants.timeKey, dateFormatter.string(from: Date()))]
}
}
private struct AppVersionItem: SupportDataGeneratorItem {
private enum Constants {
static let bundleIdentifierKey = L10n.SupportData.AppVersionItem.bundleIdentifier
static let versionKey = L10n.SupportData.AppVersionItem.version
static let unknownVersion = L10n.General.unknown
}
func generate() -> [(String, String)] {
let bundle = Bundle.main
guard let infoDict = bundle.infoDictionary else { return [(Constants.versionKey, Constants.unknownVersion)] }
var data: [(String, String)] = []
if let bundleIdentifier = bundle.bundleIdentifier {
data.append((Constants.bundleIdentifierKey, bundleIdentifier))
}
if let build = infoDict["CFBundleVersion"] as? String, let version = infoDict["CFBundleShortVersionString"] as? String {
data.append((Constants.versionKey, "\(version) (\(build))"))
} else {
data.append((Constants.versionKey, Constants.unknownVersion))
}
return data
}
}
private struct SystemVersionItem: SupportDataGeneratorItem {
private enum Constants {
static let systemVersionKey = L10n.SupportData.SystemVersionItem.version
}
func generate() -> [(String, String)] {
return [(Constants.systemVersionKey, UIDevice.current.systemVersion)]
}
}
private struct DeviceModelItem: SupportDataGeneratorItem {
private enum Constants {
static let deviceModelKey = L10n.SupportData.DeviceModelItem.device
static let unknownDevice = L10n.General.unknown
}
func generate() -> [(String, String)] {
var systemInfo = utsname()
uname(&systemInfo)
var readModel: String?
withUnsafePointer(to: &systemInfo.machine.0) { charPointer in
readModel = String(cString: charPointer, encoding: .ascii)
}
let model = readModel ?? Constants.unknownDevice
return [(Constants.deviceModelKey, model)]
}
}
private struct LocaleItem: SupportDataGeneratorItem {
private enum Constants {
static let localeKey = L10n.SupportData.LocaleItem.locale
static let groupingSeparatorKey = L10n.SupportData.LocaleItem.groupingSeparator
static let decimalSeparatorKey = L10n.SupportData.LocaleItem.decimalSeparator
static let unknownSeparator = L10n.General.unknown
}
func generate() -> [(String, String)] {
let locale = Locale.current
return [
(Constants.localeKey, locale.identifier),
(Constants.groupingSeparatorKey, locale.groupingSeparator ?? Constants.unknownSeparator),
(Constants.decimalSeparatorKey, locale.decimalSeparator ?? Constants.unknownSeparator)
]
}
}
private struct FreeDiskSpaceItem: SupportDataGeneratorItem {
private enum Constants {
static let freeDiskSpaceKey = L10n.SupportData.FreeDiskSpaceItem.freeDiskSpace
static let freeDiskSpaceUnknown = L10n.General.unknown
}
func generate() -> [(String, String)] {
let freeDiskSpace: String
let fileURL = URL(fileURLWithPath: NSHomeDirectory())
do {
let values = try fileURL.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey])
if let freeSpace = values.volumeAvailableCapacityForImportantUsage {
freeDiskSpace = "\(freeSpace / 1024 / 1024) MB"
} else {
freeDiskSpace = Constants.freeDiskSpaceUnknown
}
} catch {
LoggerProxy.debug("Can't get free disk space: \(error)")
freeDiskSpace = Constants.freeDiskSpaceUnknown
}
return [(Constants.freeDiskSpaceKey, freeDiskSpace)]
}
}
private struct PermissionsItems: SupportDataGeneratorItem {
private enum Constants {
static let permissionsKey = L10n.SupportData.PermissionItem.permissions
static let cameraPermKey = L10n.SupportData.PermissionItem.camera
static let faceIDAvailable = L10n.SupportData.PermissionItem.faceID
static let touchIDAvailable = L10n.SupportData.PermissionItem.touchID
static let yesText = L10n.General.yes
static let noText = L10n.General.no
}
func generate() -> [(String, String)] {
let cameraAuthorized = AVCaptureDevice.authorizationStatus(for: .video) == .authorized
let bioAuthContext = LAContext()
let biometricAuthAvailable = bioAuthContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
return [
(Constants.permissionsKey, ""),
(Constants.cameraPermKey, cameraAuthorized ? Constants.yesText : Constants.noText),
(Constants.faceIDAvailable, biometricAuthAvailable && bioAuthContext.biometryType == .faceID ? Constants.yesText : Constants.noText),
(Constants.touchIDAvailable, biometricAuthAvailable && bioAuthContext.biometryType == .touchID ? Constants.yesText : Constants.noText)
]
}
}