diff --git a/Sources/NextcloudKit/Extensions/Data+Extension.swift b/Sources/NextcloudKit/Extensions/Data+Extension.swift new file mode 100644 index 00000000..42d62c78 --- /dev/null +++ b/Sources/NextcloudKit/Extensions/Data+Extension.swift @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import Foundation + +extension Data { + func printJson() { + do { + let json = try JSONSerialization.jsonObject(with: self, options: []) + let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) + guard let jsonString = String(data: data, encoding: .utf8) else { + print("Invalid data") + return + } + print(jsonString) + } catch { + print("Error: \(error.localizedDescription)") + } + } + + func jsonToString() -> String { + do { + let json = try JSONSerialization.jsonObject(with: self, options: []) + let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) + guard let jsonString = String(data: data, encoding: .utf8) else { + print("Invalid data") + return "" + } + return jsonString + } catch { + print("Error: \(error.localizedDescription)") + } + return "" + } +} diff --git a/Sources/NextcloudKit/NKInterceptor.swift b/Sources/NextcloudKit/NKInterceptor.swift index 232a0945..af3d3289 100644 --- a/Sources/NextcloudKit/NKInterceptor.swift +++ b/Sources/NextcloudKit/NKInterceptor.swift @@ -31,19 +31,19 @@ final class NKInterceptor: RequestInterceptor, Sendable { if let array = groupDefaults.array(forKey: nkCommonInstance.groupDefaultsUnauthorized) as? [String], array.contains(account) { - nkLog(tag: "AUTH", message: "Unauthorized for account: \(account)") + nkLog(tag: "AUTH", emoji: .error, message: "Unauthorized for account: \(account)") let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 401)) return completion(.failure(error)) } else if let array = groupDefaults.array(forKey: nkCommonInstance.groupDefaultsUnavailable) as? [String], array.contains(account) { - nkLog(tag: "SERVICE", message: "Unavailable for account: \(account)") + nkLog(tag: "SERVICE", emoji: .error, message: "Unavailable for account: \(account)") let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 503)) return completion(.failure(error)) } else if let array = groupDefaults.array(forKey: nkCommonInstance.groupDefaultsToS) as? [String], array.contains(account) { - nkLog(tag: "TOS", message: "Terms of service error for account: \(account)") + nkLog(tag: "TOS", emoji: .error, 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/NKSession.swift b/Sources/NextcloudKit/NKSession.swift index abb72c18..df224cb7 100644 --- a/Sources/NextcloudKit/NKSession.swift +++ b/Sources/NextcloudKit/NKSession.swift @@ -12,7 +12,6 @@ public struct NKSession: Sendable { public var password: String public var account: String public var userAgent: String - public var nextcloudVersion: Int public let groupIdentifier: String public let httpMaximumConnectionsPerHost: Int public let httpMaximumConnectionsPerHostInDownload: Int @@ -33,7 +32,6 @@ public struct NKSession: Sendable { password: String, account: String, userAgent: String, - nextcloudVersion: Int, groupIdentifier: String, httpMaximumConnectionsPerHost: Int, httpMaximumConnectionsPerHostInDownload: Int, @@ -44,7 +42,6 @@ public struct NKSession: Sendable { self.password = password self.account = account self.userAgent = userAgent - self.nextcloudVersion = nextcloudVersion self.groupIdentifier = groupIdentifier self.httpMaximumConnectionsPerHost = httpMaximumConnectionsPerHost self.httpMaximumConnectionsPerHostInDownload = httpMaximumConnectionsPerHostInDownload diff --git a/Sources/NextcloudKit/NextcloudKit+API.swift b/Sources/NextcloudKit/NextcloudKit+API.swift index d0864831..b168959b 100644 --- a/Sources/NextcloudKit/NextcloudKit+API.swift +++ b/Sources/NextcloudKit/NextcloudKit+API.swift @@ -610,45 +610,6 @@ public extension NextcloudKit { } // MARK: - - func getCapabilities(account: String, - options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - 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), - let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { - return options.queue.async { completion(account, nil, .urlError) } - } - - 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) - }.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(account, response, error) } - case .success: - options.queue.async { completion(account, response, .success) } - } - } - } - - func getCapabilitiesAsync(account: String, - options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation { continuation in - getCapabilities(account: account, - options: options, - taskHandler: taskHandler) { account, responseData, error in - continuation.resume(returning: (account, responseData, error)) - } - } - } - - // MARK: - - func getRemoteWipeStatus(serverUrl: String, token: String, account: String, diff --git a/Sources/NextcloudKit/NextcloudKit+Capabilities.swift b/Sources/NextcloudKit/NextcloudKit+Capabilities.swift new file mode 100644 index 00000000..3d5f7a6a --- /dev/null +++ b/Sources/NextcloudKit/NextcloudKit+Capabilities.swift @@ -0,0 +1,534 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +#if os(macOS) +import Foundation +import AppKit +#else +import UIKit +#endif +import Alamofire + +// Provides APIs to retrieve and store server capabilities for a Nextcloud account. +// The capabilities endpoint returns server and feature flags, which are parsed, +// cached, and made accessible for feature checks throughout the app. + +public extension NextcloudKit { + + /// Retrieves the capabilities of the Nextcloud server for the given account. + /// - Parameters: + /// - account: The account identifier. + /// - options: Additional request options. + /// - taskHandler: Callback for the underlying URL session task. + /// - completion: Callback returning parsed capabilities or an error. + func getCapabilities(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, + completion: @escaping (_ account: String, _ capabilities: NCCapabilities.Capabilities?, _ 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), + let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { + return options.queue.async { completion(account, nil, nil, .urlError) } + } + + 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) + }.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(account, nil, response, error) } + case .success: + Task { + do { + let capabilities = try await self.setCapabilitiesAsync(account: account, data: response.data) + options.queue.async { + completion(account, capabilities, response, .success) + } + } catch { + nkLog(debug: "Capabilities decoding failed: \\(error)") + options.queue.async { + completion(account, nil, response, .invalidData) + } + } + } + } + } + } + + /// Asynchronous wrapper around `getCapabilities`, returning a result tuple. + /// - Parameters: + /// - account: The Nextcloud account identifier. + /// - options: Request options, such as queue, custom headers, etc. + /// - taskHandler: Callback for the underlying `URLSessionTask`. + /// - Returns: A tuple containing account, parsed capabilities, response data, and result error. + func getCapabilitiesAsync(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, + capabilities: NCCapabilities.Capabilities?, + responseData: AFDataResponse?, + error: NKError) { + await withUnsafeContinuation { continuation in + getCapabilities(account: account, + options: options, + taskHandler: taskHandler) { account, capabilities,responseData, error in + continuation.resume(returning: (account, capabilities, responseData, error)) + } + } + } + + /// Asynchronously decodes and applies server capabilities from JSON data. + /// - Parameters: + /// - account: The Nextcloud account identifier. + /// - data: The raw JSON data returned from the capabilities endpoint. + /// - Returns: A fully populated `NCCapabilities.Capabilities` object. + /// - Throws: An error if decoding fails or data is missing. + func setCapabilitiesAsync(account: String, data: Data? = nil) async throws -> NCCapabilities.Capabilities { + guard let jsonData = data else { + throw NSError(domain: "SetCapabilities", code: 0, userInfo: [NSLocalizedDescriptionKey: "Missing JSON data"]) + } + + struct CapabilityNextcloud: Codable { + struct Ocs: Codable { + let meta: Meta + let data: Data + + struct Meta: Codable { + let status: String? + let message: String? + let statuscode: Int? + } + + struct Data: Codable { + let version: Version + let capabilities: Capabilities + + struct Version: Codable { + let string: String + let major: Int + } + + struct Capabilities: Codable { + let downloadLimit: DownloadLimit? + let filessharing: FilesSharing? + let theming: Theming? + let endtoendencryption: EndToEndEncryption? + let richdocuments: RichDocuments? + let activity: Activity? + let notifications: Notifications? + let files: Files? + let userstatus: UserStatus? + let external: External? + let groupfolders: GroupFolders? + let securityguard: SecurityGuard? + let assistant: Assistant? + let recommendations: Recommendations? + let termsOfService: TermsOfService? + + enum CodingKeys: String, CodingKey { + case downloadLimit = "downloadlimit" + case filessharing = "files_sharing" + case theming + case endtoendencryption = "end-to-end-encryption" + case richdocuments, activity, notifications, files + case userstatus = "user_status" + case external, groupfolders + case securityguard = "security_guard" + case assistant + case recommendations + case termsOfService = "terms_of_service" + } + + struct DownloadLimit: Codable { + let enabled: Bool? + let defaultLimit: Int? + } + + struct FilesSharing: Codable { + let apienabled: Bool? + let groupsharing: Bool? + let resharing: Bool? + let defaultpermissions: Int? + let ncpublic: Public? + + enum CodingKeys: String, CodingKey { + case apienabled = "api_enabled" + case groupsharing = "group_sharing" + case resharing + case defaultpermissions = "default_permissions" + case ncpublic = "public" + } + + struct Public: Codable { + let enabled: Bool + let upload: Bool? + let password: Password? + let sendmail: Bool? + let uploadfilesdrop: Bool? + let multiplelinks: Bool? + let expiredate: ExpireDate? + let expiredateinternal: ExpireDate? + let expiredateremote: ExpireDate? + + enum CodingKeys: String, CodingKey { + case upload, enabled, password + case sendmail = "send_mail" + case uploadfilesdrop = "upload_files_drop" + case multiplelinks = "multiple_links" + case expiredate = "expire_date" + case expiredateinternal = "expire_date_internal" + case expiredateremote = "expire_date_remote" + } + + struct Password: Codable { + let enforced: Bool? + let askForOptionalPassword: Bool? + } + + struct ExpireDate: Codable { + let enforced: Bool? + let days: Int? + } + } + } + + struct Theming: Codable { + let color: String? + let colorelement: String? + let colortext: String? + let colorelementbright: String? + let backgrounddefault: Bool? + let backgroundplain: Bool? + let colorelementdark: String? + let name: String? + let slogan: String? + let url: String? + let logo: String? + let background: String? + let logoheader: String? + let favicon: String? + + enum CodingKeys: String, CodingKey { + case color + case colorelement = "color-element" + case colortext = "color-text" + case colorelementbright = "color-element-bright" + case backgrounddefault = "background-default" + case backgroundplain = "background-plain" + case colorelementdark = "color-element-dark" + case name, slogan, url, logo, background, logoheader, favicon + } + } + + struct EndToEndEncryption: Codable { + let enabled: Bool? + let apiversion: String? + let keysexist: Bool? + + enum CodingKeys: String, CodingKey { + case enabled + case apiversion = "api-version" + case keysexist = "keys-exist" + } + } + + struct RichDocuments: Codable { + let mimetypes: [String]? + let directediting: Bool? + + enum CodingKeys: String, CodingKey { + case mimetypes + case directediting = "direct_editing" + } + } + + struct Activity: Codable { + let apiv2: [String]? + } + + struct Notifications: Codable { + let ocsendpoints: [String]? + + enum CodingKeys: String, CodingKey { + case ocsendpoints = "ocs-endpoints" + } + } + + struct TermsOfService: Codable { + let enabled: Bool? + let termuuid: String? + + enum CodingKeys: String, CodingKey { + case enabled + case termuuid = "term_uuid" + } + } + + struct Files: Codable { + let undelete: Bool? + let locking: String? + let comments: Bool? + let versioning: Bool? + let directEditing: DirectEditing? + let bigfilechunking: Bool? + let versiondeletion: Bool? + let versionlabeling: Bool? + let forbiddenFileNames: [String]? + let forbiddenFileNameBasenames: [String]? + let forbiddenFileNameCharacters: [String]? + let forbiddenFileNameExtensions: [String]? + + enum CodingKeys: String, CodingKey { + case undelete, locking, comments, versioning, directEditing, bigfilechunking + case versiondeletion = "version_deletion" + case versionlabeling = "version_labeling" + case forbiddenFileNames = "forbidden_filenames" + case forbiddenFileNameBasenames = "forbidden_filename_basenames" + case forbiddenFileNameCharacters = "forbidden_filename_characters" + case forbiddenFileNameExtensions = "forbidden_filename_extensions" + } + + struct DirectEditing: Codable { + let url: String? + let etag: String? + let supportsFileId: Bool? + } + } + + struct UserStatus: Codable { + let enabled: Bool? + let restore: Bool? + let supportsemoji: Bool? + + enum CodingKeys: String, CodingKey { + case enabled, restore + case supportsemoji = "supports_emoji" + } + } + + struct External: Codable { + let v1: [String]? + } + + struct GroupFolders: Codable { + let hasGroupFolders: Bool? + } + + struct SecurityGuard: Codable { + let diagnostics: Bool? + } + + struct Assistant: Codable { + let enabled: Bool? + let version: String? + } + + struct Recommendations: Codable { + let enabled: Bool? + } + } + } + } + + let ocs: Ocs + } + + if NKLogFileManager.shared.logLevel >= .normal { + jsonData.printJson() + } + + do { + // Decode the full JSON structure + let decoded = try JSONDecoder().decode(CapabilityNextcloud.self, from: jsonData) + let data = decoded.ocs.data + let json = data.capabilities + + // Initialize capabilities + let capabilities = NCCapabilities.Capabilities() + + // Version info + capabilities.serverVersion = data.version.string + capabilities.serverVersionMajor = data.version.major + + // Populate capabilities from decoded JSON + capabilities.fileSharingApiEnabled = json.filessharing?.apienabled ?? false + capabilities.fileSharingDefaultPermission = json.filessharing?.defaultpermissions ?? 0 + capabilities.fileSharingPubPasswdEnforced = json.filessharing?.ncpublic?.password?.enforced ?? false + capabilities.fileSharingPubExpireDateEnforced = json.filessharing?.ncpublic?.expiredate?.enforced ?? false + capabilities.fileSharingPubExpireDateDays = json.filessharing?.ncpublic?.expiredate?.days ?? 0 + capabilities.fileSharingInternalExpireDateEnforced = json.filessharing?.ncpublic?.expiredateinternal?.enforced ?? false + capabilities.fileSharingInternalExpireDateDays = json.filessharing?.ncpublic?.expiredateinternal?.days ?? 0 + capabilities.fileSharingRemoteExpireDateEnforced = json.filessharing?.ncpublic?.expiredateremote?.enforced ?? false + capabilities.fileSharingRemoteExpireDateDays = json.filessharing?.ncpublic?.expiredateremote?.days ?? 0 + capabilities.fileSharingDownloadLimit = json.downloadLimit?.enabled ?? false + capabilities.fileSharingDownloadLimitDefaultLimit = json.downloadLimit?.defaultLimit ?? 1 + + capabilities.themingColor = json.theming?.color ?? "" + capabilities.themingColorElement = json.theming?.colorelement ?? "" + capabilities.themingColorText = json.theming?.colortext ?? "" + capabilities.themingName = json.theming?.name ?? "" + capabilities.themingSlogan = json.theming?.slogan ?? "" + + capabilities.e2EEEnabled = json.endtoendencryption?.enabled ?? false + capabilities.e2EEApiVersion = json.endtoendencryption?.apiversion ?? "" + + capabilities.richDocumentsEnabled = json.richdocuments?.directediting ?? false + capabilities.richDocumentsMimetypes.removeAll() + capabilities.richDocumentsMimetypes = json.richdocuments?.mimetypes ?? [] + + capabilities.assistantEnabled = json.assistant?.enabled ?? false + + capabilities.activityEnabled = json.activity != nil + capabilities.activity = json.activity?.apiv2 ?? [] + + capabilities.notification = json.notifications?.ocsendpoints ?? [] + + capabilities.filesUndelete = json.files?.undelete ?? false + capabilities.filesLockVersion = json.files?.locking ?? "" + capabilities.filesComments = json.files?.comments ?? false + capabilities.filesBigfilechunking = json.files?.bigfilechunking ?? false + + capabilities.userStatusEnabled = json.userstatus?.enabled ?? false + capabilities.externalSites = json.external != nil + capabilities.groupfoldersEnabled = json.groupfolders?.hasGroupFolders ?? false + + if capabilities.serverVersionMajor >= 28 { + capabilities.isLivePhotoServerAvailable = true + } + + capabilities.securityGuardDiagnostics = json.securityguard?.diagnostics ?? false + + capabilities.forbiddenFileNames = json.files?.forbiddenFileNames ?? [] + capabilities.forbiddenFileNameBasenames = json.files?.forbiddenFileNameBasenames ?? [] + capabilities.forbiddenFileNameCharacters = json.files?.forbiddenFileNameCharacters ?? [] + capabilities.forbiddenFileNameExtensions = json.files?.forbiddenFileNameExtensions ?? [] + + capabilities.recommendations = json.recommendations?.enabled ?? false + capabilities.termsOfService = json.termsOfService?.enabled ?? false + + // Persist capabilities in shared store + await NCCapabilities.shared.appendCapabilitiesAsync(for: account, capabilities: capabilities) + return capabilities + } catch { + nkLog(error: "Could not decode json capabilities: \(error.localizedDescription)") + throw error + } + } +} + +/// A concurrency-safe store for capabilities associated with Nextcloud accounts. +actor CapabilitiesStore { + private var store: [String: NCCapabilities.Capabilities] = [:] + + func get(_ account: String) -> NCCapabilities.Capabilities? { + return store[account] + } + + func set(_ account: String, value: NCCapabilities.Capabilities) { + store[account] = value + } +} + +/// Singleton container and public API for accessing and caching capabilities. +final public class NCCapabilities: Sendable { + public static let shared = NCCapabilities() + + private let store = CapabilitiesStore() + + public class Capabilities: @unchecked Sendable { + public var serverVersionMajor: Int = 0 + public var serverVersion: String = "" + public var fileSharingApiEnabled: Bool = false + public var fileSharingPubPasswdEnforced: Bool = false + public var fileSharingPubExpireDateEnforced: Bool = false + public var fileSharingPubExpireDateDays: Int = 0 + public var fileSharingInternalExpireDateEnforced: Bool = false + public var fileSharingInternalExpireDateDays: Int = 0 + public var fileSharingRemoteExpireDateEnforced: Bool = false + public var fileSharingRemoteExpireDateDays: Int = 0 + public var fileSharingDefaultPermission: Int = 0 + public var fileSharingDownloadLimit: Bool = false + public var fileSharingDownloadLimitDefaultLimit: Int = 1 + public var themingColor: String = "" + public var themingColorElement: String = "" + public var themingColorText: String = "" + public var themingName: String = "" + public var themingSlogan: String = "" + public var e2EEEnabled: Bool = false + public var e2EEApiVersion: String = "" + public var richDocumentsEnabled: Bool = false + public var richDocumentsMimetypes: [String] = [] + public var activity: [String] = [] + public var notification: [String] = [] + public var filesUndelete: Bool = false + public var filesLockVersion: String = "" // NC 24 + public var filesComments: Bool = false // NC 20 + public var filesBigfilechunking: Bool = false + public var userStatusEnabled: Bool = false + public var externalSites: Bool = false + public var activityEnabled: Bool = false + public var groupfoldersEnabled: Bool = false // NC27 + public var assistantEnabled: Bool = false // NC28 + public var isLivePhotoServerAvailable: Bool = false // NC28 + public var securityGuardDiagnostics = false + public var forbiddenFileNames: [String] = [] + public var forbiddenFileNameBasenames: [String] = [] + public var forbiddenFileNameCharacters: [String] = [] + public var forbiddenFileNameExtensions: [String] = [] + public var recommendations: Bool = false + public var termsOfService: Bool = false + + public init() {} + } + + // MARK: - Public API + + public func appendCapabilitiesAsync(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 { + 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() + } + let group = DispatchGroup() + var result: Capabilities? + + group.enter() + Task.detached(priority: .userInitiated) { + result = await self.store.get(account) + group.leave() + } + + group.wait() + return result ?? Capabilities() + } +} diff --git a/Sources/NextcloudKit/NextcloudKit.swift b/Sources/NextcloudKit/NextcloudKit.swift index 3ff2257c..bb85c609 100644 --- a/Sources/NextcloudKit/NextcloudKit.swift +++ b/Sources/NextcloudKit/NextcloudKit.swift @@ -83,13 +83,12 @@ open class NextcloudKit { userId: String, password: String, userAgent: String, - nextcloudVersion: Int, httpMaximumConnectionsPerHost: Int = 6, httpMaximumConnectionsPerHostInDownload: Int = 6, httpMaximumConnectionsPerHostInUpload: Int = 6, groupIdentifier: String) { if nkCommonInstance.nksessions.filter({ $0.account == account }).first != nil { - return updateSession(account: account, urlBase: urlBase, userId: userId, password: password, userAgent: userAgent, nextcloudVersion: nextcloudVersion) + return updateSession(account: account, urlBase: urlBase, userId: userId, password: password, userAgent: userAgent) } let nkSession = NKSession( @@ -100,7 +99,6 @@ open class NextcloudKit { password: password, account: account, userAgent: userAgent, - nextcloudVersion: nextcloudVersion, groupIdentifier: groupIdentifier, httpMaximumConnectionsPerHost: httpMaximumConnectionsPerHost, httpMaximumConnectionsPerHostInDownload: httpMaximumConnectionsPerHostInDownload, @@ -116,7 +114,6 @@ open class NextcloudKit { userId: String? = nil, password: String? = nil, userAgent: String? = nil, - nextcloudVersion: Int? = nil, replaceWithAccount: String? = nil) { guard var nkSession = nkCommonInstance.nksessions.filter({ $0.account == account }).first else { return } if let urlBase { @@ -134,9 +131,6 @@ open class NextcloudKit { if let userAgent { nkSession.userAgent = userAgent } - if let nextcloudVersion { - nkSession.nextcloudVersion = nextcloudVersion - } if let replaceWithAccount { nkSession.account = replaceWithAccount }