From 9c22be99b2ff7a4143408149541fffaa7d0b1ee5 Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Tue, 15 Jul 2025 18:14:28 +0200 Subject: [PATCH 1/5] fix Signed-off-by: Marino Faggiana --- .../NextcloudKit+Capabilities.swift | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Sources/NextcloudKit/NextcloudKit+Capabilities.swift b/Sources/NextcloudKit/NextcloudKit+Capabilities.swift index 8c116459..21df7f1d 100644 --- a/Sources/NextcloudKit/NextcloudKit+Capabilities.swift +++ b/Sources/NextcloudKit/NextcloudKit+Capabilities.swift @@ -520,19 +520,21 @@ final public class NKCapabilities: Sendable { /// Blocks the current thread until the async actor returns. /// Use only outside the Swift async context (never from another actor or async function). public func getCapabilitiesBlocking(for account: String?) -> Capabilities { - guard let account else { - return Capabilities() - } - let group = DispatchGroup() + guard let account else { return Capabilities() } + var result: Capabilities? + let semaphore = DispatchSemaphore(value: 0) - group.enter() Task.detached(priority: .userInitiated) { - result = await self.store.get(account) - group.leave() + let value = await self.store.get(account) + result = value + semaphore.signal() } - group.wait() + // Aspetta al massimo 5 secondi per evitare blocchi infiniti + let timeout = DispatchTime.now() + .seconds(5) + _ = semaphore.wait(timeout: timeout) + return result ?? Capabilities() } } From 037420ef0577c56e20d7c8942479d4af704df111 Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Tue, 15 Jul 2025 18:20:25 +0200 Subject: [PATCH 2/5] test Signed-off-by: Marino Faggiana --- .../NextcloudKit/NextcloudKit+Capabilities.swift | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Sources/NextcloudKit/NextcloudKit+Capabilities.swift b/Sources/NextcloudKit/NextcloudKit+Capabilities.swift index 21df7f1d..c52b85f0 100644 --- a/Sources/NextcloudKit/NextcloudKit+Capabilities.swift +++ b/Sources/NextcloudKit/NextcloudKit+Capabilities.swift @@ -525,16 +525,15 @@ final public class NKCapabilities: Sendable { var result: Capabilities? let semaphore = DispatchSemaphore(value: 0) - Task.detached(priority: .userInitiated) { - let value = await self.store.get(account) - result = value - semaphore.signal() + let queue = DispatchQueue(label: "CapabilitiesBlockingQueue") + queue.async { + Task { + result = await self.store.get(account) + semaphore.signal() + } } - // Aspetta al massimo 5 secondi per evitare blocchi infiniti - let timeout = DispatchTime.now() + .seconds(5) - _ = semaphore.wait(timeout: timeout) - + _ = semaphore.wait(timeout: .now() + 1) return result ?? Capabilities() } } From 246dac11453527b087ced1a84ff12ca68c5726f3 Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Tue, 15 Jul 2025 21:01:06 +0200 Subject: [PATCH 3/5] async code Signed-off-by: Marino Faggiana --- Sources/NextcloudKit/NKCommon.swift | 4 +- Sources/NextcloudKit/NKMonitor.swift | 4 +- .../NextcloudKit+Capabilities.swift | 42 ++----------------- .../NextcloudKit/NextcloudKit+NCText.swift | 8 ++-- .../NKFilePropertyResolver.swift | 4 +- .../TypeIdentifiers/NKTypeIdentifiers.swift | 5 +-- 6 files changed, 16 insertions(+), 51 deletions(-) diff --git a/Sources/NextcloudKit/NKCommon.swift b/Sources/NextcloudKit/NKCommon.swift index 8e5f67a7..b4134041 100644 --- a/Sources/NextcloudKit/NKCommon.swift +++ b/Sources/NextcloudKit/NKCommon.swift @@ -200,11 +200,11 @@ public struct NKCommon: Sendable { // MARK: - Server Error GroupDefaults - public func appendServerErrorAccount(_ account: String, errorCode: Int) { + public func appendServerErrorAccount(_ account: String, errorCode: Int) async { guard let groupDefaults = UserDefaults(suiteName: groupIdentifier) else { return } - let capabilities = NKCapabilities.shared.getCapabilitiesBlocking(for: account) + let capabilities = await NKCapabilities.shared.getCapabilities(for: account) /// Unavailable if errorCode == 503 { diff --git a/Sources/NextcloudKit/NKMonitor.swift b/Sources/NextcloudKit/NKMonitor.swift index e579130e..863939ea 100644 --- a/Sources/NextcloudKit/NKMonitor.swift +++ b/Sources/NextcloudKit/NKMonitor.swift @@ -41,7 +41,9 @@ final class NKMonitor: EventMonitor, Sendable { let headerCheckInterceptor = request.request?.allHTTPHeaderFields?[nkCommonInstance.headerCheckInterceptor], headerCheckInterceptor.lowercased() == "true", let account = request.request?.allHTTPHeaderFields?[nkCommonInstance.headerAccount] { - nkCommonInstance.appendServerErrorAccount(account, errorCode: statusCode) + Task { + await nkCommonInstance.appendServerErrorAccount(account, errorCode: statusCode) + } } DispatchQueue.global(qos: .utility).async { diff --git a/Sources/NextcloudKit/NextcloudKit+Capabilities.swift b/Sources/NextcloudKit/NextcloudKit+Capabilities.swift index c52b85f0..5eccc00c 100644 --- a/Sources/NextcloudKit/NextcloudKit+Capabilities.swift +++ b/Sources/NextcloudKit/NextcloudKit+Capabilities.swift @@ -410,7 +410,7 @@ public extension NextcloudKit { capabilities.termsOfService = json.termsOfService?.enabled ?? false // Persist capabilities in shared store - await NKCapabilities.shared.appendCapabilitiesAsync(for: account, capabilities: capabilities) + await NKCapabilities.shared.appendCapabilities(for: account, capabilities: capabilities) return capabilities } catch { nkLog(error: "Could not decode json capabilities: \(error.localizedDescription)") @@ -490,50 +490,14 @@ final public class NKCapabilities: Sendable { // MARK: - Public API - public func appendCapabilitiesAsync(for account: String, capabilities: Capabilities) async { + public func appendCapabilities(for account: String, capabilities: Capabilities) async { await store.set(account, value: capabilities) } - /// Synchronously stores capabilities for the given account. - /// Blocks the current thread until the async actor completes. - /// Use only outside of async/actor contexts. - public func appendCapabilitiesBlocking(for account: String, capabilities: Capabilities) { - let group = DispatchGroup() - - group.enter() - Task.detached(priority: .userInitiated) { - await self.store.set(account, value: capabilities) - group.leave() - } - - group.wait() - } - - public func getCapabilitiesAsync(for account: String?) async -> Capabilities { + public func getCapabilities(for account: String?) async -> Capabilities { guard let account else { return Capabilities() } return await store.get(account) ?? Capabilities() } - - /// Synchronously retrieves capabilities for the given account. - /// Blocks the current thread until the async actor returns. - /// Use only outside the Swift async context (never from another actor or async function). - public func getCapabilitiesBlocking(for account: String?) -> Capabilities { - guard let account else { return Capabilities() } - - var result: Capabilities? - let semaphore = DispatchSemaphore(value: 0) - - let queue = DispatchQueue(label: "CapabilitiesBlockingQueue") - queue.async { - Task { - result = await self.store.get(account) - semaphore.signal() - } - } - - _ = semaphore.wait(timeout: .now() + 1) - return result ?? Capabilities() - } } diff --git a/Sources/NextcloudKit/NextcloudKit+NCText.swift b/Sources/NextcloudKit/NextcloudKit+NCText.swift index 03b61d5b..fb884433 100644 --- a/Sources/NextcloudKit/NextcloudKit+NCText.swift +++ b/Sources/NextcloudKit/NextcloudKit+NCText.swift @@ -37,10 +37,10 @@ public extension NextcloudKit { Task { do { let (editors, creators) = try NKEditorDetailsConverter.from(data: responseData) - let capabilities = await NKCapabilities.shared.getCapabilitiesAsync(for: account) + let capabilities = await NKCapabilities.shared.getCapabilities(for: account) capabilities.directEditingEditors = editors capabilities.directEditingCreators = creators - await NKCapabilities.shared.appendCapabilitiesAsync(for: account, capabilities: capabilities) + await NKCapabilities.shared.appendCapabilities(for: account, capabilities: capabilities) options.queue.async { completion(account, editors, creators, response, .success) @@ -206,9 +206,9 @@ public extension NextcloudKit { let decoded = try JSONDecoder().decode(NKEditorTemplateResponse.self, from: data) let templates = decoded.ocs.data.editors // Update capabilities - let capabilities = await NKCapabilities.shared.getCapabilitiesAsync(for: account) + let capabilities = await NKCapabilities.shared.getCapabilities(for: account) capabilities.directEditingTemplates = templates - await NKCapabilities.shared.appendCapabilitiesAsync(for: account, capabilities: capabilities) + await NKCapabilities.shared.appendCapabilities(for: account, capabilities: capabilities) options.queue.async { completion(account, templates, response, .success) } } catch { diff --git a/Sources/NextcloudKit/TypeIdentifiers/NKFilePropertyResolver.swift b/Sources/NextcloudKit/TypeIdentifiers/NKFilePropertyResolver.swift index d4304476..91c3e2e9 100644 --- a/Sources/NextcloudKit/TypeIdentifiers/NKFilePropertyResolver.swift +++ b/Sources/NextcloudKit/TypeIdentifiers/NKFilePropertyResolver.swift @@ -44,10 +44,10 @@ public final class NKFilePropertyResolver { public init() {} - public func resolve(inUTI: String, account: String) -> NKFileProperty { + public func resolve(inUTI: String, account: String) async -> NKFileProperty { let fileProperty = NKFileProperty() let typeIdentifier = inUTI as String - let capabilities = NKCapabilities.shared.getCapabilitiesBlocking(for: account) + let capabilities = await NKCapabilities.shared.getCapabilities(for: account) let utiString = inUTI as String // Preferred extension diff --git a/Sources/NextcloudKit/TypeIdentifiers/NKTypeIdentifiers.swift b/Sources/NextcloudKit/TypeIdentifiers/NKTypeIdentifiers.swift index edf08cb0..ec7f2c8a 100644 --- a/Sources/NextcloudKit/TypeIdentifiers/NKTypeIdentifiers.swift +++ b/Sources/NextcloudKit/TypeIdentifiers/NKTypeIdentifiers.swift @@ -26,8 +26,7 @@ public actor NKTypeIdentifiers { private init() {} // Resolves type info from file name and optional MIME type - public func getInternalType(fileName: String, mimeType inputMimeType: String, directory: Bool, account: String) -> NKTypeIdentifierCache { - + public func getInternalType(fileName: String, mimeType inputMimeType: String, directory: Bool, account: String) async -> NKTypeIdentifierCache { var ext = (fileName as NSString).pathExtension.lowercased() var mimeType = inputMimeType var classFile = "" @@ -63,7 +62,7 @@ public actor NKTypeIdentifiers { fileNameWithoutExt = fileName ext = "" } else { - let props = resolver.resolve(inUTI: typeIdentifier, account: account) + let props = await resolver.resolve(inUTI: typeIdentifier, account: account) classFile = props.classFile.rawValue iconName = props.iconName.rawValue } From 45ca79a4fe752428c697fc010cd35be7973190ba Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Wed, 16 Jul 2025 09:42:09 +0200 Subject: [PATCH 4/5] code Signed-off-by: Marino Faggiana --- .../TypeIdentifiers/NKTypeIdentifiers.swift | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/Sources/NextcloudKit/TypeIdentifiers/NKTypeIdentifiers.swift b/Sources/NextcloudKit/TypeIdentifiers/NKTypeIdentifiers.swift index ec7f2c8a..de244286 100644 --- a/Sources/NextcloudKit/TypeIdentifiers/NKTypeIdentifiers.swift +++ b/Sources/NextcloudKit/TypeIdentifiers/NKTypeIdentifiers.swift @@ -90,39 +90,3 @@ public actor NKTypeIdentifiers { filePropertyCache.removeAll() } } - -/// Helper class to access NKTypeIdentifiers from sync contexts (e.g. in legacy code or libraries). -public final class NKTypeIdentifiersHelper { - public static let shared = NKTypeIdentifiersHelper() - - // Internal actor reference (uses NKTypeIdentifiers.shared by default) - private let actor: NKTypeIdentifiers - - private init() { - self.actor = .shared - } - - // Init with optional custom actor (useful for testing) - public init(actor: NKTypeIdentifiers = .shared) { - self.actor = actor - } - - // Synchronously resolves file type info by calling the async actor inside a semaphore block. - public func getInternalTypeSync(fileName: String, mimeType: String, directory: Bool, account: String) -> NKTypeIdentifierCache { - var result: NKTypeIdentifierCache? - let semaphore = DispatchSemaphore(value: 0) - - Task { - result = await actor.getInternalType( - fileName: fileName, - mimeType: mimeType, - directory: directory, - account: account - ) - semaphore.signal() - } - - semaphore.wait() - return result! - } -} From f0e4f4076c5bbd7bcc234d37ab869cc32914b658 Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Wed, 16 Jul 2025 12:03:02 +0200 Subject: [PATCH 5/5] code Signed-off-by: Marino Faggiana --- Sources/NextcloudKit/NKCommon.swift | 6 +----- Sources/NextcloudKit/NextcloudKit+Capabilities.swift | 4 ++-- Sources/NextcloudKit/NextcloudKit+NCText.swift | 6 +++--- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Sources/NextcloudKit/NKCommon.swift b/Sources/NextcloudKit/NKCommon.swift index b4134041..db20a135 100644 --- a/Sources/NextcloudKit/NKCommon.swift +++ b/Sources/NextcloudKit/NKCommon.swift @@ -78,8 +78,6 @@ public struct NKCommon: Sendable { public let groupDefaultsUnavailable = "Unavailable" public let groupDefaultsToS = "ToS" - - // MARK: - Init init() { } @@ -204,7 +202,7 @@ public struct NKCommon: Sendable { guard let groupDefaults = UserDefaults(suiteName: groupIdentifier) else { return } - let capabilities = await NKCapabilities.shared.getCapabilities(for: account) + let capabilities = await NKCapabilities.shared.getCapabilities(for: account) /// Unavailable if errorCode == 503 { @@ -239,8 +237,6 @@ public struct NKCommon: Sendable { return "\(identifier).\(account)" } - - public func getStandardHeaders(account: String, options: NKRequestOptions? = nil) -> HTTPHeaders? { guard let session = nksessions.session(forAccount: account) else { return nil diff --git a/Sources/NextcloudKit/NextcloudKit+Capabilities.swift b/Sources/NextcloudKit/NextcloudKit+Capabilities.swift index 5eccc00c..7b75fdb2 100644 --- a/Sources/NextcloudKit/NextcloudKit+Capabilities.swift +++ b/Sources/NextcloudKit/NextcloudKit+Capabilities.swift @@ -410,7 +410,7 @@ public extension NextcloudKit { capabilities.termsOfService = json.termsOfService?.enabled ?? false // Persist capabilities in shared store - await NKCapabilities.shared.appendCapabilities(for: account, capabilities: capabilities) + await NKCapabilities.shared.setCapabilities(for: account, capabilities: capabilities) return capabilities } catch { nkLog(error: "Could not decode json capabilities: \(error.localizedDescription)") @@ -490,7 +490,7 @@ final public class NKCapabilities: Sendable { // MARK: - Public API - public func appendCapabilities(for account: String, capabilities: Capabilities) async { + public func setCapabilities(for account: String, capabilities: Capabilities) async { await store.set(account, value: capabilities) } diff --git a/Sources/NextcloudKit/NextcloudKit+NCText.swift b/Sources/NextcloudKit/NextcloudKit+NCText.swift index fb884433..99c5e1d3 100644 --- a/Sources/NextcloudKit/NextcloudKit+NCText.swift +++ b/Sources/NextcloudKit/NextcloudKit+NCText.swift @@ -40,7 +40,7 @@ public extension NextcloudKit { let capabilities = await NKCapabilities.shared.getCapabilities(for: account) capabilities.directEditingEditors = editors capabilities.directEditingCreators = creators - await NKCapabilities.shared.appendCapabilities(for: account, capabilities: capabilities) + await NKCapabilities.shared.setCapabilities(for: account, capabilities: capabilities) options.queue.async { completion(account, editors, creators, response, .success) @@ -208,8 +208,8 @@ public extension NextcloudKit { // Update capabilities let capabilities = await NKCapabilities.shared.getCapabilities(for: account) capabilities.directEditingTemplates = templates - await NKCapabilities.shared.appendCapabilities(for: account, capabilities: capabilities) - + await NKCapabilities.shared.setCapabilities(for: account, capabilities: capabilities) + options.queue.async { completion(account, templates, response, .success) } } catch { nkLog(error: "Failed to decode template list: \(error)")