diff --git a/Sources/NextcloudKit/Extensions/Date+Extension.swift b/Sources/NextcloudKit/Extensions/Date+Extension.swift new file mode 100644 index 00000000..ba39dcaa --- /dev/null +++ b/Sources/NextcloudKit/Extensions/Date+Extension.swift @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import Foundation + +extension Date { + func formatted(using format: String) -> String { + NKLogFileManager.shared.convertDate(self, format: format) + } +} diff --git a/Sources/NextcloudKit/Extensions/String+Extension.swift b/Sources/NextcloudKit/Extensions/String+Extension.swift index ebfcde33..256d782f 100644 --- a/Sources/NextcloudKit/Extensions/String+Extension.swift +++ b/Sources/NextcloudKit/Extensions/String+Extension.swift @@ -28,4 +28,8 @@ extension String { public var fileExtension: String { return String(NSString(string: self).pathExtension) } + + func parsedDate(using format: String) -> Date? { + NKLogFileManager.shared.convertDate(self, format: format) + } } diff --git a/Sources/NextcloudKit/Log/NKLog.swift b/Sources/NextcloudKit/Log/NKLog.swift new file mode 100644 index 00000000..2dc8832c --- /dev/null +++ b/Sources/NextcloudKit/Log/NKLog.swift @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import Foundation + +// Public logging helpers for apps using the NextcloudKit library. +// These functions internally use `NKLogFileManager.shared`. + +@inlinable +public func nkLog(debug message: String) { + NKLogFileManager.shared.writeLog(debug: message) +} + +@inlinable +public func nkLog(info message: String) { + NKLogFileManager.shared.writeLog(info: message) +} + +@inlinable +public func nkLog(warning message: String) { + NKLogFileManager.shared.writeLog(warning: message) +} + +@inlinable +public func nkLog(error message: String) { + NKLogFileManager.shared.writeLog(error: message) +} + +@inlinable +public func nkLog(success message: String) { + NKLogFileManager.shared.writeLog(success: message) +} + +@inlinable +public func nkLog(network message: String) { + NKLogFileManager.shared.writeLog(network: message) +} + +@inlinable +public func nkLog(start message: String) { + NKLogFileManager.shared.writeLog(start: message) +} + +@inlinable +public func nkLog(stop message: String) { + NKLogFileManager.shared.writeLog(stop: message) +} + +/// Logs a custom tagged message. +/// - Parameters: +/// - tag: A custom uppercase tag, e.g. \"PUSH\", \"SYNC\", \"AUTH\". +/// - emoji: the type tag .info, .debug, .warning, .error, .success .. +/// - message: The message to log. +@inlinable +public func nkLog(tag: String, emoji: NKLogTagEmoji = .debug, message: String) { + NKLogFileManager.shared.writeLog(tag: tag, emoji: emoji, message: message) +} diff --git a/Sources/NextcloudKit/Log/NKLogFileManager.swift b/Sources/NextcloudKit/Log/NKLogFileManager.swift new file mode 100644 index 00000000..61696dac --- /dev/null +++ b/Sources/NextcloudKit/Log/NKLogFileManager.swift @@ -0,0 +1,344 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import Foundation + +// Defines the severity level of a log message. +// Defines the level of log verbosity. +public enum NKLogLevel: Int, CaseIterable, Identifiable, Comparable { + // Logging is disabled. + case disabled = 0 + + // Logs basic request lifecycle for developers (request started, response result). + case compact = 1 + + // Logs important info such as result content, errors. + case normal = 2 + + // Logs detailed debug info like headers and bodies. + case verbose = 3 + + // Needed for Picker + public var id: Int { rawValue } + + // For Picker display + public var displayText: String { + switch self { + case .disabled: return NSLocalizedString("_disabled_", comment: "") + case .compact: return NSLocalizedString("_compact_", comment: "") + case .normal: return NSLocalizedString("_normal_", comment: "") + case .verbose: return NSLocalizedString("_verbose_", comment: "") + } + } + + // For Comparable + public static func < (lhs: NKLogLevel, rhs: NKLogLevel) -> Bool { + lhs.rawValue < rhs.rawValue + } +} + +/// Type for writes a emoji in writeLog(tag: ...) +public enum NKLogTagEmoji: String { + case error = "[ERROR]" + case success = "[SUCCESS]" + case warning = "[WARNING]" + case info = "[INFO]" + case debug = "[DEBUG]" + case network = "[NETWORK]" + case start = "[START]" + case stop = "[STOP]" +} + +/// A logger that writes log messages to a file in a subdirectory of the user's Documents folder, +/// rotates the log daily +/// Compatible with iOS 13.0+ and Swift 6. +public final class NKLogFileManager { + + // MARK: - Singleton + + /// Shared singleton instance of the log manager. + public static let shared = NKLogFileManager() + + /// Configures the shared logger instance. + /// - Parameters: + /// - minLevel: The minimum log level to be recorded. + + public static func configure(logLevel: NKLogLevel = .normal) { + shared.setConfiguration(logLevel: logLevel) + } + + /// Returns the file URL of the currently active log file. + public func currentLogFileURL() -> URL { + return logDirectory.appendingPathComponent(logFileName) + } + + // MARK: - Configuration + + private let logFileName = "log.txt" + private let logDirectory: URL + public var logLevel: NKLogLevel + private var currentLogDate: String + private let logQueue = DispatchQueue(label: "LogWriterQueue", attributes: .concurrent) + private let rotationQueue = DispatchQueue(label: "LogRotationQueue") + private let fileManager = FileManager.default + + // Cache for dynamic format strings, populated at runtime. Thread-safe via serial queue. + private static var cachedDynamicFormatters: [String: DateFormatter] = [:] + private static let formatterAccessQueue = DispatchQueue(label: "com.yourapp.dateformatter.cache") + + // MARK: - Initialization + + private init(logLevel: NKLogLevel = .normal) { + self.logLevel = logLevel + + let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! + let logsFolder = documents.appendingPathComponent("Logs", isDirectory: true) + if !FileManager.default.fileExists(atPath: logsFolder.path) { + try? FileManager.default.createDirectory(at: logsFolder, withIntermediateDirectories: true) + } + self.logDirectory = logsFolder + self.currentLogDate = Self.currentDateString() + } + + /// Sets configuration parameters for the logger. + /// - Parameters: + /// - logLevel: The NKLogLevel { disabled .. verbose } + private func setConfiguration(logLevel: NKLogLevel) { + self.logLevel = logLevel + } + + // MARK: - Public API + + public func writeLog(debug message: String) { + writeLog("[DEBUG] \(message)") + } + + public func writeLog(info message: String) { + writeLog("[INFO] \(message)") + } + + public func writeLog(warning message: String) { + writeLog("[WARNING] \(message)") + } + + public func writeLog(error message: String) { + writeLog("[ERROR] \(message)") + } + + public func writeLog(success message: String) { + writeLog("[SUCCESS] \(message)") + } + + public func writeLog(network message: String) { + writeLog("[NETWORK] \(message)") + } + + public func writeLog(start message: String) { + writeLog("[START] \(message)") + } + + public func writeLog(stop message: String) { + writeLog("[STOP] \(message)") + } + + /// Writes a tagged log message with a specific log level. + /// - Parameters: + /// - tag: A custom tag to classify the log message (e.g. "SYNC", "AUTH"). + /// - emoji: .info, .debug, .warning, .error, .success .. + /// - message: The log message content. + public func writeLog(tag: String, emoji: NKLogTagEmoji, message: String) { + guard !tag.isEmpty else { return } + + let taggedMessage = "[\(tag.uppercased())] \(message)" + writeLog(taggedMessage, emoji: emoji) + } + + /// Writes a log message with an optional typeTag to determine console emoji. + /// Emojis and keyword replacements (e.g. [SUCCESS] -> đŸŸĸ) are only applied to the console output. + /// The file output remains clean (no emoji or substitutions). + /// + /// - Parameters: + /// - message: The log message to record. + /// - emoji: Optional type to determine console emoji (e.g. [INFO], [ERROR]). + public func writeLog(_ message: String?, emoji: NKLogTagEmoji? = nil) { + guard logLevel != .disabled, let message = message else { return } + + let fileTimestamp = Self.stableTimestampString() + let consoleTimestamp = Self.localizedTimestampString() + let fileLine = "\(fileTimestamp) \(message)\n" + + // Determine which emoji to display in console + let emoji = emoji.map { emojiColored($0.rawValue) } ?? emojiColored(message) + + // Visual message with inline replacements + let visualMessage = message + .replacingOccurrences(of: "RESPONSE: SUCCESS", with: "đŸŸĸ") + .replacingOccurrences(of: "RESPONSE: ERROR", with: "🔴") + + // Build the console line with emoji + let consoleLine = "[NKLOG] [\(consoleTimestamp)] \(emoji)\(visualMessage)" + print(consoleLine) + + rotationQueue.sync { + self.checkForRotation() + } + + logQueue.async { + self.appendToLog(fileLine) + } + } + + private func emojiColored(_ message: String) -> String { + if message.contains("[ERROR]") { + return "🔴 " + } else if message.contains("[SUCCESS]") { + return "đŸŸĸ " + } else if message.contains("[WARNING]") { + return "🟡 " + } else if message.contains("[INFO]") { + return "đŸ”ĩ " + } else if message.contains("[DEBUG]") { + return "âšĒī¸ " + } else if message.contains("[NETWORK]") { + return "🌐 " + } else if message.contains("[START]") { + return "🚀 " + } else if message.contains("[STOP]") { + return "âšī¸ " + } else { + return "" + } + } + + // MARK: - Log Rotation + + private func checkForRotation() { + let today = Self.currentDateString() + guard today != currentLogDate else { return } + + rotateLog(for: currentLogDate) + currentLogDate = today + } + + private func rotateLog(for date: String) { + let currentPath = logDirectory.appendingPathComponent(logFileName) + let rotatedPath = logDirectory.appendingPathComponent("log-\(date).txt") + + do { + if fileManager.fileExists(atPath: currentPath.path) { + try fileManager.moveItem(at: currentPath, to: rotatedPath) + } + + // Create a new empty log file for today + try Data().write(to: currentPath) + + } catch { + print("Log rotation failed: \(error)") + } + } + + // MARK: - Log Writing + + private func appendToLog(_ message: String) { + let logPath = logDirectory.appendingPathComponent(logFileName) + + guard let data = message.data(using: .utf8) else { return } + + if fileManager.fileExists(atPath: logPath.path) { + if let handle = FileHandle(forWritingAtPath: logPath.path) { + handle.seekToEndOfFile() + handle.write(data) + handle.closeFile() + } + } else { + try? data.write(to: logPath) + } + } + + // MARK: - Cached DateFormatters + + /// Cached formatter for "yyyy-MM-dd". Uses current calendar, locale, and time zone. + private static let cachedCurrentDateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.calendar = Calendar.current + formatter.locale = Locale.current + formatter.timeZone = TimeZone.current + formatter.dateFormat = "yyyy-MM-dd" + return formatter + }() + + /// Cached formatter for "yyyy-MM-dd HH:mm:ss". Uses en_US_POSIX locale and Gregorian calendar for stable output. + private static let cachedStableTimestampFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .gregorian) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone.current + formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + return formatter + }() + + /// Cached formatter using `.short` dateStyle and `.medium` timeStyle with current calendar and locale. + private static let cachedLocalizedTimestampFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.calendar = Calendar.current + formatter.locale = Locale.current + formatter.timeZone = TimeZone.current + formatter.dateStyle = .short + formatter.timeStyle = .medium + return formatter + }() + + /// Returns a cached `DateFormatter` instance for the given format string. + /// Formatters are created on-demand and reused to improve performance. + private static func cachedFormatter(for format: String) -> DateFormatter { + return formatterAccessQueue.sync { + if let formatter = cachedDynamicFormatters[format] { + return formatter + } + + let formatter = DateFormatter() + formatter.dateFormat = format + formatter.calendar = Calendar(identifier: .gregorian) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + cachedDynamicFormatters[format] = formatter + return formatter + } + } + + /// Converts a `String` into a `Date` using a cached formatter for the specified format. + /// - Parameters: + /// - string: The date string to convert. + /// - format: The format pattern (e.g., "EEE, dd MMM y HH:mm:ss zzz"). + /// - Returns: A `Date` object if parsing succeeds; otherwise `nil`. + public func convertDate(_ string: String, format: String) -> Date? { + let formatter = Self.cachedFormatter(for: format) + return formatter.date(from: string) + } + + /// Converts a `Date` to a `String` using a cached formatter for the specified format. + /// - Parameters: + /// - date: The `Date` to format. + /// - format: The format string (e.g., "yyyy-MM-dd HH:mm:ss"). + /// - Returns: The formatted date string. + public func convertDate(_ date: Date, format: String) -> String { + let formatter = Self.cachedFormatter(for: format) + return formatter.string(from: date) + } + + /// Returns today's date string in "yyyy-MM-dd" format using a cached formatter. + private static func currentDateString() -> String { + return cachedCurrentDateFormatter.string(from: Date()) + } + + /// Returns a stable timestamp string in "yyyy-MM-dd HH:mm:ss" format using a cached formatter. + private static func stableTimestampString() -> String { + return cachedStableTimestampFormatter.string(from: Date()) + } + + /// Returns a localized timestamp string using short date and medium time styles. + private static func localizedTimestampString() -> String { + return cachedLocalizedTimestampFormatter.string(from: Date()) + } + } diff --git a/Sources/NextcloudKit/NKCommon.swift b/Sources/NextcloudKit/NKCommon.swift index 2c305d64..a2a56df4 100644 --- a/Sources/NextcloudKit/NKCommon.swift +++ b/Sources/NextcloudKit/NKCommon.swift @@ -128,45 +128,10 @@ public struct NKCommon: Sendable { #endif internal var internalTypeIdentifiers = ThreadSafeArray() - public var filenamePathLog: String = "" - public var levelLog: Int = 0 - public var copyLogToDocumentDirectory: Bool = false - public var printLog: Bool = true - - private var internalFilenameLog: String = "communication.log" - public var filenameLog: String { - get { - return internalFilenameLog - } - set(newVal) { - if !newVal.isEmpty { - internalFilenameLog = newVal - internalFilenameLog = internalPathLog + "/" + internalFilenameLog - } - } - } - - private var internalPathLog: String = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! - public var pathLog: String { - get { - return internalPathLog - } - set(newVal) { - var tempVal = newVal - if tempVal.last == "/" { - tempVal = String(tempVal.dropLast()) - } - if !tempVal.isEmpty { - internalPathLog = tempVal - filenamePathLog = internalPathLog + "/" + internalFilenameLog - } - } - } - // MARK: - Init init() { - filenamePathLog = internalPathLog + "/" + internalFilenameLog + } // MARK: - Type Identifier @@ -544,24 +509,6 @@ public struct NKCommon: Sendable { return serverUrl.asUrl } - public func convertDate(_ dateString: String, format: String) -> Date? { - if dateString.isEmpty { return nil } - let dateFormatter = DateFormatter() - - dateFormatter.locale = Locale(identifier: "en_US_POSIX") - dateFormatter.dateFormat = format - guard let date = dateFormatter.date(from: dateString) else { return nil } - return date - } - - func convertDate(_ date: Date, format: String) -> String? { - let dateFormatter = DateFormatter() - - dateFormatter.locale = Locale(identifier: "en_US_POSIX") - dateFormatter.dateFormat = format - return dateFormatter.string(from: date) - } - func findHeader(_ header: String, allHeaderFields: [AnyHashable: Any]?) -> String? { guard let allHeaderFields = allHeaderFields else { return nil } let keyValues = allHeaderFields.map { (String(describing: $0.key).lowercased(), String(describing: $0.value)) } @@ -617,91 +564,4 @@ public struct NKCommon: Sendable { } return nil } - - // MARK: - Log - - public func clearFileLog() { - FileManager.default.createFile(atPath: filenamePathLog, contents: nil, attributes: nil) - if copyLogToDocumentDirectory, let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first { - let filenameCopyToDocumentDirectory = path + "/" + filenameLog - FileManager.default.createFile(atPath: filenameCopyToDocumentDirectory, contents: nil, attributes: nil) - - } - } - - /// - /// Write a message with an "[DEBUG] " prefix to the log. - /// - public func writeLog(debug message: String) { - writeLog("[DEBUG] \(message)") - } - - /// - /// Write a message with an "[INFO] " prefix to the log. - /// - public func writeLog(info message: String) { - writeLog("[INFO] \(message)") - } - - /// - /// Write a message with an "[WARNING] " prefix to the log. - /// - public func writeLog(warning message: String) { - writeLog("[WARNING] \(message)") - } - - /// - /// Write a message with an "[ERROR] " prefix to the log. - /// - public func writeLog(error message: String) { - writeLog("[ERROR] \(message)") - } - - /// - /// Write an arbitrary string to the log. - /// - /// Does not write anything, should `text` be `nil`. - /// - public func writeLog(_ text: String?) { - guard let text = text else { - return - } - - guard let date = self.convertDate(Date(), format: "yyyy-MM-dd' 'HH:mm:ss") else { - return - } - - let textToWrite = "\(date) " + text + "\n" - - if printLog { - print(textToWrite) - } - - if levelLog > 0 { - logQueue.async(flags: .barrier) { - self.writeLogToDisk(filename: self.filenamePathLog, text: textToWrite) - - if self.copyLogToDocumentDirectory, let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first { - let filenameCopyToDocumentDirectory = path + "/" + self.filenameLog - self.writeLogToDisk(filename: filenameCopyToDocumentDirectory, text: textToWrite) - } - } - } - } - - private func writeLogToDisk(filename: String, text: String) { - guard let data = text.data(using: .utf8) else { - return - } - - if !FileManager.default.fileExists(atPath: filename) { - FileManager.default.createFile(atPath: filename, contents: nil, attributes: nil) - } - - if let fileHandle = FileHandle(forWritingAtPath: filename) { - fileHandle.seekToEndOfFile() - fileHandle.write(data) - fileHandle.closeFile() - } - } } diff --git a/Sources/NextcloudKit/NKDataFileXML.swift b/Sources/NextcloudKit/NKDataFileXML.swift index bafb99cd..d079d50f 100644 --- a/Sources/NextcloudKit/NKDataFileXML.swift +++ b/Sources/NextcloudKit/NKDataFileXML.swift @@ -308,7 +308,8 @@ public class NKDataFileXML: NSObject { let propstat = element["d:propstat"][0] - if let getlastmodified = propstat["d:prop", "d:getlastmodified"].text, let date = self.nkCommonInstance.convertDate(getlastmodified, format: "EEE, dd MMM y HH:mm:ss zzz") { + if let getlastmodified = propstat["d:prop", "d:getlastmodified"].text, + let date = getlastmodified.parsedDate(using: "EEE, dd MMM y HH:mm:ss zzz") { file.date = date } @@ -600,7 +601,8 @@ public class NKDataFileXML: NSObject { let propstat = element["d:propstat"][0] - if let getlastmodified = propstat["d:prop", "d:getlastmodified"].text, let date = self.nkCommonInstance.convertDate(getlastmodified, format: "EEE, dd MMM y HH:mm:ss zzz") { + if let getlastmodified = propstat["d:prop", "d:getlastmodified"].text, + let date = getlastmodified.parsedDate(using: "EEE, dd MMM y HH:mm:ss zzz") { file.date = date } @@ -678,7 +680,8 @@ public class NKDataFileXML: NSObject { item.actorType = value } - if let creationDateTime = element["d:propstat", "d:prop", "oc:creationDateTime"].text, let date = self.nkCommonInstance.convertDate(creationDateTime, format: "EEE, dd MMM y HH:mm:ss zzz") { + if let creationDateTime = element["d:propstat", "d:prop", "oc:creationDateTime"].text, + let date = creationDateTime.parsedDate(using: "EEE, dd MMM y HH:mm:ss zzz") { item.creationDateTime = date } diff --git a/Sources/NextcloudKit/NKError.swift b/Sources/NextcloudKit/NKError.swift index 829b59bd..a70a7ed2 100644 --- a/Sources/NextcloudKit/NKError.swift +++ b/Sources/NextcloudKit/NKError.swift @@ -34,14 +34,6 @@ extension OCSPath { public struct NKError: Error, Equatable { static let internalError = -9999 - // Chunk error - public static let chunkNoEnoughMemory = -9998 - public static let chunkMoveFile = -9997 - public static let chunkCreateFolder = -9996 - public static let chunkFilesNull = -9995 - public static let chunkFileNull = -9994 - public static let chunkFileUpload = -9993 - public let errorCode: Int public let errorDescription: String public let error: Error @@ -55,6 +47,16 @@ public struct NKError: Error, Equatable { public static let unauthorizedError = NKError(errorCode: 401, errorDescription: NSLocalizedString("_unauthorized_", value: "Unauthorized", comment: "")) public static let unavailableError = NKError(errorCode: 503, errorDescription: NSLocalizedString("_Unavailable_", value: "Unavailable", comment: "")) public static let forbiddenError = NKError(errorCode: 403, errorDescription: NSLocalizedString("_forbidden_", value: "Forbidden", comment: "")) + public static let cancelled = NKError(errorCode: -999, errorDescription: NSLocalizedString("_cancelled_", value: "Cancelled", comment: "")) + + public static let uploadIncomplete = NKError(errorCode: -9992, errorDescription: NSLocalizedString("_upload_incomplete_", value: "Upload incomplete", comment: "")) + + public static let errorChunkFileUpload = NKError(errorCode: -9993, errorDescription: NSLocalizedString("_upload_incomplete_", value: "Upload incomplete", comment: "")) + public static let errorChunkFileNull = NKError(errorCode: -9994, errorDescription: NSLocalizedString("_error_file_null_", value: "File not found", comment: "")) + public static let errorChunkFilesEmpty = NKError(errorCode: -9995, errorDescription: NSLocalizedString("_chunk_files_empty_", value: "Files not found", comment: "")) + public static let errorChunkCreateFolder = NKError(errorCode: -9996, errorDescription: NSLocalizedString("_error_create_folder_", value: "Create folder error", comment: "")) + public static let errorChunkMoveFile = NKError(errorCode: -9997, errorDescription: NSLocalizedString("_error_move_folder_", value: "Move file error", comment: "")) + public static let errorChunkNoEnoughMemory = NKError(errorCode: -9998, errorDescription: NSLocalizedString("_no_enough_memory_", value: "No enough memory", comment: "")) public static let success = NKError(errorCode: 0, errorDescription: "") diff --git a/Sources/NextcloudKit/NKInterceptor.swift b/Sources/NextcloudKit/NKInterceptor.swift index 394748b3..232a0945 100644 --- a/Sources/NextcloudKit/NKInterceptor.swift +++ b/Sources/NextcloudKit/NKInterceptor.swift @@ -13,34 +13,37 @@ final class NKInterceptor: RequestInterceptor, Sendable { } func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { - if let url: String = urlRequest.url?.absoluteString, - self.nkCommonInstance.levelLog > 0 { - debugPrint("[DEBUG] Interceptor request url: " + url) + // Log request URL in verbose mode + if NKLogFileManager.shared.logLevel == .verbose, + let url = urlRequest.url?.absoluteString { + nkLog(debug: "Interceptor request url: \(url)") } + // Skip check if explicitly disabled if let checkInterceptor = urlRequest.value(forHTTPHeaderField: nkCommonInstance.headerCheckInterceptor), checkInterceptor == "false" { return completion(.success(urlRequest)) } + // Check for special error states via group defaults if let account = urlRequest.value(forHTTPHeaderField: nkCommonInstance.headerAccount), let groupDefaults = UserDefaults(suiteName: nkCommonInstance.groupIdentifier) { - /// Unauthorized + if let array = groupDefaults.array(forKey: nkCommonInstance.groupDefaultsUnauthorized) as? [String], array.contains(account) { - self.nkCommonInstance.writeLog("[DEBUG] Unauthorized for account: \(account)") + nkLog(tag: "AUTH", message: "Unauthorized for account: \(account)") let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 401)) return completion(.failure(error)) - /// Unavailable + } else if let array = groupDefaults.array(forKey: nkCommonInstance.groupDefaultsUnavailable) as? [String], array.contains(account) { - self.nkCommonInstance.writeLog("[DEBUG] Unavailable for account: \(account)") + nkLog(tag: "SERVICE", message: "Unavailable for account: \(account)") let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 503)) return completion(.failure(error)) - /// ToS + } else if let array = groupDefaults.array(forKey: nkCommonInstance.groupDefaultsToS) as? [String], array.contains(account) { - self.nkCommonInstance.writeLog("[DEBUG] ToS for account: \(account)") + nkLog(tag: "TOS", message: "Terms of service error for account: \(account)") let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 403)) return completion(.failure(error)) } diff --git a/Sources/NextcloudKit/NKMonitor.swift b/Sources/NextcloudKit/NKMonitor.swift index ac650ef4..9b2b8388 100644 --- a/Sources/NextcloudKit/NKMonitor.swift +++ b/Sources/NextcloudKit/NKMonitor.swift @@ -7,20 +7,28 @@ import Alamofire final class NKMonitor: EventMonitor, Sendable { let nkCommonInstance: NKCommon + let queue = DispatchQueue(label: "com.nextcloudkit.monitor") init(nkCommonInstance: NKCommon) { self.nkCommonInstance = nkCommonInstance } func requestDidResume(_ request: Request) { - if self.nkCommonInstance.levelLog > 0 { - self.nkCommonInstance.writeLog("Network request started: \(request)") - if self.nkCommonInstance.levelLog > 1 { - let allHeaders = request.request.flatMap { $0.allHTTPHeaderFields.map { $0.description } } ?? "None" - let body = request.request.flatMap { $0.httpBody.map { String(decoding: $0, as: UTF8.self) } } ?? "None" - - self.nkCommonInstance.writeLog("Network request headers: \(allHeaders)") - self.nkCommonInstance.writeLog("Network request body: \(body)") + DispatchQueue.global(qos: .utility).async { + switch NKLogFileManager.shared.logLevel { + case .normal: + // General-purpose log: full Request description + nkLog(info: "Request started: \(request)") + case .verbose: + // Full dump: headers + body + let headers = request.request?.allHTTPHeaderFields?.description ?? "None" + let body = request.request?.httpBody.flatMap { String(data: $0, encoding: .utf8) } ?? "None" + + nkLog(debug: "Request started: \(request)") + nkLog(debug: "Headers: \(headers)") + nkLog(debug: "Body: \(body)") + default: + break } } } @@ -28,35 +36,44 @@ final class NKMonitor: EventMonitor, Sendable { func request(_ request: DataRequest, didParseResponse response: AFDataResponse) { nkCommonInstance.delegate?.request(request, didParseResponse: response) - // - // Server Error GroupDefaults - // + // Check for header and account error code tracking if let statusCode = response.response?.statusCode, - let headerCheckInterceptor = request.request?.allHTTPHeaderFields?[self.nkCommonInstance.headerCheckInterceptor], + let headerCheckInterceptor = request.request?.allHTTPHeaderFields?[nkCommonInstance.headerCheckInterceptor], headerCheckInterceptor.lowercased() == "true", - let account = request.request?.allHTTPHeaderFields?[self.nkCommonInstance.headerAccount] as? String { - self.nkCommonInstance.appendServerErrorAccount(account, errorCode: statusCode) + let account = request.request?.allHTTPHeaderFields?[nkCommonInstance.headerAccount] { + nkCommonInstance.appendServerErrorAccount(account, errorCode: statusCode) } - // - // LOG - // - guard let date = self.nkCommonInstance.convertDate(Date(), format: "yyyy-MM-dd' 'HH:mm:ss") else { return } - let responseResultString = String("\(response.result)") - let responseDebugDescription = String("\(response.debugDescription)") - let responseAllHeaderFields = String("\(String(describing: response.response?.allHeaderFields))") + DispatchQueue.global(qos: .utility).async { + switch NKLogFileManager.shared.logLevel { + case .normal: + let resultString = String(describing: response.result) - if self.nkCommonInstance.levelLog > 0 { - if self.nkCommonInstance.levelLog == 1 { if let request = response.request { - let requestString = "\(request)" - self.nkCommonInstance.writeLog("Network response request: " + requestString + ", result: " + responseResultString) + nkLog(info: "Network response request: \(request), result: \(resultString)") } else { - self.nkCommonInstance.writeLog("Network response result: " + responseResultString) + nkLog(info: "Network response result: \(resultString)") } - } else { - self.nkCommonInstance.writeLog("Network response result: \(date) " + responseDebugDescription) - self.nkCommonInstance.writeLog("Network response all headers: \(date) " + responseAllHeaderFields) + + case .compact: + if let method = request.request?.httpMethod, + let url = request.request?.url?.absoluteString, + let code = response.response?.statusCode { + + let responseStatus = (200..<300).contains(code) ? "RESPONSE: SUCCESS" : "RESPONSE: ERROR" + nkLog(network: "\(code) \(method) \(url) \(responseStatus)") + } + + case .verbose: + let debugDesc = String(describing: response) + let headerFields = String(describing: response.response?.allHeaderFields ?? [:]) + let date = Date().formatted(using: "yyyy-MM-dd' 'HH:mm:ss") + + nkLog(debug: "Network response result: \(date) " + debugDesc) + nkLog(debug: "Network response all headers: \(date) " + headerFields) + + default: + break } } } diff --git a/Sources/NextcloudKit/NextcloudKit+API.swift b/Sources/NextcloudKit/NextcloudKit+API.swift index d23230a5..d0864831 100644 --- a/Sources/NextcloudKit/NextcloudKit+API.swift +++ b/Sources/NextcloudKit/NextcloudKit+API.swift @@ -34,7 +34,7 @@ public extension NextcloudKit { func checkServer(serverUrl: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ responseData: AFDataResponse?, _ error: NKError) -> Void) { guard let url = serverUrl.asUrl else { return options.queue.async { completion(nil, .urlError) } } @@ -42,16 +42,10 @@ public extension NextcloudKit { unauthorizedSession.request(url, method: .head, encoding: URLEncoding.default).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } - switch response.result { - case .failure(let error): - let error = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completion(response, error) } - case .success: - options.queue.async { completion(response, .success) } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + let result = self.evaluateResponse(response) + options.queue.async { + completion(response, result) } } } @@ -75,15 +69,9 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } - switch response.result { - case .failure(let error): - let error = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completion(account, response, error) } - case .success: - options.queue.async { completion(account, response, .success) } + let result = self.evaluateResponse(response) + options.queue.async { + completion(account, response, result) } } } @@ -106,9 +94,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -180,9 +165,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -236,7 +218,7 @@ public extension NextcloudKit { etag: String? = nil, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { guard let nkSession = nkCommonInstance.getSession(account: account), var headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { return options.queue.async { completion(account, nil, .urlError) } @@ -249,11 +231,7 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } - + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -266,7 +244,7 @@ public extension NextcloudKit { func downloadPreviewAsync(url: URL, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, responseData: AFDataResponse?, error: NKError) { + options: NKRequestOptions = NKRequestOptions()) async -> (account: String, responseData: AFDataResponse?, error: NKError) { await withUnsafeContinuation({ continuation in NextcloudKit.shared.downloadPreview(url: url, account: account, options: options) { account, responseData, error in continuation.resume(returning: (account: account, responseData: responseData, error: error)) @@ -285,7 +263,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ width: Int, _ height: Int, _ etag: String?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ width: Int, _ height: Int, _ etag: String?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { let endpoint = "index.php/core/preview?fileId=\(fileId)&x=\(width)&y=\(height)&a=\(crop)&mode=\(cropMode)&forceIcon=\(forceIcon)&mimeFallback=\(mimeFallback)" guard let nkSession = nkCommonInstance.getSession(account: account), let url = nkCommonInstance.createStandardUrl(serverUrl: nkSession.urlBase, endpoint: endpoint, options: options), @@ -301,10 +279,7 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -319,7 +294,7 @@ public extension NextcloudKit { func downloadPreviewAsync(fileId: String, etag: String? = nil, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, width: Int, height: Int, etag: String?, responseData: AFDataResponse?, error: NKError) { + options: NKRequestOptions = NKRequestOptions()) async -> (account: String, width: Int, height: Int, etag: String?, responseData: AFDataResponse?, error: NKError) { await withUnsafeContinuation({ continuation in NextcloudKit.shared.downloadPreview(fileId: fileId, etag: etag, account: account, options: options) { account, width, height, etag, responseData, error in continuation.resume(returning: (account: account, width: width, height: height, etag: etag, responseData: responseData, error: error)) @@ -337,7 +312,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ width: Int, _ height: Int, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ width: Int, _ height: Int, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { let endpoint = "index.php/apps/files_trashbin/preview?fileId=\(fileId)&x=\(width)&y=\(height)&a=\(crop)&mode=\(cropMode)&forceIcon=\(forceIcon)&mimeFallback=\(mimeFallback)" guard let nkSession = nkCommonInstance.getSession(account: account), @@ -349,10 +324,7 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -371,7 +343,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ imageAvatar: UIImage?, _ imageOriginal: UIImage?, _ etag: String?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ imageAvatar: UIImage?, _ imageOriginal: UIImage?, _ etag: String?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { let endpoint = "index.php/avatar/\(user)/\(sizeImage)" guard let nkSession = nkCommonInstance.getSession(account: account), let url = nkCommonInstance.createStandardUrl(serverUrl: nkSession.urlBase, endpoint: endpoint, options: options), @@ -387,10 +359,7 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -474,7 +443,7 @@ public extension NextcloudKit { etag: String?, account: String, options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, imageAvatar: UIImage?, imageOriginal: UIImage?, etag: String?, responseData: AFDataResponse?, error: NKError) { + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, imageAvatar: UIImage?, imageOriginal: UIImage?, etag: String?, responseData: AFDataResponse?, error: NKError) { await withUnsafeContinuation { continuation in downloadAvatar(user: user, fileNameLocalPath: fileNameLocalPath, @@ -493,7 +462,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { guard let url = serverUrl.asUrl, let nkSession = nkCommonInstance.getSession(account: account), let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { @@ -503,10 +472,7 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -538,9 +504,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -585,9 +548,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -653,7 +613,7 @@ public extension NextcloudKit { func getCapabilities(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { let endpoint = "ocs/v1.php/cloud/capabilities" guard let nkSession = nkCommonInstance.getSession(account: account), let url = nkCommonInstance.createStandardUrl(serverUrl: nkSession.urlBase, endpoint: endpoint, options: options), @@ -664,10 +624,7 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -680,7 +637,7 @@ public extension NextcloudKit { func getCapabilitiesAsync(account: String, options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, responseData: AFDataResponse?, error: NKError) { + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, responseData: AFDataResponse?, error: NKError) { await withUnsafeContinuation { continuation in getCapabilities(account: account, options: options, @@ -715,9 +672,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -753,9 +707,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -809,9 +760,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -824,10 +772,9 @@ public extension NextcloudKit { activity.app = subJson["app"].stringValue activity.idActivity = subJson["activity_id"].intValue - if let datetime = subJson["datetime"].string { - if let date = self.nkCommonInstance.convertDate(datetime, format: "yyyy-MM-dd'T'HH:mm:ssZZZZZ") { - activity.date = date - } + if let datetime = subJson["datetime"].string, + let date = datetime.parsedDate(using: "yyyy-MM-dd'T'HH:mm:ssZZZZZ") { + activity.date = date } activity.icon = subJson["icon"].stringValue activity.link = subJson["link"].stringValue @@ -885,9 +832,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -906,10 +850,9 @@ public extension NextcloudKit { } catch {} } notification.app = subJson["app"].stringValue - if let datetime = subJson["datetime"].string { - if let date = self.nkCommonInstance.convertDate(datetime, format: "yyyy-MM-dd'T'HH:mm:ssZZZZZ") { - notification.date = date as Date - } + if let datetime = subJson["datetime"].string, + let date = datetime.parsedDate(using: "yyyy-MM-dd'T'HH:mm:ssZZZZZ") { + notification.date = date } notification.icon = subJson["icon"].string notification.idNotification = subJson["notification_id"].intValue @@ -947,7 +890,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { guard let nkSession = nkCommonInstance.getSession(account: account), let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { return options.queue.async { completion(account, nil, .urlError) } @@ -967,10 +910,7 @@ public extension NextcloudKit { nkSession.sessionData.request(urlRequest, method: method, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -1003,9 +943,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -1025,7 +962,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { let endpoint = "ocs/v2.php/apps/security_guard/diagnostics" /// options.contentType = "application/json" @@ -1046,10 +983,7 @@ public extension NextcloudKit { nkSession.sessionData.request(urlRequest, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -1065,7 +999,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } - ) async -> (responseData: AFDataResponse?, error: NKError) { + ) async -> (responseData: AFDataResponse?, error: NKError) { await withUnsafeContinuation { continuation in sendClientDiagnosticsRemoteOperation( data: data, diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index 2d795052..cd43104f 100644 --- a/Sources/NextcloudKit/NextcloudKit+Assistant.swift +++ b/Sources/NextcloudKit/NextcloudKit+Assistant.swift @@ -22,9 +22,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -63,9 +60,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -100,9 +94,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -137,9 +128,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -174,9 +162,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift index 57e20c58..ec68ca44 100644 --- a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift +++ b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift @@ -23,9 +23,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -37,7 +34,7 @@ public extension NextcloudKit { if 200..<300 ~= statusCode { let dict = TaskTypes.deserialize(from: data) let result = dict?.types.map({$0}) - var filteredResult = result? + let filteredResult = result? .filter({ $0.inputShape?.input?.type == supportedTaskType && $0.outputShape?.output?.type == supportedTaskType }) .sorted(by: {$0.id! < $1.id!}) options.queue.async { completion(account, filteredResult, response, .success) } @@ -68,9 +65,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -105,9 +99,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -142,9 +133,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit+Comments.swift b/Sources/NextcloudKit/NextcloudKit+Comments.swift index 65149708..05c35d7f 100644 --- a/Sources/NextcloudKit/NextcloudKit+Comments.swift +++ b/Sources/NextcloudKit/NextcloudKit+Comments.swift @@ -36,10 +36,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } - switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -60,7 +56,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { /// options.contentType = "application/json" /// @@ -85,17 +81,11 @@ public extension NextcloudKit { nkSession.sessionData.request(urlRequest, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + let result = self.evaluateResponse(response) - switch response.result { - case .failure(let error): - let error = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completion(account, response, error) } - case .success: - options.queue.async { completion(account, response, .success) } + options.queue.async { + completion(account, response, result) } } } @@ -106,7 +96,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { /// options.contentType = "application/xml" /// @@ -132,17 +122,11 @@ public extension NextcloudKit { nkSession.sessionData.request(urlRequest, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + let result = self.evaluateResponse(response) - switch response.result { - case .failure(let error): - let error = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completion(account, response, error) } - case .success: - options.queue.async { completion(account, response, .success) } + options.queue.async { + completion(account, response, result) } } } @@ -152,7 +136,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { guard let nkSession = nkCommonInstance.getSession(account: account), let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { return options.queue.async { completion(account, nil, .urlError) } @@ -165,17 +149,11 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .delete, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + let result = self.evaluateResponse(response) - switch response.result { - case .failure(let error): - let error = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completion(account, response, error) } - case .success: - options.queue.async { completion(account, response, .success) } + options.queue.async { + completion(account, response, result) } } } @@ -184,7 +162,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { /// options.contentType = "application/xml" /// @@ -210,17 +188,11 @@ public extension NextcloudKit { nkSession.sessionData.request(urlRequest, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + let result = self.evaluateResponse(response) - switch response.result { - case .failure(let error): - let error = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completion(account, response, error) } - case .success: - options.queue.async { completion(account, response, .success) } + options.queue.async { + completion(account, response, result) } } } diff --git a/Sources/NextcloudKit/NextcloudKit+Dashboard.swift b/Sources/NextcloudKit/NextcloudKit+Dashboard.swift index 516a6bd5..098354e3 100644 --- a/Sources/NextcloudKit/NextcloudKit+Dashboard.swift +++ b/Sources/NextcloudKit/NextcloudKit+Dashboard.swift @@ -23,9 +23,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .success(let jsonData): let json = JSON(jsonData) @@ -62,9 +59,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .success(let jsonData): let json = JSON(jsonData) diff --git a/Sources/NextcloudKit/NextcloudKit+Download.swift b/Sources/NextcloudKit/NextcloudKit+Download.swift index ae08bc5a..07707052 100644 --- a/Sources/NextcloudKit/NextcloudKit+Download.swift +++ b/Sources/NextcloudKit/NextcloudKit+Download.swift @@ -14,7 +14,7 @@ public extension NextcloudKit { requestHandler: @escaping (_ request: DownloadRequest) -> Void = { _ in }, taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, progressHandler: @escaping (_ progress: Progress) -> Void = { _ in }, - completionHandler: @escaping (_ account: String, _ etag: String?, _ date: Date?, _ lenght: Int64, _ responseData: AFDownloadResponse?, _ afError: AFError?, _ nKError: NKError) -> Void) { + completionHandler: @escaping (_ account: String, _ etag: String?, _ date: Date?, _ lenght: Int64, _ responseData: AFDownloadResponse?, _ afError: AFError?, _ nKError: NKError) -> Void) { var convertible: URLConvertible? if serverUrlFileName is URL { convertible = serverUrlFileName as? URLConvertible @@ -38,7 +38,7 @@ public extension NextcloudKit { options.queue.async { taskHandler(task) } } .downloadProgress { progress in options.queue.async { progressHandler(progress) } - } .response(queue: self.nkCommonInstance.backgroundQueue) { response in + } .responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let resultError = NKError(error: error, afResponse: response, responseData: nil) @@ -59,8 +59,8 @@ public extension NextcloudKit { if etag != nil { etag = etag?.replacingOccurrences(of: "\"", with: "") } - if let dateString = self.nkCommonInstance.findHeader("Date", allHeaderFields: response.response?.allHeaderFields) { - date = self.nkCommonInstance.convertDate(dateString, format: "EEE, dd MMM y HH:mm:ss zzz") + if let dateRaw = self.nkCommonInstance.findHeader("Date", allHeaderFields: response.response?.allHeaderFields) { + date = dateRaw.parsedDate(using: "yyyy-MM-dd HH:mm:ss") } options.queue.async { completionHandler(account, etag, date, length, response, nil, .success) } diff --git a/Sources/NextcloudKit/NextcloudKit+E2EE.swift b/Sources/NextcloudKit/NextcloudKit+E2EE.swift index 434ee018..913ec120 100644 --- a/Sources/NextcloudKit/NextcloudKit+E2EE.swift +++ b/Sources/NextcloudKit/NextcloudKit+E2EE.swift @@ -29,9 +29,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -92,9 +89,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -151,9 +145,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -206,9 +197,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -271,9 +259,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -325,9 +310,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -364,9 +346,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -405,9 +384,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -447,9 +423,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -470,7 +443,7 @@ public extension NextcloudKit { func deleteE2EECertificate(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { var version = "v1" if let optionsVesion = options.version { version = optionsVesion @@ -484,10 +457,7 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .delete, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -501,7 +471,7 @@ public extension NextcloudKit { func deleteE2EEPrivateKey(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { var version = "v1" if let optionsVesion = options.version { version = optionsVesion @@ -516,10 +486,7 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .delete, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit+FilesLock.swift b/Sources/NextcloudKit/NextcloudKit+FilesLock.swift index 54d2d493..b7b75be1 100644 --- a/Sources/NextcloudKit/NextcloudKit+FilesLock.swift +++ b/Sources/NextcloudKit/NextcloudKit+FilesLock.swift @@ -12,7 +12,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { guard let url = serverUrlFileName.encodedToUrl else { return options.queue.async { completion(account, nil, .urlError) } @@ -27,10 +27,7 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: method, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit+Groupfolders.swift b/Sources/NextcloudKit/NextcloudKit+Groupfolders.swift index 8641ad10..3620a582 100644 --- a/Sources/NextcloudKit/NextcloudKit+Groupfolders.swift +++ b/Sources/NextcloudKit/NextcloudKit+Groupfolders.swift @@ -23,9 +23,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit+Hovercard.swift b/Sources/NextcloudKit/NextcloudKit+Hovercard.swift index 2e192c91..8a993854 100644 --- a/Sources/NextcloudKit/NextcloudKit+Hovercard.swift +++ b/Sources/NextcloudKit/NextcloudKit+Hovercard.swift @@ -23,9 +23,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit+Livephoto.swift b/Sources/NextcloudKit/NextcloudKit+Livephoto.swift index 2eda50d7..08e7e641 100644 --- a/Sources/NextcloudKit/NextcloudKit+Livephoto.swift +++ b/Sources/NextcloudKit/NextcloudKit+Livephoto.swift @@ -11,7 +11,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { guard let url = serverUrlfileNamePath.encodedToUrl, let nkSession = nkCommonInstance.getSession(account: account) else { return options.queue.async { completion(account, nil, .urlError) } @@ -35,10 +35,7 @@ public extension NextcloudKit { nkSession.sessionData.request(urlRequest, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -52,7 +49,7 @@ public extension NextcloudKit { func setLivephotoAsync(serverUrlfileNamePath: String, livePhotoFile: String, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, responseData: AFDataResponse?, error: NKError) { + options: NKRequestOptions = NKRequestOptions()) async -> (account: String, responseData: AFDataResponse?, error: NKError) { await withUnsafeContinuation({ continuation in NextcloudKit.shared.setLivephoto(serverUrlfileNamePath: serverUrlfileNamePath, livePhotoFile: livePhotoFile, account: account, options: options) { account, responseData, error in continuation.resume(returning: (account: account, responseData: responseData, error: error)) diff --git a/Sources/NextcloudKit/NextcloudKit+Logging.swift b/Sources/NextcloudKit/NextcloudKit+Logging.swift new file mode 100644 index 00000000..2371b31f --- /dev/null +++ b/Sources/NextcloudKit/NextcloudKit+Logging.swift @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +public extension NextcloudKit { + /// Shared logger accessible via NextcloudKit.logger + static var logger: NKLogFileManager { + return NKLogFileManager.shared + } + + /// Configure the shared logger from NextcloudKit + static func configureLogger(logLevel: NKLogLevel = .normal) { + NKLogFileManager.configure(logLevel: logLevel) + } +} diff --git a/Sources/NextcloudKit/NextcloudKit+Login.swift b/Sources/NextcloudKit/NextcloudKit+Login.swift index c203689b..20deb2e6 100644 --- a/Sources/NextcloudKit/NextcloudKit+Login.swift +++ b/Sources/NextcloudKit/NextcloudKit+Login.swift @@ -14,7 +14,7 @@ public extension NextcloudKit { userAgent: String? = nil, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ token: String?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ token: String?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { let endpoint = "ocs/v2.php/core/getapppassword" guard let url = self.nkCommonInstance.createStandardUrl(serverUrl: url, endpoint: endpoint, options: options) else { return options.queue.async { completion(nil, nil, .urlError) } @@ -35,21 +35,14 @@ public extension NextcloudKit { unauthorizedSession.request(urlRequest).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) options.queue.async { completion(nil, response, error) } - case .success(let xmlData): - if let data = xmlData { - let apppassword = NKDataFileXML(nkCommonInstance: self.nkCommonInstance).convertDataAppPassword(data: data) - options.queue.async { completion(apppassword, response, .success) } - } else { - options.queue.async { completion(nil, response, .xmlError) } - } + case .success(let data): + let apppassword = NKDataFileXML(nkCommonInstance: self.nkCommonInstance).convertDataAppPassword(data: data) + options.queue.async { completion(apppassword, response, .success) } } } } @@ -61,7 +54,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ responseData: AFDataResponse?, _ error: NKError) -> Void) { let endpoint = "ocs/v2.php/core/apppassword" guard let nkSession = nkCommonInstance.getSession(account: account), let url = self.nkCommonInstance.createStandardUrl(serverUrl: serverUrl, endpoint: endpoint, options: options) else { @@ -83,10 +76,7 @@ public extension NextcloudKit { nkSession.sessionData.request(urlRequest).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -149,9 +139,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -186,9 +173,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit+NCText.swift b/Sources/NextcloudKit/NextcloudKit+NCText.swift index 4f7f0b2d..a2369971 100644 --- a/Sources/NextcloudKit/NextcloudKit+NCText.swift +++ b/Sources/NextcloudKit/NextcloudKit+NCText.swift @@ -24,9 +24,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -106,9 +103,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -137,9 +131,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -199,9 +190,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit+PushNotification.swift b/Sources/NextcloudKit/NextcloudKit+PushNotification.swift index 1052157e..02fcf2fb 100644 --- a/Sources/NextcloudKit/NextcloudKit+PushNotification.swift +++ b/Sources/NextcloudKit/NextcloudKit+PushNotification.swift @@ -31,9 +31,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -57,7 +54,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { let endpoint = "ocs/v2.php/apps/notifications/api/v2/push" guard let nkSession = nkCommonInstance.getSession(account: account), let url = nkCommonInstance.createStandardUrl(serverUrl: nkSession.urlBase, endpoint: endpoint, options: options), @@ -68,10 +65,7 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .delete, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -90,7 +84,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { let endpoint = "devices?format=json" guard let nkSession = nkCommonInstance.getSession(account: account), let url = self.nkCommonInstance.createStandardUrl(serverUrl: proxyServerUrl, endpoint: endpoint, options: options), @@ -108,10 +102,7 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -129,7 +120,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { let endpoint = "devices" guard let nkSession = nkCommonInstance.getSession(account: account), let url = self.nkCommonInstance.createStandardUrl(serverUrl: proxyServerUrl, endpoint: endpoint, options: options), @@ -146,10 +137,7 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .delete, parameters: parameters, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit+RecommendedFiles.swift b/Sources/NextcloudKit/NextcloudKit+RecommendedFiles.swift index 688c28a6..7f1235d7 100644 --- a/Sources/NextcloudKit/NextcloudKit+RecommendedFiles.swift +++ b/Sources/NextcloudKit/NextcloudKit+RecommendedFiles.swift @@ -26,9 +26,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .success(let data): if let xmlString = String(data: data, encoding: .utf8) { diff --git a/Sources/NextcloudKit/NextcloudKit+Richdocuments.swift b/Sources/NextcloudKit/NextcloudKit+Richdocuments.swift index 1d7445ba..b71fff46 100644 --- a/Sources/NextcloudKit/NextcloudKit+Richdocuments.swift +++ b/Sources/NextcloudKit/NextcloudKit+Richdocuments.swift @@ -24,9 +24,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -59,9 +56,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -118,9 +112,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -154,9 +145,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit+Search.swift b/Sources/NextcloudKit/NextcloudKit+Search.swift index 64b8e8df..55fecc86 100644 --- a/Sources/NextcloudKit/NextcloudKit+Search.swift +++ b/Sources/NextcloudKit/NextcloudKit+Search.swift @@ -44,9 +44,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .success(let jsonData): let json = JSON(jsonData) @@ -135,9 +132,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .success(let jsonData): let json = JSON(jsonData) diff --git a/Sources/NextcloudKit/NextcloudKit+Share.swift b/Sources/NextcloudKit/NextcloudKit+Share.swift index 3e821417..3774cbbd 100644 --- a/Sources/NextcloudKit/NextcloudKit+Share.swift +++ b/Sources/NextcloudKit/NextcloudKit+Share.swift @@ -71,9 +71,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -134,9 +131,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -304,9 +298,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -391,9 +382,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -417,7 +405,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { let endpoint = "ocs/v2.php/apps/files_sharing/api/v1/shares/\(idShare)" guard let nkSession = nkCommonInstance.getSession(account: account), let url = nkCommonInstance.createStandardUrl(serverUrl: nkSession.urlBase, endpoint: endpoint, options: options), @@ -428,16 +416,11 @@ public extension NextcloudKit { nkSession.sessionData.request(url, method: .delete, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } - switch response.result { - case .failure(let error): - let error = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completion(account, response, error) } - case .success: - options.queue.async { completion(account, response, .success) } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + let result = self.evaluateResponse(response) + + options.queue.async { + completion(account, response, result) } } } @@ -452,7 +435,7 @@ public extension NextcloudKit { share.canEdit = json["can_edit"].boolValue share.displaynameFileOwner = json["displayname_file_owner"].stringValue share.displaynameOwner = json["displayname_owner"].stringValue - if let expiration = json["expiration"].string, let date = self.nkCommonInstance.convertDate(expiration, format: "YYYY-MM-dd HH:mm:ss") { + if let expiration = json["expiration"].string, let date = expiration.parsedDate(using: "YYYY-MM-dd HH:mm:ss") { share.expirationDate = date as NSDate } share.fileParent = json["file_parent"].intValue diff --git a/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift b/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift index 1aaa19ab..4ac538ef 100644 --- a/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift +++ b/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift @@ -28,10 +28,6 @@ public extension NextcloudKit { .request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)) .validate(statusCode: 200..<300) .responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } - switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -96,11 +92,7 @@ public extension NextcloudKit { .sessionData .request(url, method: .delete, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)) .validate(statusCode: 200..<300) - .response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } - + .responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -138,11 +130,7 @@ public extension NextcloudKit { .sessionData .request(urlRequest, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)) .validate(statusCode: 200..<300) - .response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } - + .responseData(queue: self.nkCommonInstance.backgroundQueue) { response in switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit+TermsOfService.swift b/Sources/NextcloudKit/NextcloudKit+TermsOfService.swift index 862e5c00..aa70eedc 100644 --- a/Sources/NextcloudKit/NextcloudKit+TermsOfService.swift +++ b/Sources/NextcloudKit/NextcloudKit+TermsOfService.swift @@ -7,6 +7,10 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// - Parameters: + /// - account: The account to query. + /// - options: Optional request options (defaults to standard). + /// - Returns: Tuple with NKError and optional NKTermsOfService. func getTermsOfService(account: String, options: NKRequestOptions = NKRequestOptions(), request: @escaping (DataRequest?) -> Void = { _ in }, @@ -23,9 +27,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .success(let jsonData): let tos = NKTermsOfService() @@ -46,6 +47,34 @@ public extension NextcloudKit { options.queue.async { request(tosRequest) } } + /// Async wrapper for `getTermsOfService(account:options:...)` + /// - Parameters: + /// - account: The account to query. + /// - options: Optional request options (defaults to standard). + /// - Returns: Tuple with NKError and optional NKTermsOfService. + func getTermsOfServiceAsync(account: String, + options: NKRequestOptions = NKRequestOptions(), + request: ((DataRequest?) -> Void)? = nil, + taskHandler: ((URLSessionTask) -> Void)? = nil + ) async -> (error: NKError, tos: NKTermsOfService?) { + await withCheckedContinuation { continuation in + self.getTermsOfService( + account: account, + options: options, + request: request ?? { _ in }, + taskHandler: taskHandler ?? { _ in } + ) { _, tos, _, error in + continuation.resume(returning: (error, tos)) + } + } + } + + /// - Parameters: + /// - termId: The ID of the ToS to sign. + /// - account: The user account. + /// - options: Optional request options. + /// - taskHandler: Optional URLSession task handler. + /// - Returns: NKError and AFDataResponse? func signTermsOfService(termId: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -74,9 +103,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -86,4 +112,28 @@ public extension NextcloudKit { } } } + + /// Async wrapper for `signTermsOfService` + /// - Parameters: + /// - termId: The ID of the ToS to sign. + /// - account: The user account. + /// - options: Optional request options. + /// - taskHandler: Optional URLSession task handler. + /// - Returns: NKError and AFDataResponse? + func signTermsOfServiceAsync(termId: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: ((URLSessionTask) -> Void)? = nil + ) async -> (error: NKError, response: AFDataResponse?) { + await withCheckedContinuation { continuation in + self.signTermsOfService( + termId: termId, + account: account, + options: options, + taskHandler: taskHandler ?? { _ in } + ) { _, responseData, error in + continuation.resume(returning: (error, responseData)) + } + } + } } diff --git a/Sources/NextcloudKit/NextcloudKit+Upload.swift b/Sources/NextcloudKit/NextcloudKit+Upload.swift index 3d3e2f69..659d20bc 100644 --- a/Sources/NextcloudKit/NextcloudKit+Upload.swift +++ b/Sources/NextcloudKit/NextcloudKit+Upload.swift @@ -17,9 +17,11 @@ public extension NextcloudKit { requestHandler: @escaping (_ request: UploadRequest) -> Void = { _ in }, taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, progressHandler: @escaping (_ progress: Progress) -> Void = { _ in }, - completionHandler: @escaping (_ account: String, _ ocId: String?, _ etag: String?, _ date: Date?, _ size: Int64, _ responseData: AFDataResponse?, _ afError: AFError?, _ nkError: NKError) -> Void) { + completionHandler: @escaping (_ account: String, _ ocId: String?, _ etag: String?, _ date: Date?, _ size: Int64, _ responseData: AFDataResponse?, _ nkError: NKError) -> Void) { var convertible: URLConvertible? - var size: Int64 = 0 + var uploadedSize: Int64 = 0 + var uploadCompleted = false + if serverUrlFileName is URL { convertible = serverUrlFileName as? URLConvertible } else if serverUrlFileName is String || serverUrlFileName is NSString { @@ -28,7 +30,7 @@ public extension NextcloudKit { guard let url = convertible, let nkSession = nkCommonInstance.getSession(account: account), var headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { - return options.queue.async { completionHandler(account, nil, nil, nil, 0, nil, nil, .urlError) } + return options.queue.async { completionHandler(account, nil, nil, nil, 0, nil, .urlError) } } let fileNameLocalPathUrl = URL(fileURLWithPath: fileNameLocalPath) // Epoch of linux do not permitted negativ value @@ -47,37 +49,39 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription options.queue.async { taskHandler(task) } }) .uploadProgress { progress in + uploadedSize = progress.totalUnitCount + uploadCompleted = progress.fractionCompleted == 1.0 options.queue.async { progressHandler(progress) } - size = progress.totalUnitCount - } .response(queue: self.nkCommonInstance.backgroundQueue) { response in - switch response.result { - case .failure(let error): - let resultError = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completionHandler(account, nil, nil, nil, 0, response, error, resultError) } - case .success: - var ocId: String?, etag: String? - if self.nkCommonInstance.findHeader("oc-fileid", allHeaderFields: response.response?.allHeaderFields) != nil { - ocId = self.nkCommonInstance.findHeader("oc-fileid", allHeaderFields: response.response?.allHeaderFields) - } else if self.nkCommonInstance.findHeader("fileid", allHeaderFields: response.response?.allHeaderFields) != nil { - ocId = self.nkCommonInstance.findHeader("fileid", allHeaderFields: response.response?.allHeaderFields) - } - if self.nkCommonInstance.findHeader("oc-etag", allHeaderFields: response.response?.allHeaderFields) != nil { - etag = self.nkCommonInstance.findHeader("oc-etag", allHeaderFields: response.response?.allHeaderFields) - } else if self.nkCommonInstance.findHeader("etag", allHeaderFields: response.response?.allHeaderFields) != nil { - etag = self.nkCommonInstance.findHeader("etag", allHeaderFields: response.response?.allHeaderFields) - } - if etag != nil { - etag = etag?.replacingOccurrences(of: "\"", with: "") - } - if let dateString = self.nkCommonInstance.findHeader("date", allHeaderFields: response.response?.allHeaderFields) { - if let date = self.nkCommonInstance.convertDate(dateString, format: "EEE, dd MMM y HH:mm:ss zzz") { - options.queue.async { completionHandler(account, ocId, etag, date, size, response, nil, .success) } - } else { - options.queue.async { completionHandler(account, nil, nil, nil, 0, response, nil, .invalidDate) } - } - } else { - options.queue.async { completionHandler(account, nil, nil, nil, 0, response, nil, .invalidDate) } - } + } .responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + var ocId: String?, etag: String?, date: Date? + var result: NKError + + if self.nkCommonInstance.findHeader("oc-fileid", allHeaderFields: response.response?.allHeaderFields) != nil { + ocId = self.nkCommonInstance.findHeader("oc-fileid", allHeaderFields: response.response?.allHeaderFields) + } else if self.nkCommonInstance.findHeader("fileid", allHeaderFields: response.response?.allHeaderFields) != nil { + ocId = self.nkCommonInstance.findHeader("fileid", allHeaderFields: response.response?.allHeaderFields) + } + if self.nkCommonInstance.findHeader("oc-etag", allHeaderFields: response.response?.allHeaderFields) != nil { + etag = self.nkCommonInstance.findHeader("oc-etag", allHeaderFields: response.response?.allHeaderFields) + } else if self.nkCommonInstance.findHeader("etag", allHeaderFields: response.response?.allHeaderFields) != nil { + etag = self.nkCommonInstance.findHeader("etag", allHeaderFields: response.response?.allHeaderFields) + } + if etag != nil { + etag = etag?.replacingOccurrences(of: "\"", with: "") + } + if let dateRaw = self.nkCommonInstance.findHeader("date", allHeaderFields: response.response?.allHeaderFields) { + date = dateRaw.parsedDate(using: "EEE, dd MMM y HH:mm:ss zzz") + } + + if !uploadCompleted { + nkLog(error: "Upload incomplete: only \(uploadedSize) bytes sent.") + result = .uploadIncomplete + } else { + result = self.evaluateResponse(response) + } + + options.queue.async { + completionHandler(account, ocId, etag, date, uploadedSize, response, result) } } @@ -114,10 +118,10 @@ public extension NextcloudKit { taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, progressHandler: @escaping (_ totalBytesExpected: Int64, _ totalBytes: Int64, _ fractionCompleted: Double) -> Void = { _, _, _ in }, uploaded: @escaping (_ fileChunk: (fileName: String, size: Int64)) -> Void = { _ in }, - completion: @escaping (_ account: String, _ filesChunk: [(fileName: String, size: Int64)]?, _ file: NKFile?, _ afError: AFError?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ filesChunk: [(fileName: String, size: Int64)]?, _ file: NKFile?, _ error: NKError) -> Void) { guard let nkSession = nkCommonInstance.getSession(account: account) else { - return completion(account, nil, nil, nil, .urlError) + return completion(account, nil, nil, .urlError) } let fileNameLocalSize = self.nkCommonInstance.getFileSize(filePath: directory + "/" + fileName) let serverUrlChunkFolder = nkSession.urlBase + "/" + nkSession.dav + "/uploads/" + nkSession.userId + "/" + chunkFolder @@ -151,8 +155,7 @@ public extension NextcloudKit { #if os(visionOS) || os(iOS) if freeDisk < fileNameLocalSize * 4 { // It seems there is not enough space to send the file - let error = NKError(errorCode: NKError.chunkNoEnoughMemory, errorDescription: "_chunk_enough_memory_") - return completion(account, nil, nil, nil, error) + return completion(account, nil, nil, .errorChunkNoEnoughMemory) } #endif @@ -172,10 +175,9 @@ public extension NextcloudKit { createFolder { error in guard error == .success else { - return completion(account, nil, nil, nil, NKError(errorCode: NKError.chunkCreateFolder, errorDescription: error.errorDescription)) + return completion(account, nil, nil, .errorChunkCreateFolder) } var uploadNKError = NKError() - var uploadAFError: AFError? let outputDirectory = fileChunksOutputDirectory ?? directory self.nkCommonInstance.chunkedFile(inputDirectory: directory, outputDirectory: outputDirectory, fileName: fileName, chunkSize: chunkSize, filesChunk: filesChunk) { num in @@ -185,8 +187,7 @@ public extension NextcloudKit { } completion: { filesChunk in if filesChunk.isEmpty { // The file for sending could not be created - let error = NKError(errorCode: NKError.chunkFilesNull, errorDescription: "_chunk_files_null_") - return completion(account, nil, nil, nil, error) + return completion(account, nil, nil, .errorChunkFilesEmpty) } var filesChunkOutput = filesChunk start(filesChunkOutput) @@ -197,8 +198,7 @@ public extension NextcloudKit { let fileSize = self.nkCommonInstance.getFileSize(filePath: fileNameLocalPath) if fileSize == 0 { // The file could not be sent - let error = NKError(errorCode: NKError.chunkFileNull, errorDescription: "_chunk_file_null_") - return completion(account, nil, nil, .explicitlyCancelled, error) + return completion(account, nil, nil, .errorChunkFileNull) } let semaphore = DispatchSemaphore(value: 0) self.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, account: account, options: options, requestHandler: { request in @@ -210,12 +210,11 @@ public extension NextcloudKit { let totalBytes = fileChunk.size let fractionCompleted = Double(totalBytes) / Double(totalBytesExpected) progressHandler(totalBytesExpected, totalBytes, fractionCompleted) - }) { _, _, _, _, _, _, afError, error in + }) { _, _, _, _, _, _, error in if error == .success { filesChunkOutput.removeFirst() uploaded(fileChunk) } - uploadAFError = afError uploadNKError = error semaphore.signal() } @@ -227,7 +226,7 @@ public extension NextcloudKit { } guard uploadNKError == .success else { - return completion(account, filesChunkOutput, nil, uploadAFError, NKError(errorCode: NKError.chunkFileUpload, errorDescription: uploadNKError.errorDescription)) + return completion(account, filesChunkOutput, nil, .errorChunkFileUpload) } // Assemble the chunks @@ -249,13 +248,13 @@ public extension NextcloudKit { self.moveFileOrFolder(serverUrlFileNameSource: serverUrlFileNameSource, serverUrlFileNameDestination: serverUrlFileName, overwrite: true, account: account, options: options) { _, _, error in guard error == .success else { - return completion(account, filesChunkOutput, nil, nil, NKError(errorCode: NKError.chunkMoveFile, errorDescription: error.errorDescription)) + return completion(account, filesChunkOutput, nil,.errorChunkMoveFile) } self.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", account: account, options: NKRequestOptions(queue: self.nkCommonInstance.backgroundQueue)) { _, files, _, error in guard error == .success, let file = files?.first else { - return completion(account, filesChunkOutput, nil, nil, NKError(errorCode: NKError.chunkMoveFile, errorDescription: error.errorDescription)) + return completion(account, filesChunkOutput, nil, .errorChunkMoveFile) } - return completion(account, filesChunkOutput, file, nil, error) + return completion(account, filesChunkOutput, file, error) } } } diff --git a/Sources/NextcloudKit/NextcloudKit+UserStatus.swift b/Sources/NextcloudKit/NextcloudKit+UserStatus.swift index 8dce2239..de772e5a 100644 --- a/Sources/NextcloudKit/NextcloudKit+UserStatus.swift +++ b/Sources/NextcloudKit/NextcloudKit+UserStatus.swift @@ -26,9 +26,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -91,9 +88,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -133,9 +127,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -180,9 +171,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -215,9 +203,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -250,9 +235,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -307,9 +289,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit+WebDAV.swift b/Sources/NextcloudKit/NextcloudKit+WebDAV.swift index 86dea614..45d0a349 100644 --- a/Sources/NextcloudKit/NextcloudKit+WebDAV.swift +++ b/Sources/NextcloudKit/NextcloudKit+WebDAV.swift @@ -11,7 +11,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ ocId: String?, _ date: Date?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ ocId: String?, _ date: Date?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { guard let url = serverUrlFileName.encodedToUrl, let nkSession = nkCommonInstance.getSession(account: account), let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { @@ -29,32 +29,23 @@ public extension NextcloudKit { nkSession.sessionData.request(urlRequest, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + var date: Date? + let ocId = self.nkCommonInstance.findHeader("oc-fileid", allHeaderFields: response.response?.allHeaderFields) + if let dateString = self.nkCommonInstance.findHeader("date", allHeaderFields: response.response?.allHeaderFields) { + date = dateString.parsedDate(using: "EEE, dd MMM y HH:mm:ss zzz") } - switch response.result { - case .failure(let error): - let error = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completion(account, nil, nil, response, error) } - case .success: - let ocId = self.nkCommonInstance.findHeader("oc-fileid", allHeaderFields: response.response?.allHeaderFields) - if let dateString = self.nkCommonInstance.findHeader("date", allHeaderFields: response.response?.allHeaderFields) { - if let date = self.nkCommonInstance.convertDate(dateString, format: "EEE, dd MMM y HH:mm:ss zzz") { - options.queue.async { completion(account, ocId, date, response, .success) } - } else { - options.queue.async { completion(account, nil, nil, response, .invalidDate) } - } - } else { - options.queue.async { completion(account, nil, nil, response, .invalidDate) } - } + let result = self.evaluateResponse(response) + + options.queue.async { + completion(account, ocId, date, response, result) } } } func createFolderAsync(serverUrlFileName: String, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, ocId: String?, date: Date?, responseData: AFDataResponse?, error: NKError) { + options: NKRequestOptions = NKRequestOptions()) async -> (account: String, ocId: String?, date: Date?, responseData: AFDataResponse?, error: NKError) { await withUnsafeContinuation({ continuation in NextcloudKit.shared.createFolder(serverUrlFileName: serverUrlFileName, account: account, options: options) { account, ocId, date, responseData, error in continuation.resume(returning: (account: account, ocId: ocId, date: date, responseData: responseData, error: error)) @@ -66,7 +57,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { guard let url = serverUrlFileName.encodedToUrl, let nkSession = nkCommonInstance.getSession(account: account), let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { @@ -83,23 +74,18 @@ public extension NextcloudKit { nkSession.sessionData.request(urlRequest, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } - switch response.result { - case .failure(let error): - let error = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completion(account, response, error) } - case .success: - options.queue.async { completion(account, response, .success) } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + let result = self.evaluateResponse(response) + + options.queue.async { + completion(account, response, result) } } } func deleteFileOrFolderAsync(serverUrlFileName: String, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, responseData: AFDataResponse?, error: NKError) { + options: NKRequestOptions = NKRequestOptions()) async -> (account: String, responseData: AFDataResponse?, error: NKError) { await withUnsafeContinuation({ continuation in NextcloudKit.shared.deleteFileOrFolder(serverUrlFileName: serverUrlFileName, account: account, options: options) { account, responseData, error in continuation.resume(returning: (account: account, responseData: responseData, error: error)) @@ -113,7 +99,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { guard let url = serverUrlFileNameSource.encodedToUrl, let nkSession = nkCommonInstance.getSession(account: account), var headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { @@ -137,16 +123,11 @@ public extension NextcloudKit { nkSession.sessionData.request(urlRequest, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } - switch response.result { - case .failure(let error): - let error = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completion(account, response, error) } - case .success: - options.queue.async { completion(account, response, .success) } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + let result = self.evaluateResponse(response) + + options.queue.async { + completion(account, response, result) } } } @@ -155,7 +136,7 @@ public extension NextcloudKit { serverUrlFileNameDestination: String, overwrite: Bool, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, responseData: AFDataResponse?, error: NKError) { + options: NKRequestOptions = NKRequestOptions()) async -> (account: String, responseData: AFDataResponse?, error: NKError) { await withUnsafeContinuation({ continuation in NextcloudKit.shared.moveFileOrFolder(serverUrlFileNameSource: serverUrlFileNameSource, serverUrlFileNameDestination: serverUrlFileNameDestination, overwrite: overwrite, account: account, options: options) { account, responseData, error in continuation.resume(returning: (account: account, responseData: responseData, error: error)) @@ -169,7 +150,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { guard let url = serverUrlFileNameSource.encodedToUrl, let nkSession = nkCommonInstance.getSession(account: account), var headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { @@ -194,16 +175,11 @@ public extension NextcloudKit { nkSession.sessionData.request(urlRequest, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } - switch response.result { - case .failure(let error): - let error = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completion(account, response, error) } - case .success: - options.queue.async { completion(account, response, .success) } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + let result = self.evaluateResponse(response) + + options.queue.async { + completion(account, response, result) } } } @@ -212,7 +188,7 @@ public extension NextcloudKit { serverUrlFileNameDestination: String, overwrite: Bool, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, responseData: AFDataResponse?, error: NKError) { + options: NKRequestOptions = NKRequestOptions()) async -> (account: String, responseData: AFDataResponse?, error: NKError) { await withUnsafeContinuation({ continuation in NextcloudKit.shared.copyFileOrFolder(serverUrlFileNameSource: serverUrlFileNameSource, serverUrlFileNameDestination: serverUrlFileNameDestination, overwrite: overwrite, account: account, options: options) { account, responseData, error in continuation.resume(returning: (account: account, responseData: responseData, error: error)) @@ -264,9 +240,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -382,12 +355,12 @@ public extension NextcloudKit { var greaterDateString: String?, lessDateString: String? let href = "/files/" + nkSession.userId + path if let lessDate = lessDate as? Date { - lessDateString = self.nkCommonInstance.convertDate(lessDate, format: "yyyy-MM-dd'T'HH:mm:ssZZZZZ") + lessDateString = lessDate.formatted(using: "yyyy-MM-dd'T'HH:mm:ssZZZZZ") } else if let lessDate = lessDate as? Int { lessDateString = String(lessDate) } if let greaterDate = greaterDate as? Date { - greaterDateString = self.nkCommonInstance.convertDate(greaterDate, format: "yyyy-MM-dd'T'HH:mm:ssZZZZZ") + greaterDateString = greaterDate.formatted(using: "yyyy-MM-dd'T'HH:mm:ssZZZZZ") } else if let greaterDate = greaterDate as? Int { greaterDateString = String(greaterDate) } @@ -438,9 +411,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -461,7 +431,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { /// options.contentType = "application/xml" /// @@ -488,16 +458,11 @@ public extension NextcloudKit { nkSession.sessionData.request(urlRequest, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) - }.response(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } - switch response.result { - case .failure(let error): - let error = NKError(error: error, afResponse: response, responseData: response.data) - options.queue.async { completion(account, response, error) } - case .success: - options.queue.async { completion(account, response, .success) } + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + let result = self.evaluateResponse(response) + + options.queue.async { + completion(account, response, result) } } } @@ -546,9 +511,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) @@ -612,9 +574,6 @@ public extension NextcloudKit { task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in - if self.nkCommonInstance.levelLog > 0 { - debugPrint(response) - } switch response.result { case .failure(let error): let error = NKError(error: error, afResponse: response, responseData: response.data) diff --git a/Sources/NextcloudKit/NextcloudKit.swift b/Sources/NextcloudKit/NextcloudKit.swift index 8d843023..3ff2257c 100644 --- a/Sources/NextcloudKit/NextcloudKit.swift +++ b/Sources/NextcloudKit/NextcloudKit.swift @@ -22,6 +22,10 @@ open class NextcloudKit { #endif public var nkCommonInstance = NKCommon() + internal func log(debug message: String) { + NKLogFileManager.shared.writeLog(debug: message) + } + internal lazy var unauthorizedSession: Alamofire.Session = { let configuration = URLSessionConfiguration.af.default configuration.requestCachePolicy = .reloadIgnoringLocalCacheData @@ -73,14 +77,6 @@ open class NextcloudKit { } } - public func setupLog(pathLog: String, - levelLog: Int, - copyLogToDocumentDirectory: Bool) { - self.nkCommonInstance.pathLog = pathLog - self.nkCommonInstance.levelLog = levelLog - self.nkCommonInstance.copyLogToDocumentDirectory = copyLogToDocumentDirectory - } - public func appendSession(account: String, urlBase: String, user: String, @@ -192,4 +188,27 @@ open class NextcloudKit { reachabilityManager?.stopListening() } #endif + + /// Evaluates an Alamofire response and returns the appropriate NKError. + /// Treats `inputDataNilOrZeroLength` as `.success`. + func evaluateResponse(_ response: AFDataResponse) -> NKError { + if let afError = response.error?.asAFError { + if afError.isExplicitlyCancelledError { + return .cancelled + } + } + + switch response.result { + case .failure(let error): + if let afError = error.asAFError, + case .responseSerializationFailed(let reason) = afError, + case .inputDataNilOrZeroLength = reason { + return .success + } else { + return NKError(error: error, afResponse: response, responseData: response.data) + } + case .success: + return .success + } + } } diff --git a/Sources/NextcloudKit/NextcloudKitBackground.swift b/Sources/NextcloudKit/NextcloudKitBackground.swift index 7a33ec87..32f07e9e 100644 --- a/Sources/NextcloudKit/NextcloudKitBackground.swift +++ b/Sources/NextcloudKit/NextcloudKitBackground.swift @@ -59,7 +59,6 @@ public final class NKBackground: NSObject, URLSessionTaskDelegate, URLSessionDel let task = nkSession.sessionDownloadBackground.downloadTask(with: request) task.taskDescription = taskDescription task.resume() - self.nkCommonInstance.writeLog("Network start download file: \(serverUrlFileName)") return (task, .success) } @@ -135,7 +134,6 @@ public final class NKBackground: NSObject, URLSessionTaskDelegate, URLSessionDel let task = uploadSession?.uploadTask(with: request, fromFile: URL(fileURLWithPath: fileNameLocalPath)) task?.taskDescription = taskDescription task?.resume() - self.nkCommonInstance.writeLog("Network start upload file: \(serverUrlFileName)") return (task, .success) } @@ -201,11 +199,11 @@ public final class NKBackground: NSObject, URLSessionTaskDelegate, URLSessionDel etag = self.nkCommonInstance.findHeader("etag", allHeaderFields: header) } if etag != nil { etag = etag?.replacingOccurrences(of: "\"", with: "") } - if let dateString = self.nkCommonInstance.findHeader("date", allHeaderFields: header) { - date = self.nkCommonInstance.convertDate(dateString, format: "EEE, dd MMM y HH:mm:ss zzz") + if let dateRaw = self.nkCommonInstance.findHeader("date", allHeaderFields: header) { + date = dateRaw.parsedDate(using: "EEE, dd MMM y HH:mm:ss zzz") } if let dateString = header["Last-Modified"] as? String { - dateLastModified = self.nkCommonInstance.convertDate(dateString, format: "EEE, dd MMM y HH:mm:ss zzz") + dateLastModified = dateString.parsedDate(using: "EEE, dd MMM y HH:mm:ss zzz") } length = header["Content-Length"] as? Int64 ?? 0 } @@ -215,23 +213,13 @@ public final class NKBackground: NSObject, URLSessionTaskDelegate, URLSessionDel } else if task is URLSessionUploadTask { self.nkCommonInstance.delegate?.uploadComplete(fileName: fileName, serverUrl: serverUrl, ocId: ocId, etag: etag, date: date, size: task.countOfBytesExpectedToSend, task: task, error: nkError) } - - if nkError.errorCode == 0 { - self.nkCommonInstance.writeLog("Network completed file: \(serverUrl)/\(fileName)") - } else { - self.nkCommonInstance.writeLog("Network completed file: \(serverUrl)/\(fileName) with error code \(nkError.errorCode) and error description " + nkError.errorDescription) - } } public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { if self.nkCommonInstance.delegate == nil { - self.nkCommonInstance.writeLog("[WARNING] URLAuthenticationChallenge, no delegate found, perform with default handling") completionHandler(URLSession.AuthChallengeDisposition.performDefaultHandling, nil) } else { self.nkCommonInstance.delegate?.authenticationChallenge(session, didReceive: challenge, completionHandler: { authChallengeDisposition, credential in - if self.nkCommonInstance.levelLog > 1 { - self.nkCommonInstance.writeLog("[INFO AUTH] Challenge Disposition: \(authChallengeDisposition.rawValue)") - } completionHandler(authChallengeDisposition, credential) }) } diff --git a/Sources/NextcloudKit/NextcloudKitSessionDelegate.swift b/Sources/NextcloudKit/NextcloudKitSessionDelegate.swift index 9d9c65ce..789d00a1 100644 --- a/Sources/NextcloudKit/NextcloudKitSessionDelegate.swift +++ b/Sources/NextcloudKit/NextcloudKitSessionDelegate.swift @@ -25,13 +25,9 @@ final class NextcloudKitSessionDelegate: SessionDelegate { if let nkCommon = self.nkCommonInstance, let delegate = nkCommon.delegate { delegate.authenticationChallenge(session, didReceive: challenge) { authChallengeDisposition, credential in - if nkCommon.levelLog > 1 { - nkCommon.writeLog("[INFO AUTH] Challenge Disposition: \(authChallengeDisposition.rawValue)") - } completionHandler(authChallengeDisposition, credential) } } else { - self.nkCommonInstance?.writeLog("[WARNING] URLAuthenticationChallenge, no delegate found, perform with default handling") completionHandler(URLSession.AuthChallengeDisposition.performDefaultHandling, nil) } }