@capgo/capacitor-updater 6.39.0 → 6.40.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,11 +5,7 @@
5
5
  */
6
6
 
7
7
  import Foundation
8
- #if canImport(ZipArchive)
9
- import ZipArchive
10
- #else
11
- import SSZipArchive
12
- #endif
8
+ import ZIPFoundation
13
9
  import Alamofire
14
10
  import Compression
15
11
  import UIKit
@@ -161,7 +157,8 @@ import UIKit
161
157
  case .success:
162
158
  self.logger.info("Rate limit statistic sent")
163
159
  case let .failure(error):
164
- self.logger.error("Error sending rate limit statistic: \(error.localizedDescription)")
160
+ self.logger.error("Error sending rate limit statistic")
161
+ self.logger.debug("Error: \(error.localizedDescription)")
165
162
  }
166
163
  semaphore.signal()
167
164
  }
@@ -207,7 +204,8 @@ import UIKit
207
204
  do {
208
205
  try FileManager.default.createDirectory(atPath: source.path, withIntermediateDirectories: true, attributes: nil)
209
206
  } catch {
210
- logger.error("Cannot createDirectory \(source.path)")
207
+ logger.error("Cannot create directory")
208
+ logger.debug("Directory path: \(source.path)")
211
209
  throw CustomError.cannotCreateDirectory
212
210
  }
213
211
  }
@@ -217,7 +215,8 @@ import UIKit
217
215
  do {
218
216
  try FileManager.default.removeItem(atPath: source.path)
219
217
  } catch {
220
- logger.error("File not removed. \(source.path)")
218
+ logger.error("File not removed")
219
+ logger.debug("Path: \(source.path)")
221
220
  throw CustomError.cannotDeleteDirectory
222
221
  }
223
222
  }
@@ -234,43 +233,29 @@ import UIKit
234
233
  return false
235
234
  }
236
235
  } catch {
237
- logger.error("File not moved. source: \(source.path) dest: \(dest.path)")
236
+ logger.error("File not moved")
237
+ logger.debug("Source: \(source.path), Dest: \(dest.path)")
238
238
  throw CustomError.cannotUnflat
239
239
  }
240
240
  }
241
241
 
242
- private func unzipProgressHandler(entry: String, zipInfo: unz_file_info, entryNumber: Int, total: Int, destUnZip: URL, id: String, unzipError: inout NSError?) {
243
- if entry.contains("\\") {
244
- logger.error("unzip: Windows path is not supported, please use unix path as required by zip RFC: \(entry)")
242
+ private func validateZipEntry(path: String, destUnZip: URL) throws {
243
+ // Check for Windows paths
244
+ if path.contains("\\") {
245
+ logger.error("Unzip failed: Windows path not supported")
246
+ logger.debug("Invalid path: \(path)")
245
247
  self.sendStats(action: "windows_path_fail")
248
+ throw CustomError.cannotUnzip
246
249
  }
247
250
 
248
- let fileURL = destUnZip.appendingPathComponent(entry)
249
- let canonicalPath = fileURL.path
250
- let canonicalDir = destUnZip.path
251
+ // Check for path traversal
252
+ let fileURL = destUnZip.appendingPathComponent(path)
253
+ let canonicalPath = fileURL.standardizedFileURL.path
254
+ let canonicalDir = destUnZip.standardizedFileURL.path
251
255
 
252
256
  if !canonicalPath.hasPrefix(canonicalDir) {
253
257
  self.sendStats(action: "canonical_path_fail")
254
- unzipError = NSError(domain: "CanonicalPathError", code: 0, userInfo: nil)
255
- }
256
-
257
- let isDirectory = entry.hasSuffix("/")
258
- if !isDirectory {
259
- let folderURL = fileURL.deletingLastPathComponent()
260
- if !FileManager.default.fileExists(atPath: folderURL.path) {
261
- do {
262
- try FileManager.default.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil)
263
- } catch {
264
- self.sendStats(action: "directory_path_fail")
265
- unzipError = error as NSError
266
- }
267
- }
268
- }
269
-
270
- let newPercent = self.calcTotalPercent(percent: Int(Double(entryNumber) / Double(total) * 100), min: 75, max: 81)
271
- if newPercent != self.unzipPercent {
272
- self.unzipPercent = newPercent
273
- self.notifyDownload(id: id, percent: newPercent)
258
+ throw CustomError.cannotUnzip
274
259
  }
275
260
  }
276
261
 
@@ -282,36 +267,52 @@ import UIKit
282
267
  self.unzipPercent = 0
283
268
  self.notifyDownload(id: id, percent: 75)
284
269
 
285
- let semaphore = DispatchSemaphore(value: 0)
286
- var unzipError: NSError?
287
-
288
- let success = SSZipArchive.unzipFile(atPath: sourceZip.path,
289
- toDestination: destUnZip.path,
290
- preserveAttributes: true,
291
- overwrite: true,
292
- nestedZipLevel: 1,
293
- password: nil,
294
- error: &unzipError,
295
- delegate: nil,
296
- progressHandler: { [weak self] (entry, zipInfo, entryNumber, total) in
297
- DispatchQueue.global(qos: .background).async {
298
- guard let self = self else { return }
299
- if !notify {
300
- return
301
- }
302
- self.unzipProgressHandler(entry: entry, zipInfo: zipInfo, entryNumber: entryNumber, total: total, destUnZip: destUnZip, id: id, unzipError: &unzipError)
303
- }
304
- },
305
- completionHandler: { _, _, _ in
306
- semaphore.signal()
307
- })
270
+ // Open the archive
271
+ let archive: Archive
272
+ do {
273
+ archive = try Archive(url: sourceZip, accessMode: .read)
274
+ } catch {
275
+ self.sendStats(action: "unzip_fail")
276
+ throw CustomError.cannotUnzip
277
+ }
308
278
 
309
- semaphore.wait()
279
+ // Create destination directory
280
+ try FileManager.default.createDirectory(at: destUnZip, withIntermediateDirectories: true, attributes: nil)
281
+
282
+ // Count total entries for progress
283
+ let totalEntries = archive.reduce(0) { count, _ in count + 1 }
284
+ var processedEntries = 0
285
+
286
+ do {
287
+ for entry in archive {
288
+ // Validate entry path for security
289
+ try validateZipEntry(path: entry.path, destUnZip: destUnZip)
290
+
291
+ let destPath = destUnZip.appendingPathComponent(entry.path)
292
+
293
+ // Create parent directories if needed
294
+ let parentDir = destPath.deletingLastPathComponent()
295
+ if !FileManager.default.fileExists(atPath: parentDir.path) {
296
+ try FileManager.default.createDirectory(at: parentDir, withIntermediateDirectories: true, attributes: nil)
297
+ }
310
298
 
311
- if !success || unzipError != nil {
299
+ // Extract the entry
300
+ _ = try archive.extract(entry, to: destPath, skipCRC32: true)
301
+
302
+ // Update progress
303
+ processedEntries += 1
304
+ if notify && totalEntries > 0 {
305
+ let newPercent = self.calcTotalPercent(percent: Int(Double(processedEntries) / Double(totalEntries) * 100), min: 75, max: 81)
306
+ if newPercent != self.unzipPercent {
307
+ self.unzipPercent = newPercent
308
+ self.notifyDownload(id: id, percent: newPercent)
309
+ }
310
+ }
311
+ }
312
+ } catch {
312
313
  self.sendStats(action: "unzip_fail")
313
314
  try? FileManager.default.removeItem(at: destUnZip)
314
- throw unzipError ?? CustomError.cannotUnzip
315
+ throw error
315
316
  }
316
317
 
317
318
  if try unflatFolder(source: destUnZip, dest: destPersist) {
@@ -324,7 +325,8 @@ import UIKit
324
325
  try FileManager.default.removeItem(at: sourceZip)
325
326
  }
326
327
  } catch {
327
- logger.error("Could not delete source zip at \(sourceZip.path): \(error)")
328
+ logger.error("Could not delete source zip")
329
+ logger.debug("Path: \(sourceZip.path), Error: \(error)")
328
330
  }
329
331
  }
330
332
 
@@ -399,8 +401,9 @@ import UIKit
399
401
  latest.comment = comment
400
402
  }
401
403
  case let .failure(error):
402
- self.logger.error("Error getting Latest \(response.value.debugDescription) \(error)")
403
- latest.message = "Error getting Latest \(String(describing: response.value))"
404
+ self.logger.error("Error getting latest version")
405
+ self.logger.debug("Response: \(response.value.debugDescription), Error: \(error)")
406
+ latest.message = "Error getting Latest"
404
407
  latest.error = "response_error"
405
408
  latest.statusCode = response.response?.statusCode ?? 0
406
409
  }
@@ -468,7 +471,8 @@ import UIKit
468
471
  fileHash = try CryptoCipher.decryptChecksum(checksum: fileHash, publicKey: self.publicKey)
469
472
  } catch {
470
473
  downloadError = error
471
- logger.error("CryptoCipher.decryptChecksum error \(id) \(fileName) error: \(error)")
474
+ logger.error("Checksum decryption failed")
475
+ logger.debug("Bundle: \(id), File: \(fileName), Error: \(error)")
472
476
  }
473
477
  } else if self.hasOldPrivateKeyPropertyInConfig {
474
478
  // V1 Encryption (privateKey) - deprecated but supported
@@ -500,7 +504,8 @@ import UIKit
500
504
  self.notifyDownload(id: id, percent: self.calcTotalPercent(percent: Int((Double(completedFiles) / Double(totalFiles)) * 100), min: 10, max: 70))
501
505
  } catch {
502
506
  downloadError = error
503
- logger.error("Failed to copy builtin file \(fileName): \(error.localizedDescription)")
507
+ logger.error("Failed to copy builtin file")
508
+ logger.debug("File: \(fileName), Error: \(error.localizedDescription)")
504
509
  }
505
510
  dispatchGroup.leave()
506
511
  } else if self.tryCopyFromCache(from: cacheFilePath, to: destFilePath, expectedHash: fileHash) {
@@ -574,15 +579,18 @@ import UIKit
574
579
 
575
580
  completedFiles += 1
576
581
  self.notifyDownload(id: id, percent: self.calcTotalPercent(percent: Int((Double(completedFiles) / Double(totalFiles)) * 100), min: 10, max: 70))
577
- self.logger.info("downloadManifest \(id) \(fileName) downloaded\(isBrotli ? ", decompressed" : "")\(!self.publicKey.isEmpty && !sessionKey.isEmpty ? ", decrypted" : ""), and cached")
582
+ self.logger.info("Manifest file downloaded and cached")
583
+ self.logger.debug("Bundle: \(id), File: \(fileName), Brotli: \(isBrotli), Encrypted: \(!self.publicKey.isEmpty && !sessionKey.isEmpty)")
578
584
  } catch {
579
585
  downloadError = error
580
- self.logger.error("downloadManifest \(id) \(fileName) error: \(error.localizedDescription)")
586
+ self.logger.error("Manifest file download failed")
587
+ self.logger.debug("Bundle: \(id), File: \(fileName), Error: \(error.localizedDescription)")
581
588
  }
582
589
  case .failure(let error):
583
590
  downloadError = error
584
591
  self.sendStats(action: "download_manifest_file_fail", versionName: "\(version):\(fileName)")
585
- self.logger.error("downloadManifest \(id) \(fileName) download error: \(error.localizedDescription). Debug response: \(response.debugDescription).")
592
+ self.logger.error("Manifest file download network error")
593
+ self.logger.debug("Bundle: \(id), File: \(fileName), Error: \(error.localizedDescription), Response: \(response.debugDescription)")
586
594
  }
587
595
  }
588
596
  }
@@ -670,7 +678,8 @@ import UIKit
670
678
  var status = compression_stream_init(streamPointer, COMPRESSION_STREAM_DECODE, COMPRESSION_BROTLI)
671
679
 
672
680
  guard status != COMPRESSION_STATUS_ERROR else {
673
- logger.error("Error: Failed to initialize Brotli stream for \(fileName). Status: \(status)")
681
+ logger.error("Failed to initialize Brotli stream")
682
+ logger.debug("File: \(fileName), Status: \(status)")
674
683
  return nil
675
684
  }
676
685
 
@@ -692,7 +701,8 @@ import UIKit
692
701
  if let baseAddress = rawBufferPointer.baseAddress {
693
702
  streamPointer.pointee.src_ptr = baseAddress.assumingMemoryBound(to: UInt8.self)
694
703
  } else {
695
- logger.error("Error: Failed to get base address for \(fileName)")
704
+ logger.error("Failed to get base address for Brotli decompression")
705
+ logger.debug("File: \(fileName)")
696
706
  status = COMPRESSION_STATUS_ERROR
697
707
  return
698
708
  }
@@ -702,7 +712,8 @@ import UIKit
702
712
  if status == COMPRESSION_STATUS_ERROR {
703
713
  let maxBytes = min(32, data.count)
704
714
  let hexDump = data.prefix(maxBytes).map { String(format: "%02x", $0) }.joined(separator: " ")
705
- logger.error("Error: Brotli decompression failed for \(fileName). First \(maxBytes) bytes: \(hexDump)")
715
+ logger.error("Brotli decompression failed")
716
+ logger.debug("File: \(fileName), First \(maxBytes) bytes: \(hexDump)")
706
717
  break
707
718
  }
708
719
 
@@ -716,18 +727,19 @@ import UIKit
716
727
  if status == COMPRESSION_STATUS_END {
717
728
  break
718
729
  } else if status == COMPRESSION_STATUS_ERROR {
719
- logger.error("Error: Brotli process failed for \(fileName). Status: \(status)")
730
+ logger.error("Brotli process failed")
731
+ logger.debug("File: \(fileName), Status: \(status)")
720
732
  if let text = String(data: data, encoding: .utf8) {
721
733
  let asciiCount = text.unicodeScalars.filter { $0.isASCII }.count
722
734
  let totalCount = text.unicodeScalars.count
723
735
  if totalCount > 0 && Double(asciiCount) / Double(totalCount) >= 0.8 {
724
- logger.error("Error: Input appears to be plain text: \(text)")
736
+ logger.debug("Input appears to be plain text: \(text)")
725
737
  }
726
738
  }
727
739
 
728
740
  let maxBytes = min(32, data.count)
729
741
  let hexDump = data.prefix(maxBytes).map { String(format: "%02x", $0) }.joined(separator: " ")
730
- logger.error("Error: Raw data (\(fileName)): \(hexDump)")
742
+ logger.debug("Raw data: \(hexDump)")
731
743
 
732
744
  return nil
733
745
  }
@@ -738,7 +750,8 @@ import UIKit
738
750
  }
739
751
 
740
752
  if input.count == 0 {
741
- logger.error("Error: Zero input size for \(fileName)")
753
+ logger.error("Zero input size for Brotli decompression")
754
+ logger.debug("File: \(fileName)")
742
755
  break
743
756
  }
744
757
  }
@@ -830,7 +843,8 @@ import UIKit
830
843
  reachabilityManager?.stopListening()
831
844
 
832
845
  if mainError != nil {
833
- logger.error("Failed to download: \(String(describing: mainError))")
846
+ logger.error("Failed to download bundle")
847
+ logger.debug("Error: \(String(describing: mainError))")
834
848
  self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum, link: link, comment: comment))
835
849
  throw mainError!
836
850
  }
@@ -841,7 +855,8 @@ import UIKit
841
855
  try CryptoCipher.decryptFile(filePath: tempDataPath, publicKey: self.publicKey, sessionKey: sessionKey, version: version)
842
856
  try FileManager.default.moveItem(at: tempDataPath, to: finalPath)
843
857
  } catch {
844
- logger.error("Failed decrypt file : \(error)")
858
+ logger.error("Failed to decrypt file")
859
+ logger.debug("Error: \(error)")
845
860
  self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum, link: link, comment: comment))
846
861
  cleanDownloadData()
847
862
  throw error
@@ -854,7 +869,8 @@ import UIKit
854
869
  try self.saveDownloaded(sourceZip: finalPath, id: id, base: self.libraryDir.appendingPathComponent(self.bundleDirectory), notify: true)
855
870
 
856
871
  } catch {
857
- logger.error("Failed to unzip file: \(error)")
872
+ logger.error("Failed to unzip file")
873
+ logger.debug("Error: \(error)")
858
874
  self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum, link: link, comment: comment))
859
875
  // Best-effort cleanup of the decrypted zip file when unzip fails
860
876
  do {
@@ -862,7 +878,8 @@ import UIKit
862
878
  try FileManager.default.removeItem(at: finalPath)
863
879
  }
864
880
  } catch {
865
- logger.error("Could not delete failed zip at \(finalPath.path): \(error)")
881
+ logger.error("Could not delete failed zip")
882
+ logger.debug("Path: \(finalPath.path), Error: \(error)")
866
883
  }
867
884
  cleanDownloadData()
868
885
  throw error
@@ -885,13 +902,15 @@ import UIKit
885
902
  let fileManager = FileManager.default
886
903
  if !fileManager.fileExists(atPath: tempDataPath.path) {
887
904
  if !fileManager.createFile(atPath: tempDataPath.path, contents: Data()) {
888
- logger.error("Cannot ensure that a file at \(tempDataPath.path) exists")
905
+ logger.error("Cannot ensure temp data file exists")
906
+ logger.debug("Path: \(tempDataPath.path)")
889
907
  }
890
908
  }
891
909
 
892
910
  if !fileManager.fileExists(atPath: updateInfo.path) {
893
911
  if !fileManager.createFile(atPath: updateInfo.path, contents: Data()) {
894
- logger.error("Cannot ensure that a file at \(updateInfo.path) exists")
912
+ logger.error("Cannot ensure update info file exists")
913
+ logger.debug("Path: \(updateInfo.path)")
895
914
  }
896
915
  }
897
916
  }
@@ -903,7 +922,8 @@ import UIKit
903
922
  do {
904
923
  try fileManager.removeItem(at: tempDataPath)
905
924
  } catch {
906
- logger.error("Could not delete file at \(tempDataPath): \(error)")
925
+ logger.error("Could not delete temp data file")
926
+ logger.debug("Path: \(tempDataPath), Error: \(error)")
907
927
  }
908
928
  }
909
929
  // Deleting update.dat
@@ -911,7 +931,8 @@ import UIKit
911
931
  do {
912
932
  try fileManager.removeItem(at: updateInfo)
913
933
  } catch {
914
- logger.error("Could not delete file at \(updateInfo): \(error)")
934
+ logger.error("Could not delete update info file")
935
+ logger.debug("Path: \(updateInfo), Error: \(error)")
915
936
  }
916
937
  }
917
938
  }
@@ -930,7 +951,8 @@ import UIKit
930
951
  fileHandle.closeFile()
931
952
  }
932
953
  } catch {
933
- logger.error("Failed to write data starting at byte \(byteOffset): \(error)")
954
+ logger.error("Failed to write partial data")
955
+ logger.debug("Byte offset: \(byteOffset), Error: \(error)")
934
956
  }
935
957
  self.tempData.removeAll() // Clearing tempData to avoid writing the same data multiple times
936
958
  }
@@ -939,7 +961,8 @@ import UIKit
939
961
  do {
940
962
  try "\(version)".write(to: updateInfo, atomically: true, encoding: .utf8)
941
963
  } catch {
942
- logger.error("Failed to save progress: \(error)")
964
+ logger.error("Failed to save download progress")
965
+ logger.debug("Error: \(error)")
943
966
  }
944
967
  }
945
968
  private func getLocalUpdateVersion() -> String { // Return the version that was tried to be downloaded on last download attempt
@@ -961,7 +984,8 @@ import UIKit
961
984
  return fileSize.int64Value
962
985
  }
963
986
  } catch {
964
- logger.error("Could not retrieve already downloaded data size : \(error)")
987
+ logger.error("Could not retrieve download progress size")
988
+ logger.debug("Error: \(error)")
965
989
  }
966
990
  return 0
967
991
  }
@@ -1005,7 +1029,8 @@ import UIKit
1005
1029
  public func delete(id: String, removeInfo: Bool) -> Bool {
1006
1030
  let deleted: BundleInfo = self.getBundleInfo(id: id)
1007
1031
  if deleted.isBuiltin() || self.getCurrentBundleId() == id {
1008
- logger.info("Cannot delete \(id)")
1032
+ logger.info("Cannot delete current or builtin bundle")
1033
+ logger.debug("Bundle ID: \(id)")
1009
1034
  return false
1010
1035
  }
1011
1036
 
@@ -1014,7 +1039,8 @@ import UIKit
1014
1039
  !next.isDeleted() &&
1015
1040
  !next.isErrorStatus() &&
1016
1041
  next.getId() == id {
1017
- logger.info("Cannot delete the next bundle \(id)")
1042
+ logger.info("Cannot delete the next bundle")
1043
+ logger.debug("Bundle ID: \(id)")
1018
1044
  return false
1019
1045
  }
1020
1046
 
@@ -1022,7 +1048,8 @@ import UIKit
1022
1048
  do {
1023
1049
  try FileManager.default.removeItem(atPath: destPersist.path)
1024
1050
  } catch {
1025
- logger.error("Folder \(destPersist.path), not removed.")
1051
+ logger.error("Bundle folder not removed")
1052
+ logger.debug("Path: \(destPersist.path)")
1026
1053
  // even if, we don;t care. Android doesn't care
1027
1054
  if removeInfo {
1028
1055
  self.removeBundleInfo(id: id)
@@ -1035,7 +1062,8 @@ import UIKit
1035
1062
  } else {
1036
1063
  self.saveBundleInfo(id: id, bundle: deleted.setStatus(status: BundleStatus.DELETED.localizedString))
1037
1064
  }
1038
- logger.info("bundle delete \(deleted.getVersionName())")
1065
+ logger.info("Bundle deleted successfully")
1066
+ logger.debug("Version: \(deleted.getVersionName())")
1039
1067
  self.sendStats(action: "delete", versionName: deleted.getVersionName())
1040
1068
  return true
1041
1069
  }
@@ -1063,7 +1091,8 @@ import UIKit
1063
1091
  try fileManager.removeItem(at: cacheFolder)
1064
1092
  logger.info("Cleaned up delta cache folder")
1065
1093
  } catch {
1066
- logger.error("Failed to cleanup delta cache: \(error.localizedDescription)")
1094
+ logger.error("Failed to cleanup delta cache")
1095
+ logger.debug("Error: \(error.localizedDescription)")
1067
1096
  }
1068
1097
  }
1069
1098
 
@@ -1103,13 +1132,16 @@ import UIKit
1103
1132
  do {
1104
1133
  try fileManager.removeItem(at: url)
1105
1134
  self.removeBundleInfo(id: id)
1106
- logger.info("Deleted orphan bundle directory: \(id)")
1135
+ logger.info("Deleted orphan bundle directory")
1136
+ logger.debug("Bundle ID: \(id)")
1107
1137
  } catch {
1108
- logger.error("Failed to delete orphan bundle directory: \(id) \(error.localizedDescription)")
1138
+ logger.error("Failed to delete orphan bundle directory")
1139
+ logger.debug("Bundle ID: \(id), Error: \(error.localizedDescription)")
1109
1140
  }
1110
1141
  }
1111
1142
  } catch {
1112
- logger.error("Failed to enumerate bundle directory for cleanup: \(error.localizedDescription)")
1143
+ logger.error("Failed to enumerate bundle directory for cleanup")
1144
+ logger.debug("Error: \(error.localizedDescription)")
1113
1145
  }
1114
1146
  }
1115
1147
 
@@ -1140,13 +1172,16 @@ import UIKit
1140
1172
 
1141
1173
  do {
1142
1174
  try fileManager.removeItem(at: url)
1143
- logger.info("Deleted orphaned temp unzip folder: \(folderName)")
1175
+ logger.info("Deleted orphaned temp unzip folder")
1176
+ logger.debug("Folder: \(folderName)")
1144
1177
  } catch {
1145
- logger.error("Failed to delete orphaned temp folder: \(folderName) \(error.localizedDescription)")
1178
+ logger.error("Failed to delete orphaned temp folder")
1179
+ logger.debug("Folder: \(folderName), Error: \(error.localizedDescription)")
1146
1180
  }
1147
1181
  }
1148
1182
  } catch {
1149
- logger.error("Failed to enumerate library directory for temp folder cleanup: \(error.localizedDescription)")
1183
+ logger.error("Failed to enumerate library directory for temp folder cleanup")
1184
+ logger.debug("Error: \(error.localizedDescription)")
1150
1185
  }
1151
1186
  }
1152
1187
 
@@ -1222,9 +1257,11 @@ import UIKit
1222
1257
  if autoDeletePrevious && !fallback.isBuiltin() && fallback.getId() != bundle.getId() {
1223
1258
  let res = self.delete(id: fallback.getId())
1224
1259
  if res {
1225
- logger.info("Deleted previous bundle: \(fallback.toString())")
1260
+ logger.info("Deleted previous bundle")
1261
+ logger.debug("Bundle: \(fallback.toString())")
1226
1262
  } else {
1227
- logger.error("Failed to delete previous bundle: \(fallback.toString())")
1263
+ logger.error("Failed to delete previous bundle")
1264
+ logger.debug("Bundle: \(fallback.toString())")
1228
1265
  }
1229
1266
  }
1230
1267
  self.setFallbackBundle(fallback: bundle)
@@ -1305,7 +1342,8 @@ import UIKit
1305
1342
  }
1306
1343
  }
1307
1344
  case let .failure(error):
1308
- self.logger.error("Error set Channel \(error)")
1345
+ self.logger.error("Error setting channel")
1346
+ self.logger.debug("Error: \(error)")
1309
1347
  setChannel.error = "Request failed: \(error.localizedDescription)"
1310
1348
  }
1311
1349
  semaphore.signal()
@@ -1368,7 +1406,8 @@ import UIKit
1368
1406
  }
1369
1407
  }
1370
1408
 
1371
- self.logger.error("Error get Channel \(error)")
1409
+ self.logger.error("Error getting channel")
1410
+ self.logger.debug("Error: \(error)")
1372
1411
  getChannel.error = "Request failed: \(error.localizedDescription)"
1373
1412
  }
1374
1413
  }
@@ -1458,7 +1497,8 @@ import UIKit
1458
1497
  }
1459
1498
  }
1460
1499
  case let .failure(error):
1461
- self.logger.error("Error list channels \(error)")
1500
+ self.logger.error("Error listing channels")
1501
+ self.logger.debug("Error: \(error)")
1462
1502
  listChannels.error = "Request failed: \(error.localizedDescription)"
1463
1503
  }
1464
1504
  }
@@ -1504,9 +1544,11 @@ import UIKit
1504
1544
 
1505
1545
  switch response.result {
1506
1546
  case .success:
1507
- self.logger.info("Stats sent for \(action), version \(versionName)")
1547
+ self.logger.info("Stats sent successfully")
1548
+ self.logger.debug("Action: \(action), Version: \(versionName)")
1508
1549
  case let .failure(error):
1509
- self.logger.error("Error sending stats: \(response.value?.debugDescription ?? "") \(error.localizedDescription)")
1550
+ self.logger.error("Error sending stats")
1551
+ self.logger.debug("Response: \(response.value?.debugDescription ?? "nil"), Error: \(error.localizedDescription)")
1510
1552
  }
1511
1553
  semaphore.signal()
1512
1554
  }
@@ -1530,7 +1572,8 @@ import UIKit
1530
1572
  do {
1531
1573
  result = try UserDefaults.standard.getObj(forKey: "\(trueId)\(self.INFO_SUFFIX)", castTo: BundleInfo.self)
1532
1574
  } catch {
1533
- logger.error("Failed to parse info for bundle [\(trueId)] \(error.localizedDescription)")
1575
+ logger.error("Failed to parse bundle info")
1576
+ logger.debug("Bundle ID: \(trueId), Error: \(error.localizedDescription)")
1534
1577
  result = BundleInfo(id: trueId, version: "", status: BundleStatus.PENDING, checksum: "")
1535
1578
  }
1536
1579
  }
@@ -1565,7 +1608,8 @@ import UIKit
1565
1608
  do {
1566
1609
  try UserDefaults.standard.setObj(update, forKey: "\(id)\(self.INFO_SUFFIX)")
1567
1610
  } catch {
1568
- logger.error("Failed to save info for bundle [\(id)] \(error.localizedDescription)")
1611
+ logger.error("Failed to save bundle info")
1612
+ logger.debug("Bundle ID: \(id), Error: \(error.localizedDescription)")
1569
1613
  }
1570
1614
  }
1571
1615
  UserDefaults.standard.synchronize()