From 64a712b9fe30c9f9d99da4b0fe561443416855e8 Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Thu, 17 Jul 2025 08:04:09 +0200 Subject: [PATCH 1/6] improved Signed-off-by: Marino Faggiana --- Sources/NextcloudKit/NKCommon.swift | 104 +++++++++++++----- .../NextcloudKit/NextcloudKit+Upload.swift | 34 ++++-- 2 files changed, 104 insertions(+), 34 deletions(-) diff --git a/Sources/NextcloudKit/NKCommon.swift b/Sources/NextcloudKit/NKCommon.swift index db20a135..596979a8 100644 --- a/Sources/NextcloudKit/NKCommon.swift +++ b/Sources/NextcloudKit/NKCommon.swift @@ -13,6 +13,28 @@ public enum NKTypeReachability: Int { case reachableCellular = 3 } +enum ChunkedFileError: Error, Equatable { + case noSpaceAvailable + case cannotCreateOutputDirectory + case cannotOpenInputFile + case writeFailed(Error) + case stoppedByNotification + + static func == (lhs: ChunkedFileError, rhs: ChunkedFileError) -> Bool { + switch (lhs, rhs) { + case (.noSpaceAvailable, .noSpaceAvailable), + (.cannotCreateOutputDirectory, .cannotCreateOutputDirectory), + (.cannotOpenInputFile, .cannotOpenInputFile), + (.stoppedByNotification, .stoppedByNotification): + return true + case (.writeFailed, .writeFailed): + return false // Not comparable + default: + return false + } + } +} + public protocol NextcloudKitDelegate: AnyObject, Sendable { func authenticationChallenge(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) @@ -91,10 +113,10 @@ public struct NKCommon: Sendable { filesChunk: [(fileName: String, size: Int64)], numChunks: @escaping (_ num: Int) -> Void = { _ in }, counterChunk: @escaping (_ counter: Int) -> Void = { _ in }, - completion: @escaping (_ filesChunk: [(fileName: String, size: Int64)]) -> Void = { _ in }) { + completion: @escaping (_ filesChunk: [(fileName: String, size: Int64)], _ error: Error?) -> Void = { _, _ in }) { // Check if filesChunk is empty if !filesChunk.isEmpty { - return completion(filesChunk) + return completion(filesChunk, nil) } defer { @@ -110,7 +132,7 @@ public struct NKCommon: Sendable { var incrementalSize: Int64 = 0 var filesChunk: [(fileName: String, size: Int64)] = [] var chunkSize = chunkSize - let bufferSize = 1000000 + let bufferSize = 1_000_000 var stop: Bool = false NotificationCenter.default.addObserver(forName: notificationCenterChunkedFileStop, object: nil, queue: nil) { _ in @@ -122,31 +144,34 @@ public struct NKCommon: Sendable { let totalSize = getFileSize(filePath: inputFilePath) var num: Int = Int(totalSize / Int64(chunkSize)) - if num > 10000 { + if num > 10_000 { chunkSize += 100_000_000 - num = Int(totalSize / Int64(chunkSize)) // ricalcolo + num = Int(totalSize / Int64(chunkSize)) } numChunks(num) + // Create output directory if needed if !fileManager.fileExists(atPath: outputDirectory, isDirectory: &isDirectory) { do { try fileManager.createDirectory(atPath: outputDirectory, withIntermediateDirectories: true, attributes: nil) } catch { - return completion([]) + return completion([], ChunkedFileError.cannotCreateOutputDirectory) } } + // Open input file do { reader = try .init(forReadingFrom: URL(fileURLWithPath: inputFilePath)) } catch { - return completion([]) + return completion([], ChunkedFileError.cannotOpenInputFile) } repeat { if stop { - return completion([]) + return completion([], ChunkedFileError.stoppedByNotification) } - if autoreleasepool(invoking: { () -> Int in + + let result = autoreleasepool(invoking: { () -> Int in if chunk >= chunkSize { writer?.closeFile() writer = nil @@ -159,6 +184,13 @@ public struct NKCommon: Sendable { let chunkRemaining: Int = chunkSize - chunk let rawBuffer = reader?.readData(ofLength: min(bufferSize, chunkRemaining)) + guard let rawBuffer else { + return 0 // end of file + } + + let safeBuffer = Data(rawBuffer) + + // Create writer if needed if writer == nil { let fileNameChunk = String(counter) let outputFileName = outputDirectory + "/" + fileNameChunk @@ -166,34 +198,54 @@ public struct NKCommon: Sendable { do { writer = try .init(forWritingTo: URL(fileURLWithPath: outputFileName)) } catch { - filesChunk = [] - return 0 + return -2 } filesChunk.append((fileName: fileNameChunk, size: 0)) } - if let rawBuffer = rawBuffer { - let safeBuffer = Data(rawBuffer) // secure copy - writer?.write(safeBuffer) - chunk = chunk + safeBuffer.count - return safeBuffer.count + + // Check free space before writing + if let free = try? URL(fileURLWithPath: outputDirectory).resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey]).volumeAvailableCapacityForImportantUsage, + free < Int64(safeBuffer.count * 2) { + return -1 // not enough space + } + + // Write chunk to file + do { + try writer?.write(contentsOf: safeBuffer) + chunk += safeBuffer.count + return 1 + } catch { + return -2 } - filesChunk = [] - return 0 - }) == 0 { break } + }) + + switch result { + case -1: + return completion([], ChunkedFileError.noSpaceAvailable) + case -2: + return completion([], ChunkedFileError.writeFailed(NSError(domain: "chunkedFile", code: -2))) + case 0: + break // EOF + case 1: + continue // success + default: + break + } } while true writer?.closeFile() reader?.closeFile() - counter = 0 - for fileChunk in filesChunk { - let size = getFileSize(filePath: outputDirectory + "/" + fileChunk.fileName) - incrementalSize = incrementalSize + size - filesChunk[counter].size = incrementalSize - counter += 1 + // Update incremental chunk sizes + for i in 0.. Void) { + // Ensure upload chunk folder exists + func createFolderIfNeeded(completion: @escaping (_ errorCode: NKError) -> Void) { readFileOrFolder(serverUrlFileName: serverUrlChunkFolder, depth: "0", account: account, options: options) { _, _, _, error in if error == .success { completion(NKError()) @@ -286,22 +287,37 @@ public extension NextcloudKit { } } - createFolder { error in + createFolderIfNeeded { error in guard error == .success else { return completion(account, nil, nil, .errorChunkCreateFolder) } + let outputDirectory = fileChunksOutputDirectory ?? directory var uploadNKError = NKError() - let outputDirectory = fileChunksOutputDirectory ?? directory - self.nkCommonInstance.chunkedFile(inputDirectory: directory, outputDirectory: outputDirectory, fileName: fileName, chunkSize: chunkSize, filesChunk: filesChunk) { num in + + self.nkCommonInstance.chunkedFile(inputDirectory: directory, + outputDirectory: outputDirectory, + fileName: fileName, + chunkSize: chunkSize, + filesChunk: filesChunk) { num in numChunks(num) } counterChunk: { counter in counterChunk(counter) - } completion: { filesChunk in - if filesChunk.isEmpty { - // The file for sending could not be created + } completion: { filesChunk, error in + + // Check chunking error + if let error = error { + if (error as? ChunkedFileError) == .noSpaceAvailable { + return completion(account, nil, nil, .errorChunkNoEnoughMemory) + } else { + return completion(account, nil, nil, .errorChunkFilesEmpty) + } + } + + guard !filesChunk.isEmpty else { return completion(account, nil, nil, .errorChunkFilesEmpty) } + var filesChunkOutput = filesChunk start(filesChunkOutput) @@ -309,10 +325,12 @@ public extension NextcloudKit { let serverUrlFileName = serverUrlChunkFolder + "/" + fileChunk.fileName let fileNameLocalPath = outputDirectory + "/" + fileChunk.fileName let fileSize = self.nkCommonInstance.getFileSize(filePath: fileNameLocalPath) + if fileSize == 0 { // The file could not be sent return completion(account, nil, nil, .errorChunkFileNull) } + let semaphore = DispatchSemaphore(value: 0) self.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, account: account, options: options, requestHandler: { request in requestHandler(request) From f975651b4730538666afc506dbe3d3737963be4d Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Thu, 17 Jul 2025 08:31:44 +0200 Subject: [PATCH 2/6] fix eof Signed-off-by: Marino Faggiana --- Sources/NextcloudKit/NKCommon.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/NextcloudKit/NKCommon.swift b/Sources/NextcloudKit/NKCommon.swift index 596979a8..37fa717c 100644 --- a/Sources/NextcloudKit/NKCommon.swift +++ b/Sources/NextcloudKit/NKCommon.swift @@ -184,8 +184,8 @@ public struct NKCommon: Sendable { let chunkRemaining: Int = chunkSize - chunk let rawBuffer = reader?.readData(ofLength: min(bufferSize, chunkRemaining)) - guard let rawBuffer else { - return 0 // end of file + guard let rawBuffer = reader?.readData(ofLength: min(bufferSize, chunkRemaining)), !rawBuffer.isEmpty else { + return 0 // EOF } let safeBuffer = Data(rawBuffer) @@ -226,9 +226,9 @@ public struct NKCommon: Sendable { case -2: return completion([], ChunkedFileError.writeFailed(NSError(domain: "chunkedFile", code: -2))) case 0: - break // EOF + break // EOF reached case 1: - continue // success + continue // keep chunking default: break } From c7504fcb26267c0784525e7652abfa923592b487 Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Thu, 17 Jul 2025 08:50:38 +0200 Subject: [PATCH 3/6] cod Signed-off-by: Marino Faggiana --- Sources/NextcloudKit/NKCommon.swift | 75 +++++++++++++++++------------ 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/Sources/NextcloudKit/NKCommon.swift b/Sources/NextcloudKit/NKCommon.swift index 37fa717c..ed7f0ab8 100644 --- a/Sources/NextcloudKit/NKCommon.swift +++ b/Sources/NextcloudKit/NKCommon.swift @@ -114,7 +114,7 @@ public struct NKCommon: Sendable { numChunks: @escaping (_ num: Int) -> Void = { _ in }, counterChunk: @escaping (_ counter: Int) -> Void = { _ in }, completion: @escaping (_ filesChunk: [(fileName: String, size: Int64)], _ error: Error?) -> Void = { _, _ in }) { - // Check if filesChunk is empty + // Return existing chunks immediately if !filesChunk.isEmpty { return completion(filesChunk, nil) } @@ -123,17 +123,17 @@ public struct NKCommon: Sendable { NotificationCenter.default.removeObserver(self, name: notificationCenterChunkedFileStop, object: nil) } - let fileManager: FileManager = .default + let fileManager = FileManager.default var isDirectory: ObjCBool = false var reader: FileHandle? var writer: FileHandle? - var chunk: Int = 0 - var counter: Int = 1 + var chunkWrittenBytes = 0 + var counter = 1 var incrementalSize: Int64 = 0 var filesChunk: [(fileName: String, size: Int64)] = [] var chunkSize = chunkSize let bufferSize = 1_000_000 - var stop: Bool = false + var stop = false NotificationCenter.default.addObserver(forName: notificationCenterChunkedFileStop, object: nil, queue: nil) { _ in stop = true @@ -172,63 +172,76 @@ public struct NKCommon: Sendable { } let result = autoreleasepool(invoking: { () -> Int in - if chunk >= chunkSize { - writer?.closeFile() - writer = nil - chunk = 0 - counterChunk(counter) - debugPrint("[DEBUG] Counter: \(counter)") - counter += 1 + let remaining = chunkSize - chunkWrittenBytes + guard let rawBuffer = reader?.readData(ofLength: min(bufferSize, remaining)) else { + return -1 // Error: read failed } - let chunkRemaining: Int = chunkSize - chunk - let rawBuffer = reader?.readData(ofLength: min(bufferSize, chunkRemaining)) - - guard let rawBuffer = reader?.readData(ofLength: min(bufferSize, chunkRemaining)), !rawBuffer.isEmpty else { - return 0 // EOF + if rawBuffer.isEmpty { + // Final flush of last chunk + if writer != nil { + writer?.closeFile() + writer = nil + counterChunk(counter) + debugPrint("[DEBUG] Final chunk closed: \(counter)") + counter += 1 + } + return 0 // End of file } let safeBuffer = Data(rawBuffer) - // Create writer if needed + if writer == nil { let fileNameChunk = String(counter) let outputFileName = outputDirectory + "/" + fileNameChunk fileManager.createFile(atPath: outputFileName, contents: nil, attributes: nil) do { - writer = try .init(forWritingTo: URL(fileURLWithPath: outputFileName)) + writer = try FileHandle(forWritingTo: URL(fileURLWithPath: outputFileName)) } catch { - return -2 + return -2 // Error: cannot create writer } filesChunk.append((fileName: fileNameChunk, size: 0)) } - - // Check free space before writing - if let free = try? URL(fileURLWithPath: outputDirectory).resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey]).volumeAvailableCapacityForImportantUsage, + // Check free disk space + if let free = try? URL(fileURLWithPath: outputDirectory) + .resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey]) + .volumeAvailableCapacityForImportantUsage, free < Int64(safeBuffer.count * 2) { - return -1 // not enough space + return -3 // Not enough disk space } - // Write chunk to file do { try writer?.write(contentsOf: safeBuffer) - chunk += safeBuffer.count - return 1 + chunkWrittenBytes += safeBuffer.count + if chunkWrittenBytes >= chunkSize { + writer?.closeFile() + writer = nil + chunkWrittenBytes = 0 + counterChunk(counter) + debugPrint("[DEBUG] Chunk completed: \(counter)") + counter += 1 + } + return 1 // OK } catch { - return -2 + return -4 // Write error } }) switch result { case -1: - return completion([], ChunkedFileError.noSpaceAvailable) + return completion([], ChunkedFileError.writeFailed(NSError(domain: "chunkedFile", code: -1))) case -2: return completion([], ChunkedFileError.writeFailed(NSError(domain: "chunkedFile", code: -2))) + case -3: + return completion([], ChunkedFileError.noSpaceAvailable) + case -4: + return completion([], ChunkedFileError.writeFailed(NSError(domain: "chunkedFile", code: -4))) case 0: - break // EOF reached + break case 1: - continue // keep chunking + continue default: break } From 35b4201266e77c3caf8486cf463c24c22fddca23 Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Thu, 17 Jul 2025 09:10:45 +0200 Subject: [PATCH 4/6] fix error Signed-off-by: Marino Faggiana --- Sources/NextcloudKit/NKCommon.swift | 40 +++++-------------- .../NextcloudKit/NextcloudKit+Upload.swift | 12 ++---- 2 files changed, 13 insertions(+), 39 deletions(-) diff --git a/Sources/NextcloudKit/NKCommon.swift b/Sources/NextcloudKit/NKCommon.swift index ed7f0ab8..ae183bcf 100644 --- a/Sources/NextcloudKit/NKCommon.swift +++ b/Sources/NextcloudKit/NKCommon.swift @@ -13,28 +13,6 @@ public enum NKTypeReachability: Int { case reachableCellular = 3 } -enum ChunkedFileError: Error, Equatable { - case noSpaceAvailable - case cannotCreateOutputDirectory - case cannotOpenInputFile - case writeFailed(Error) - case stoppedByNotification - - static func == (lhs: ChunkedFileError, rhs: ChunkedFileError) -> Bool { - switch (lhs, rhs) { - case (.noSpaceAvailable, .noSpaceAvailable), - (.cannotCreateOutputDirectory, .cannotCreateOutputDirectory), - (.cannotOpenInputFile, .cannotOpenInputFile), - (.stoppedByNotification, .stoppedByNotification): - return true - case (.writeFailed, .writeFailed): - return false // Not comparable - default: - return false - } - } -} - public protocol NextcloudKitDelegate: AnyObject, Sendable { func authenticationChallenge(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) @@ -155,7 +133,7 @@ public struct NKCommon: Sendable { do { try fileManager.createDirectory(atPath: outputDirectory, withIntermediateDirectories: true, attributes: nil) } catch { - return completion([], ChunkedFileError.cannotCreateOutputDirectory) + return completion([], NSError(domain: "chunkedFile", code: -2,userInfo: [NSLocalizedDescriptionKey: "Failed to create the output directory for file chunks."])) } } @@ -163,12 +141,12 @@ public struct NKCommon: Sendable { do { reader = try .init(forReadingFrom: URL(fileURLWithPath: inputFilePath)) } catch { - return completion([], ChunkedFileError.cannotOpenInputFile) + return completion([], NSError(domain: "chunkedFile", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to open the input file for reading."])) } - repeat { + outerLoop: repeat { if stop { - return completion([], ChunkedFileError.stoppedByNotification) + return completion([], NSError(domain: "chunkedFile", code: -5, userInfo: [NSLocalizedDescriptionKey: "Chunking was stopped by user request or system notification."])) } let result = autoreleasepool(invoking: { () -> Int in @@ -231,15 +209,15 @@ public struct NKCommon: Sendable { switch result { case -1: - return completion([], ChunkedFileError.writeFailed(NSError(domain: "chunkedFile", code: -1))) + return completion([], NSError(domain: "chunkedFile", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to read data from the input file."])) case -2: - return completion([], ChunkedFileError.writeFailed(NSError(domain: "chunkedFile", code: -2))) + return completion([], NSError(domain: "chunkedFile", code: -2, userInfo: [NSLocalizedDescriptionKey: "Failed to open the output chunk file for writing."])) case -3: - return completion([], ChunkedFileError.noSpaceAvailable) + return completion([], NSError(domain: "chunkedFile", code: -3, userInfo: [NSLocalizedDescriptionKey: "There is not enough available disk space to proceed."])) case -4: - return completion([], ChunkedFileError.writeFailed(NSError(domain: "chunkedFile", code: -4))) + return completion([], NSError(domain: "chunkedFile", code: -4, userInfo: [NSLocalizedDescriptionKey: "Failed to write data to chunk file."])) case 0: - break + break outerLoop case 1: continue default: diff --git a/Sources/NextcloudKit/NextcloudKit+Upload.swift b/Sources/NextcloudKit/NextcloudKit+Upload.swift index de45f5fa..87254923 100644 --- a/Sources/NextcloudKit/NextcloudKit+Upload.swift +++ b/Sources/NextcloudKit/NextcloudKit+Upload.swift @@ -306,16 +306,12 @@ public extension NextcloudKit { } completion: { filesChunk, error in // Check chunking error - if let error = error { - if (error as? ChunkedFileError) == .noSpaceAvailable { - return completion(account, nil, nil, .errorChunkNoEnoughMemory) - } else { - return completion(account, nil, nil, .errorChunkFilesEmpty) - } + if let error { + return completion(account, nil, nil, NKError(error: error)) } guard !filesChunk.isEmpty else { - return completion(account, nil, nil, .errorChunkFilesEmpty) + return completion(account, nil, nil, NKError(error: NSError(domain: "chunkedFile", code: -5,userInfo: [NSLocalizedDescriptionKey: "Files empty."]))) } var filesChunkOutput = filesChunk @@ -328,7 +324,7 @@ public extension NextcloudKit { if fileSize == 0 { // The file could not be sent - return completion(account, nil, nil, .errorChunkFileNull) + return completion(account, nil, nil, NKError(error: NSError(domain: "chunkedFile", code: -6,userInfo: [NSLocalizedDescriptionKey: "File empty."]))) } let semaphore = DispatchSemaphore(value: 0) From dfc7369eb7695f13c695eda82b397eb41dfa714b Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Thu, 17 Jul 2025 09:24:06 +0200 Subject: [PATCH 5/6] added parameter Signed-off-by: Marino Faggiana --- .../NextcloudKit/NextcloudKit+Upload.swift | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/Sources/NextcloudKit/NextcloudKit+Upload.swift b/Sources/NextcloudKit/NextcloudKit+Upload.swift index 87254923..84ec03ec 100644 --- a/Sources/NextcloudKit/NextcloudKit+Upload.swift +++ b/Sources/NextcloudKit/NextcloudKit+Upload.swift @@ -220,6 +220,7 @@ 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 }, + assemble: @escaping () -> Void = { }, completion: @escaping (_ account: String, _ filesChunk: [(fileName: String, size: Int64)]?, _ file: NKFile?, _ error: NKError) -> Void) { guard let nkSession = nkCommonInstance.nksessions.session(forAccount: account) else { return completion(account, nil, nil, .urlError) @@ -373,6 +374,8 @@ public extension NextcloudKit { let assembleTimeMax: Double = 30 * 60 // 30 min options.timeout = max(assembleTimeMin, min(assembleTimePerGB * assembleSizeInGB, assembleTimeMax)) + assemble() + self.moveFileOrFolder(serverUrlFileNameSource: serverUrlFileNameSource, serverUrlFileNameDestination: serverUrlFileName, overwrite: true, account: account, options: options) { _, _, error in guard error == .success else { return completion(account, filesChunkOutput, nil,.errorChunkMoveFile) @@ -414,13 +417,9 @@ public extension NextcloudKit { requestHandler: @escaping (_ request: UploadRequest) -> Void = { _ in }, taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, progressHandler: @escaping (_ totalBytesExpected: Int64, _ totalBytes: Int64, _ fractionCompleted: Double) -> Void = { _, _, _ in }, + assemble: @escaping () -> Void = { }, uploaded: @escaping (_ fileChunk: (fileName: String, size: Int64)) -> Void = { _ in } - ) async -> ( - account: String, - remainingChunks: [(fileName: String, size: Int64)]?, - file: NKFile?, - error: NKError - ) { + ) async -> (account: String, remainingChunks: [(fileName: String, size: Int64)]?, file: NKFile?, error: NKError) { await withCheckedContinuation { continuation in uploadChunk(directory: directory, fileChunksOutputDirectory: fileChunksOutputDirectory, @@ -440,13 +439,9 @@ public extension NextcloudKit { requestHandler: requestHandler, taskHandler: taskHandler, progressHandler: progressHandler, - uploaded: uploaded) { account, remaining, file, error in - continuation.resume(returning: ( - account: account, - remainingChunks: remaining, - file: file, - error: error - )) + uploaded: uploaded, + assemble: assemble) { account, remaining, file, error in + continuation.resume(returning: (account: account, remainingChunks: remaining, file: file, error: error)) } } } From c376da122479ca845df8f29d856b2da459c4bcfb Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Thu, 17 Jul 2025 09:32:46 +0200 Subject: [PATCH 6/6] x3 Signed-off-by: Marino Faggiana --- Sources/NextcloudKit/NextcloudKit+Upload.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/NextcloudKit/NextcloudKit+Upload.swift b/Sources/NextcloudKit/NextcloudKit+Upload.swift index 84ec03ec..17f007fd 100644 --- a/Sources/NextcloudKit/NextcloudKit+Upload.swift +++ b/Sources/NextcloudKit/NextcloudKit+Upload.swift @@ -267,7 +267,7 @@ public extension NextcloudKit { #endif #if os(visionOS) || os(iOS) - if freeDisk < fileNameLocalSize * 2 { + if freeDisk < fileNameLocalSize * 3 { // It seems there is not enough space to send the file return completion(account, nil, nil, .errorChunkNoEnoughMemory) }