#104 trace file : no need to enable it in the settings, it is enabled by default. There's a maximum of 15 MB stored, which corresponds to around 6 days (depends on how much logs the specific installation is generating). The files are not deleted anymore but can be sent at any time.
This commit is contained in:
parent
b7061730cd
commit
91e17302a9
|
@ -118,6 +118,7 @@
|
|||
F869188C23A044340065B607 /* TextsM5StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F869188B23A044340065B607 /* TextsM5StackView.swift */; };
|
||||
F878FA7D2405B3CF00BC6DA6 /* BluetoothPeripheralManager+CGMBubbleTransmitterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F878FA7C2405B3CF00BC6DA6 /* BluetoothPeripheralManager+CGMBubbleTransmitterDelegate.swift */; };
|
||||
F889CB6F236D84AC00A81068 /* M5StackView.strings in Resources */ = {isa = PBXBuildFile; fileRef = F889CB71236D84AC00A81068 /* M5StackView.strings */; };
|
||||
F890E07A247687AE008FB2EC /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = F890E079247687AE008FB2EC /* URL.swift */; };
|
||||
F897AAF92200F2D200CDDD10 /* CBPeripheralState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F897AAF82200F2D200CDDD10 /* CBPeripheralState.swift */; };
|
||||
F897AAFB2201018800CDDD10 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = F897AAFA2201018800CDDD10 /* String.swift */; };
|
||||
F897E24B23FC86CF0075E0E8 /* CGMG5TransmitterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F897E24A23FC86CF0075E0E8 /* CGMG5TransmitterDelegate.swift */; };
|
||||
|
@ -454,6 +455,7 @@
|
|||
F889CB8E236D89C800A81068 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Main.strings; sourceTree = "<group>"; };
|
||||
F889CB97236D89C800A81068 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F889CB9D236D8EEC00A81068 /* xdrip v4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "xdrip v4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
F890E079247687AE008FB2EC /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
|
||||
F897AAF82200F2D200CDDD10 /* CBPeripheralState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBPeripheralState.swift; sourceTree = "<group>"; };
|
||||
F897AAFA2201018800CDDD10 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||
F897E24A23FC86CF0075E0E8 /* CGMG5TransmitterDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGMG5TransmitterDelegate.swift; sourceTree = "<group>"; };
|
||||
|
@ -799,6 +801,7 @@
|
|||
F83098FD23AD3F84005741DF /* UITabBarController.swift */,
|
||||
F8E51D602448E695001C9E5A /* Bundle.swift */,
|
||||
F8AF361A245D93ED00B5977B /* Int16.swift */,
|
||||
F890E079247687AE008FB2EC /* URL.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2385,6 +2388,7 @@
|
|||
F816E0ED2432A55F009EE65B /* BluconBluetoothPeripheralViewModel.swift in Sources */,
|
||||
F8F9722923A5915900C3F17D /* CGMTransmitter.swift in Sources */,
|
||||
F8A389ED23342EB10010F405 /* ConstantsNightScout.swift in Sources */,
|
||||
F890E07A247687AE008FB2EC /* URL.swift in Sources */,
|
||||
F8B3A856227F28DC004BA588 /* AlertTypeSettingsViewController.swift in Sources */,
|
||||
F8A1584F22ECB281007F5B5D /* SettingsViewInfoViewModel.swift in Sources */,
|
||||
F8B3A845227F090E004BA588 /* SettingsViewDexcomSettingsViewModel.swift in Sources */,
|
||||
|
|
|
@ -5,8 +5,15 @@ enum ConstantsTrace {
|
|||
/// email address to which to send trace file
|
||||
static let traceFileDestinationAddress = "xdrip@proximus.be"
|
||||
|
||||
/// will be used as filename to store traces on disk, and attachment file name when sending trace via e-mail
|
||||
static let traceFileName = "xdriptrace.txt"
|
||||
/// - will be used as filename to store traces on disk, and attachment file name when sending trace via e-mail
|
||||
/// - filename will be extended with digit, eg xdriptrace.0.log, or xdriptrace.1.log - the actual trace file is always xdriptrace.0.log
|
||||
static let traceFileName = "xdriptrace"
|
||||
|
||||
/// maximum size of one trace file, in MB. If size is larger, files will rotate, ie all trace files will be renamed, from xdriptrace.2.log to xdriptrace.3.log, from xdriptrace.1.log to xdriptrace.2.log, from xdriptrace.0.log to xdriptrace.1.log,
|
||||
static let maximumFileSizeInMB: UInt64 = 5
|
||||
|
||||
/// maximum amount of trace files to hold. When rotating, and if value is 3, then tracefile xdriptrace.2.log will be deleted
|
||||
static let maximumAmountOfTraceFiles = 3
|
||||
|
||||
/// will be used as filename to app info (configured transmitters etc ...)
|
||||
static let appInfoFileName = "appinfo.txt"
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import Foundation
|
||||
|
||||
// source : https://stackoverflow.com/questions/28268145/get-file-size-in-swift
|
||||
extension URL {
|
||||
|
||||
var attributes: [FileAttributeKey : Any]? {
|
||||
do {
|
||||
return try FileManager.default.attributesOfItem(atPath: path)
|
||||
} catch let error as NSError {
|
||||
print("FileAttribute error: \(error)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var fileSize: UInt64 {
|
||||
return attributes?[.size] as? UInt64 ?? UInt64(0)
|
||||
}
|
||||
|
||||
var fileSizeString: String {
|
||||
return ByteCountFormatter.string(fromByteCount: Int64(fileSize), countStyle: .file)
|
||||
}
|
||||
|
||||
var creationDate: Date? {
|
||||
return attributes?[.creationDate] as? Date
|
||||
}
|
||||
|
||||
}
|
|
@ -175,9 +175,6 @@ extension UserDefaults {
|
|||
/// OSLogEnabled enabled or not
|
||||
case OSLogEnabled = "OSLogEnabled"
|
||||
|
||||
/// write trace to file enabled or not
|
||||
case writeTraceToFile = "writeTraceToFile"
|
||||
|
||||
/// to merge from 3.x to 4.x, can be deleted once 3.x is not used anymore
|
||||
case cgmTransmitterDeviceAddress = "cgmTransmitterDeviceAddress"
|
||||
|
||||
|
@ -828,16 +825,6 @@ extension UserDefaults {
|
|||
}
|
||||
}
|
||||
|
||||
/// write trace to file enabled or not
|
||||
var writeTraceToFile: Bool {
|
||||
get {
|
||||
return bool(forKey: Key.writeTraceToFile.rawValue)
|
||||
}
|
||||
set {
|
||||
set(newValue, forKey: Key.writeTraceToFile.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
/// to merge from 3.x to 4.x, can be deleted once 3.x is not used anymore
|
||||
var cgmTransmitterDeviceAddress: String? {
|
||||
get {
|
||||
|
|
|
@ -71,7 +71,6 @@
|
|||
"displayDeltaInCalendarEvent" = "Display Delta?";
|
||||
"infoCalendarAccessDeniedByUser" = "You previously denied access to your Calendar.\n\nTo enable it go to your device settings, privacy, calendars and enable it.";
|
||||
"infoCalendarAccessRestricted" = "You cannot give authorization to xDrip4iOS to access your calendar. This is possibly due to active restrictions such as parental controls being in place.";
|
||||
"writeTraceToFile" = "Write Trace to File?";
|
||||
"sectionTitleTrace" = "Trace";
|
||||
"sendTraceFile" = "Send Trace File";
|
||||
"describeProblem" = "Explain why you need to send the trace file with as much detail as possible. If you have already reported your problem in the Facebook support group 'xDrip4iOS', then mention your facebook name in the e-mail";
|
||||
|
|
|
@ -69,7 +69,6 @@
|
|||
"displayDeltaInCalendarEvent" = "Mostrar Delta?";
|
||||
"infoCalendarAccessDeniedByUser" = "Has denegado previamente permiso para acceder al calendario.\n\nPara habilitarlo de nuevo, ir a Configuración en tu iPhone, Privacidad, Calendarios y habilitarlo.";
|
||||
"infoCalendarAccessRestricted" = "No se puede autorizar a que xDrip4iOS tenga acceso a tu calendario. Podría ser debido a que restricciones tipo Controles Parentales o similares lo están impidiendo.";
|
||||
"writeTraceToFile" = "Crear Archivo de Rastreo?";
|
||||
"sectionTitleTrace" = "Rastrear";
|
||||
"sendTraceFile" = "Enviar Archivo de Rastreo";
|
||||
"describeProblem" = "Explicar en detalle porqué es necesario enviar este archivo de rastreo. Si ya has reportando previamente este problema en el grupo de Facebook 'xDrip4iOS', confirmar tu Nombre en Facebook dentro del cuerpo del correo";
|
||||
|
|
|
@ -69,7 +69,6 @@
|
|||
"displayDeltaInCalendarEvent" = "Mostrar Delta?";
|
||||
"infoCalendarAccessDeniedByUser" = "Has denegado previamente permiso para acceder al calendario.\n\nPara habilitarlo de nuevo, ir a Configuración en tu iPhone, Privacidad, Calendarios y habilitarlo.";
|
||||
"infoCalendarAccessRestricted" = "No se puede autorizar a que xDrip4iOS tenga acceso a tu calendario. Podría ser debido a que restricciones tipo Controles Parentales o similares lo están impidiendo.";
|
||||
"writeTraceToFile" = "Crear Archivo de Rastreo?";
|
||||
"sectionTitleTrace" = "Rastrear";
|
||||
"sendTraceFile" = "Enviar Archivo de Rastreo";
|
||||
"describeProblem" = "Explicar en detalle porqué es necesario enviar este archivo de rastreo. Si usted ya ha reportando previamente este problema en el grupo de Facebook 'xDrip4iOS', confirmar su Nombre en Facebook dentro del cuerpo del correo";
|
||||
|
|
|
@ -322,10 +322,6 @@ class Texts_SettingsView {
|
|||
return NSLocalizedString("infoCalendarAccessRestricted", tableName: filename, bundle: Bundle.main, value: "You cannot give authorization to xDrip4iOS to access your calendar. This is possibly due to active restrictions such as parental controls being in place.", comment: "If user is not allowed to give any app access to the Calendar, due to restrictions. And then tries to activate creation of events in calendar, this message will be shown")
|
||||
}()
|
||||
|
||||
static let writeTraceToFile: String = {
|
||||
return NSLocalizedString("writeTraceToFile", tableName: filename, bundle: Bundle.main, value: "Write Trace to File?", comment: "Should write trace to file be enabled or not")
|
||||
}()
|
||||
|
||||
static let sectionTitleTrace: String = {
|
||||
return NSLocalizedString("sectionTitleTrace", tableName: filename, bundle: Bundle.main, value: "Trace", comment: "in Settings, section title for Trace")
|
||||
}()
|
||||
|
@ -334,10 +330,6 @@ class Texts_SettingsView {
|
|||
return NSLocalizedString("sendTraceFile", tableName: filename, bundle: Bundle.main, value: "Send Trace File", comment: "in Settings, row title to send settings")
|
||||
}()
|
||||
|
||||
static let warningWriteTraceToFile: String = {
|
||||
return NSLocalizedString("warningWriteTraceToFile", tableName: filename, bundle: Bundle.main, value: "Write trace to file is not enabled. Enable this first, perform your tests and then send the trace file", comment: "in Settings, when user clicks send trace to file, but write trace to file is not enabled")
|
||||
}()
|
||||
|
||||
static let describeProblem: String = {
|
||||
return NSLocalizedString("describeProblem", tableName: filename, bundle: Bundle.main, value: "Explain why you need to send the trace file with as much detail as possible. If you have already reported your problem in the Facebook support group 'xDrip4iOS', then mention your facebook name in the e-mail", comment: "Text in pop up shown when user wants to send the trace file")
|
||||
}()
|
||||
|
|
|
@ -26,15 +26,8 @@ fileprivate func getDocumentsDirectory() -> URL {
|
|||
return paths[0]
|
||||
}
|
||||
|
||||
/// trace file, in case tracing needs to be stored on file
|
||||
var traceFileName:URL = {
|
||||
getDocumentsDirectory().appendingPathComponent(ConstantsTrace.traceFileName)
|
||||
}()
|
||||
|
||||
// appinfo filename
|
||||
var appInfoFileName:URL = {
|
||||
getDocumentsDirectory().appendingPathComponent(ConstantsTrace.appInfoFileName)
|
||||
}()
|
||||
/// trace file currently in use, in case tracing needs to be stored on file
|
||||
fileprivate var traceFileName:URL?
|
||||
|
||||
/// function to be used for logging, takes same parameters as os_log but in a next phase also NSLog can be added, or writing to disk to send later via e-mail ..
|
||||
/// - message : the text, same format as in os_log with %{private} and %{public} to either keep variables private or public , for NSLog, only 3 String formatters are suppored "@" for String, "d" for Int, "f" for double.
|
||||
|
@ -45,6 +38,12 @@ var appInfoFileName:URL = {
|
|||
/// Example
|
||||
func trace(_ message: StaticString, log:OSLog, category: String, type: OSLogType, _ args: CVarArg...) {
|
||||
|
||||
// initialize traceFileName if needed
|
||||
if traceFileName == nil {
|
||||
traceFileName = getDocumentsDirectory().appendingPathComponent(ConstantsTrace.traceFileName + ".0.log")
|
||||
}
|
||||
guard let traceFileName = traceFileName else {return}
|
||||
|
||||
// if oslog is enabled in settings, then do os_log
|
||||
if UserDefaults.standard.OSLogEnabled {
|
||||
|
||||
|
@ -79,124 +78,151 @@ func trace(_ message: StaticString, log:OSLog, category: String, type: OSLogType
|
|||
|
||||
}
|
||||
|
||||
// if nslog or write trace to file is enabled in settings, then calculate first string to log
|
||||
if UserDefaults.standard.NSLogEnabled || UserDefaults.standard.writeTraceToFile {
|
||||
// calculate string to log, replacing arguments
|
||||
|
||||
var argumentsCounter: Int = 0
|
||||
|
||||
var actualMessage = message.description
|
||||
|
||||
// try to find the publicMark as long as argumentsCounter is less than the number of arguments
|
||||
while argumentsCounter < args.count {
|
||||
|
||||
var argumentsCounter: Int = 0
|
||||
// mark to replace
|
||||
let publicMark = "%{public}"
|
||||
|
||||
var actualMessage = message.description
|
||||
// get array of indexes of location of publicMark
|
||||
let indexesOfPublicMark = actualMessage.indexes(of: "%{public}")
|
||||
|
||||
// try to find the publicMark as long as argumentsCounter is less than the number of arguments
|
||||
while argumentsCounter < args.count {
|
||||
if indexesOfPublicMark.count > 0 {
|
||||
|
||||
// mark to replace
|
||||
let publicMark = "%{public}"
|
||||
// range starts from first character until just before the publicMark
|
||||
let startOfMessageRange = actualMessage.startIndex..<indexesOfPublicMark[0]
|
||||
// text as String, until just before the publicMark
|
||||
let startOfMessage = String(actualMessage[startOfMessageRange])
|
||||
|
||||
// get array of indexes of location of publicMark
|
||||
let indexesOfPublicMark = actualMessage.indexes(of: "%{public}")
|
||||
// range starts from just after the publicMark till the end
|
||||
var endOfMessageRange = actualMessage.index(indexesOfPublicMark[0], offsetBy: publicMark.count)..<actualMessage.endIndex
|
||||
// text as String, from just after the publicMark till the end
|
||||
var endOfMessage = String(actualMessage[endOfMessageRange])
|
||||
|
||||
if indexesOfPublicMark.count > 0 {
|
||||
|
||||
// range starts from first character until just before the publicMark
|
||||
let startOfMessageRange = actualMessage.startIndex..<indexesOfPublicMark[0]
|
||||
// text as String, until just before the publicMark
|
||||
let startOfMessage = String(actualMessage[startOfMessageRange])
|
||||
|
||||
// range starts from just after the publicMark till the end
|
||||
var endOfMessageRange = actualMessage.index(indexesOfPublicMark[0], offsetBy: publicMark.count)..<actualMessage.endIndex
|
||||
// text as String, from just after the publicMark till the end
|
||||
var endOfMessage = String(actualMessage[endOfMessageRange])
|
||||
|
||||
// no start looking for String Format Specifiers
|
||||
// possible formatting see https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFStrings/formatSpecifiers.html#//apple_ref/doc/uid/TP40004265
|
||||
// not doing them all
|
||||
|
||||
if endOfMessage.starts(with: "@") {
|
||||
let indexOfAt = endOfMessage.indexes(of: "@")
|
||||
endOfMessageRange = endOfMessage.index(after: indexOfAt[0])..<endOfMessage.endIndex
|
||||
endOfMessage = String(endOfMessage[endOfMessageRange])
|
||||
if let argValue = args[argumentsCounter] as? String {
|
||||
endOfMessage = argValue + endOfMessage
|
||||
}
|
||||
} else if endOfMessage.starts(with: "d") || endOfMessage.starts(with: "D") {
|
||||
let indexOfAt = endOfMessage.indexes(of: "d", options: [NSString.CompareOptions.caseInsensitive])
|
||||
endOfMessageRange = endOfMessage.index(after: indexOfAt[0])..<endOfMessage.endIndex
|
||||
endOfMessage = String(endOfMessage[endOfMessageRange])
|
||||
if let argValue = args[argumentsCounter] as? Int {
|
||||
endOfMessage = argValue.description + endOfMessage
|
||||
}
|
||||
} else if endOfMessage.starts(with: "f") || endOfMessage.starts(with: "F") {
|
||||
let indexOfAt = endOfMessage.indexes(of: "f", options: [NSString.CompareOptions.caseInsensitive])
|
||||
endOfMessageRange = endOfMessage.index(after: indexOfAt[0])..<endOfMessage.endIndex
|
||||
endOfMessage = String(endOfMessage[endOfMessageRange])
|
||||
if let argValue = args[argumentsCounter] as? Double {
|
||||
endOfMessage = argValue.description + endOfMessage
|
||||
}
|
||||
// no start looking for String Format Specifiers
|
||||
// possible formatting see https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFStrings/formatSpecifiers.html#//apple_ref/doc/uid/TP40004265
|
||||
// not doing them all
|
||||
|
||||
if endOfMessage.starts(with: "@") {
|
||||
let indexOfAt = endOfMessage.indexes(of: "@")
|
||||
endOfMessageRange = endOfMessage.index(after: indexOfAt[0])..<endOfMessage.endIndex
|
||||
endOfMessage = String(endOfMessage[endOfMessageRange])
|
||||
if let argValue = args[argumentsCounter] as? String {
|
||||
endOfMessage = argValue + endOfMessage
|
||||
}
|
||||
} else if endOfMessage.starts(with: "d") || endOfMessage.starts(with: "D") {
|
||||
let indexOfAt = endOfMessage.indexes(of: "d", options: [NSString.CompareOptions.caseInsensitive])
|
||||
endOfMessageRange = endOfMessage.index(after: indexOfAt[0])..<endOfMessage.endIndex
|
||||
endOfMessage = String(endOfMessage[endOfMessageRange])
|
||||
if let argValue = args[argumentsCounter] as? Int {
|
||||
endOfMessage = argValue.description + endOfMessage
|
||||
}
|
||||
} else if endOfMessage.starts(with: "f") || endOfMessage.starts(with: "F") {
|
||||
let indexOfAt = endOfMessage.indexes(of: "f", options: [NSString.CompareOptions.caseInsensitive])
|
||||
endOfMessageRange = endOfMessage.index(after: indexOfAt[0])..<endOfMessage.endIndex
|
||||
endOfMessage = String(endOfMessage[endOfMessageRange])
|
||||
if let argValue = args[argumentsCounter] as? Double {
|
||||
endOfMessage = argValue.description + endOfMessage
|
||||
}
|
||||
|
||||
actualMessage = startOfMessage + endOfMessage
|
||||
|
||||
} else {
|
||||
// there's no more occurrences of the publicMark, no need to continue
|
||||
break
|
||||
}
|
||||
|
||||
argumentsCounter += 1
|
||||
actualMessage = startOfMessage + endOfMessage
|
||||
|
||||
} else {
|
||||
// there's no more occurrences of the publicMark, no need to continue
|
||||
break
|
||||
}
|
||||
|
||||
argumentsCounter += 1
|
||||
|
||||
}
|
||||
|
||||
// create timeStamp to use in NSLog and tracefile
|
||||
let timeStamp = dateFormatNSLog.string(from: Date())
|
||||
|
||||
// nslog if enabled
|
||||
if UserDefaults.standard.NSLogEnabled {
|
||||
|
||||
NSLog("%@", ConstantsLog.tracePrefix + " " + timeStamp + " " + category + " " + actualMessage)
|
||||
|
||||
}
|
||||
|
||||
// write trace to file
|
||||
do {
|
||||
|
||||
let textToWrite = timeStamp + " " + category + " " + actualMessage + "\n"
|
||||
|
||||
if let fileHandle = FileHandle(forWritingAtPath: traceFileName.path) {
|
||||
|
||||
// file already exists, go to end of file and append text
|
||||
fileHandle.seekToEndOfFile()
|
||||
fileHandle.write(textToWrite.data(using: .utf8)!)
|
||||
|
||||
} else {
|
||||
|
||||
// file doesn't exist yet
|
||||
try textToWrite.write(to: traceFileName, atomically: true, encoding: String.Encoding.utf8)
|
||||
|
||||
}
|
||||
|
||||
// create timeStamp to use in NSLog and tracefile
|
||||
let timeStamp = dateFormatNSLog.string(from: Date())
|
||||
} catch {
|
||||
NSLog("%@", ConstantsLog.tracePrefix + " " + dateFormatNSLog.string(from: Date()) + " write trace to file failed")
|
||||
}
|
||||
|
||||
// check if tracefile has reached limit size and if yes rotate the files
|
||||
if traceFileName.fileSize > ConstantsTrace.maximumFileSizeInMB * 1024 * 1024 {
|
||||
|
||||
// nslog if enabled
|
||||
if UserDefaults.standard.NSLogEnabled {
|
||||
|
||||
NSLog("%@", ConstantsLog.tracePrefix + " " + timeStamp + " " + category + " " + actualMessage)
|
||||
|
||||
}
|
||||
rotateTraceFiles()
|
||||
|
||||
// if trace to file enabled, then write trace to file
|
||||
if UserDefaults.standard.writeTraceToFile {
|
||||
|
||||
do {
|
||||
|
||||
let textToWrite = timeStamp + " " + category + " " + actualMessage + "\n"
|
||||
|
||||
if let fileHandle = FileHandle(forWritingAtPath: traceFileName.path) {
|
||||
|
||||
// file already exists, go to end of file and append text
|
||||
fileHandle.seekToEndOfFile()
|
||||
fileHandle.write(textToWrite.data(using: .utf8)!)
|
||||
|
||||
} else {
|
||||
|
||||
// file doesn't exist yet
|
||||
try textToWrite.write(to: traceFileName, atomically: true, encoding: String.Encoding.utf8)
|
||||
|
||||
}
|
||||
|
||||
} catch {
|
||||
NSLog("%@", ConstantsLog.tracePrefix + " " + dateFormatNSLog.string(from: Date()) + " write trace to file failed")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// deletes the tracefile from disk
|
||||
func deleteTraceFile() {
|
||||
fileprivate func rotateTraceFiles() {
|
||||
|
||||
// assign fileManager
|
||||
let fileManager = FileManager.default
|
||||
|
||||
do {
|
||||
|
||||
let fileManager = FileManager.default
|
||||
try fileManager.removeItem(at: traceFileName)
|
||||
|
||||
} catch {
|
||||
//
|
||||
// first check if last trace file exists
|
||||
let lastFile = getDocumentsDirectory().appendingPathComponent(ConstantsTrace.traceFileName + "." + (ConstantsTrace.maximumAmountOfTraceFiles - 1).description + ".log")
|
||||
if FileHandle(forWritingAtPath: lastFile.path) != nil {
|
||||
|
||||
do {
|
||||
try fileManager.removeItem(at: lastFile)
|
||||
} catch {
|
||||
debuglogging("failed to delete file " + lastFile.absoluteString)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// now rename trace files if they exist,
|
||||
for indexFrom0ToMax in 0...(ConstantsTrace.maximumAmountOfTraceFiles - 2) {
|
||||
|
||||
let index = (ConstantsTrace.maximumAmountOfTraceFiles - 2) - indexFrom0ToMax
|
||||
|
||||
let file = getDocumentsDirectory().appendingPathComponent(ConstantsTrace.traceFileName + "." + index.description + ".log")
|
||||
let newFile = getDocumentsDirectory().appendingPathComponent(ConstantsTrace.traceFileName + "." + (index + 1).description + ".log")
|
||||
|
||||
if FileHandle(forWritingAtPath: file.path) != nil {
|
||||
|
||||
do {
|
||||
try fileManager.moveItem(at: file, to: newFile)
|
||||
} catch {
|
||||
debuglogging("failed to rename file " + lastFile.absoluteString)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// now set tracefilename to nil, it will be reassigned to correct name, ie the one with index 0, at next usage
|
||||
traceFileName = nil
|
||||
|
||||
}
|
||||
|
||||
class Trace {
|
||||
|
@ -221,12 +247,29 @@ class Trace {
|
|||
|
||||
// MARK: - public static functions
|
||||
|
||||
/// creates file on disk with app info
|
||||
static func createAppInfoFile() {
|
||||
/// creates file on disk with app info, and returns the file as Data and the filename
|
||||
static func getAppInfoFileAsData() -> (Data?, String) {
|
||||
|
||||
var traceInfo = ""
|
||||
|
||||
// Info from User Defaults
|
||||
// app version and build
|
||||
if let dictionary = Bundle.main.infoDictionary {
|
||||
|
||||
if let version = dictionary["CFBundleShortVersionString"] as? String {
|
||||
traceInfo.appendStringAndNewLine("Version " + version)
|
||||
} else {
|
||||
traceInfo.appendStringAndNewLine("Version unknown")
|
||||
}
|
||||
|
||||
if let buildnumber = dictionary["CFBundleShortVersionString"] as? String {
|
||||
traceInfo.appendStringAndNewLine("Build number " + buildnumber)
|
||||
} else {
|
||||
traceInfo.appendStringAndNewLine("Build number")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Info from UserDefaults
|
||||
|
||||
// timestamp app launch
|
||||
// unwrap timeStampAppLaunch
|
||||
|
@ -419,14 +462,38 @@ class Trace {
|
|||
}
|
||||
}
|
||||
|
||||
do {
|
||||
return (traceInfo.data(using: .utf8), ConstantsTrace.appInfoFileName)
|
||||
|
||||
}
|
||||
|
||||
/// returns tuple, first type is an array of Data, each element is a tracefile converted to Data, second type is String, each element is the name of the tracefile
|
||||
static func getTraceFilesInData() -> ([Data], [String]) {
|
||||
|
||||
var traceFilesInData = [Data]()
|
||||
var traceFileNames = [String]()
|
||||
|
||||
for index in 0..<ConstantsTrace.maximumAmountOfTraceFiles {
|
||||
|
||||
try traceInfo.write(to: appInfoFileName, atomically: true, encoding: String.Encoding.utf8)
|
||||
let filename = ConstantsTrace.traceFileName + "." + index.description + ".log"
|
||||
|
||||
let file = getDocumentsDirectory().appendingPathComponent(filename)
|
||||
|
||||
if FileHandle(forWritingAtPath: file.path) != nil {
|
||||
|
||||
do {
|
||||
// create traceFile info as data
|
||||
let fileData = try Data(contentsOf: file)
|
||||
traceFilesInData.append(fileData)
|
||||
traceFileNames.append(filename)
|
||||
} catch {
|
||||
debuglogging("failed to create data from " + filename)
|
||||
}
|
||||
|
||||
} catch {
|
||||
NSLog("%@", ConstantsLog.tracePrefix + " " + dateFormatNSLog.string(from: Date()) + " write appinfo to file failed")
|
||||
}
|
||||
}
|
||||
|
||||
return (traceFilesInData, traceFileNames)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,11 +4,8 @@ import os
|
|||
|
||||
fileprivate enum Setting:Int, CaseIterable {
|
||||
|
||||
/// write trace to file enabled or not
|
||||
case writeTraceToFile = 0
|
||||
|
||||
/// to send trace file
|
||||
case sendTraceFile = 1
|
||||
case sendTraceFile = 0
|
||||
|
||||
}
|
||||
|
||||
|
@ -17,9 +14,6 @@ class SettingsViewTraceSettingsViewModel: NSObject {
|
|||
/// need to present MFMailComposeViewController
|
||||
private var uIViewController: UIViewController?
|
||||
|
||||
/// to force a row reload
|
||||
private var rowReloadClosure: ((Int) -> Void)?
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryTraceSettingsViewModel)
|
||||
|
||||
|
@ -35,9 +29,7 @@ class SettingsViewTraceSettingsViewModel: NSObject {
|
|||
|
||||
extension SettingsViewTraceSettingsViewModel: SettingsViewModelProtocol {
|
||||
|
||||
func storeRowReloadClosure(rowReloadClosure: @escaping ((Int) -> Void)) {
|
||||
self.rowReloadClosure = rowReloadClosure
|
||||
}
|
||||
func storeRowReloadClosure(rowReloadClosure: @escaping ((Int) -> Void)) {}
|
||||
|
||||
func storeUIViewController(uIViewController: UIViewController) {
|
||||
self.uIViewController = uIViewController
|
||||
|
@ -57,9 +49,6 @@ extension SettingsViewTraceSettingsViewModel: SettingsViewModelProtocol {
|
|||
|
||||
switch setting {
|
||||
|
||||
case .writeTraceToFile:
|
||||
return Texts_SettingsView.writeTraceToFile
|
||||
|
||||
case .sendTraceFile:
|
||||
return Texts_SettingsView.sendTraceFile
|
||||
|
||||
|
@ -72,9 +61,6 @@ extension SettingsViewTraceSettingsViewModel: SettingsViewModelProtocol {
|
|||
|
||||
switch setting {
|
||||
|
||||
case .writeTraceToFile:
|
||||
return .none
|
||||
|
||||
case .sendTraceFile:
|
||||
return .disclosureIndicator
|
||||
|
||||
|
@ -87,9 +73,6 @@ extension SettingsViewTraceSettingsViewModel: SettingsViewModelProtocol {
|
|||
|
||||
switch setting {
|
||||
|
||||
case .writeTraceToFile:
|
||||
return nil
|
||||
|
||||
case .sendTraceFile:
|
||||
return nil
|
||||
|
||||
|
@ -103,31 +86,6 @@ extension SettingsViewTraceSettingsViewModel: SettingsViewModelProtocol {
|
|||
|
||||
switch setting {
|
||||
|
||||
case .writeTraceToFile:
|
||||
return UISwitch(isOn: UserDefaults.standard.writeTraceToFile, action: {
|
||||
|
||||
(isOn:Bool) in
|
||||
|
||||
if isOn {
|
||||
|
||||
// set writeTraceToFile to true before logging in trace that it is enabled
|
||||
UserDefaults.standard.writeTraceToFile = true
|
||||
|
||||
trace("Trace to file enabled", log: self.log, category: ConstantsLog.categoryTraceSettingsViewModel, type: .info)
|
||||
|
||||
} else {
|
||||
|
||||
trace("Trace to file disabled", log: self.log, category: ConstantsLog.categoryTraceSettingsViewModel, type: .info)
|
||||
|
||||
// set writeTraceToFile to false after logging in trace that it is enabled
|
||||
UserDefaults.standard.writeTraceToFile = false
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
case .sendTraceFile:
|
||||
return nil
|
||||
|
||||
|
@ -145,59 +103,40 @@ extension SettingsViewTraceSettingsViewModel: SettingsViewModelProtocol {
|
|||
|
||||
switch setting {
|
||||
|
||||
case .writeTraceToFile:
|
||||
return .nothing
|
||||
|
||||
case .sendTraceFile:
|
||||
|
||||
if !UserDefaults.standard.writeTraceToFile {
|
||||
|
||||
return .showInfoText(title: Texts_Common.warning, message: Texts_SettingsView.warningWriteTraceToFile)
|
||||
|
||||
} else {
|
||||
|
||||
guard let uIViewController = uIViewController else {fatalError("in SettingsViewTraceSettingsViewModel, onRowSelect, uIViewController is nil")}
|
||||
|
||||
do {
|
||||
guard let uIViewController = uIViewController else {fatalError("in SettingsViewTraceSettingsViewModel, onRowSelect, uIViewController is nil")}
|
||||
|
||||
// check if iOS device can send email, this depends of an email account is configured
|
||||
if MFMailComposeViewController.canSendMail() {
|
||||
|
||||
// create traceFile info as data
|
||||
let fileData = try Data(contentsOf: traceFileName)
|
||||
|
||||
// create app info as data
|
||||
Trace.createAppInfoFile()
|
||||
let appInfoData = try Data(contentsOf: appInfoFileName)
|
||||
|
||||
if MFMailComposeViewController.canSendMail() {
|
||||
return .askConfirmation(title: Texts_HomeView.info, message: Texts_SettingsView.describeProblem, actionHandler: {
|
||||
|
||||
return .askConfirmation(title: Texts_HomeView.info, message: Texts_SettingsView.describeProblem, actionHandler: {
|
||||
|
||||
let mail = MFMailComposeViewController()
|
||||
mail.mailComposeDelegate = self
|
||||
mail.setToRecipients([ConstantsTrace.traceFileDestinationAddress])
|
||||
mail.setMessageBody(Texts_SettingsView.emailbodyText, isHTML: true)
|
||||
|
||||
mail.addAttachmentData(fileData as Data, mimeType: "text/txt", fileName: ConstantsTrace.traceFileName)
|
||||
|
||||
mail.addAttachmentData(appInfoData as Data, mimeType: "text/txt", fileName: ConstantsTrace.appInfoFileName)
|
||||
|
||||
uIViewController.present(mail, animated: true)
|
||||
|
||||
}, cancelHandler: nil)
|
||||
let mail = MFMailComposeViewController()
|
||||
mail.mailComposeDelegate = self
|
||||
mail.setToRecipients([ConstantsTrace.traceFileDestinationAddress])
|
||||
mail.setMessageBody(Texts_SettingsView.emailbodyText, isHTML: true)
|
||||
|
||||
// add all trace files as attachment
|
||||
let traceFilesInData = Trace.getTraceFilesInData()
|
||||
for (index, traceFileInData) in traceFilesInData.0.enumerated() {
|
||||
mail.addAttachmentData(traceFileInData as Data, mimeType: "text/txt", fileName: traceFilesInData.1[index])
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
return .showInfoText(title: Texts_Common.warning, message: Texts_SettingsView.emailNotConfigured)
|
||||
if let appInfoAsData = Trace.getAppInfoFileAsData().0 {
|
||||
mail.addAttachmentData(appInfoAsData as Data, mimeType: "text/txt", fileName: Trace.getAppInfoFileAsData().1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch {
|
||||
// should never get here ?
|
||||
return .showInfoText(title: Texts_Common.warning, message: "Failed to create attachment")
|
||||
uIViewController.present(mail, animated: true)
|
||||
|
||||
}, cancelHandler: nil)
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
return .showInfoText(title: Texts_Common.warning, message: Texts_SettingsView.emailNotConfigured)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -226,18 +165,8 @@ extension SettingsViewTraceSettingsViewModel: MFMailComposeViewControllerDelegat
|
|||
break
|
||||
|
||||
case .sent, .saved:
|
||||
|
||||
// delete the file as it's been sent successfully
|
||||
deleteTraceFile()
|
||||
break
|
||||
|
||||
// disable tracing, to avoid user forgets turning it off
|
||||
UserDefaults.standard.writeTraceToFile = false
|
||||
|
||||
// as setting writeTraceToFile has been changed to false, the row with that setting needs to be reloaded
|
||||
if let rowReloadClosure = rowReloadClosure {
|
||||
rowReloadClosure(Setting.writeTraceToFile.rawValue)
|
||||
}
|
||||
|
||||
case .failed:
|
||||
if let messageHandler = messageHandler {
|
||||
messageHandler(Texts_Common.warning, Texts_SettingsView.failedToSendEmail)
|
||||
|
|
Loading…
Reference in New Issue