diff --git a/Sources/NextcloudKit/NextcloudKit+API.swift b/Sources/NextcloudKit/NextcloudKit+API.swift index ceaec9e2..a56c89a9 100644 --- a/Sources/NextcloudKit/NextcloudKit+API.swift +++ b/Sources/NextcloudKit/NextcloudKit+API.swift @@ -31,6 +31,14 @@ public class NKNotifications: NSObject { } public extension NextcloudKit { + /// Checks if the specified server URL is reachable and returns the raw HTTP response. + /// Used to verify the availability and responsiveness of a Nextcloud server. + /// + /// Parameters: + /// - serverUrl: Full URL of the Nextcloud server to check. + /// - options: Optional request options (e.g. custom headers, queue). + /// - taskHandler: Closure to access the URLSessionTask (default is no-op). + /// - completion: Completion handler with the raw HTTP response and any NKError. func checkServer(serverUrl: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -50,8 +58,43 @@ public extension NextcloudKit { } } + /// Asynchronously checks the specified server URL and returns the HTTP response and error. + /// - Parameters: + /// - serverUrl: The URL of the server to check. + /// - options: Optional request options. + /// - taskHandler: Optional closure to access the session task. + /// - Returns: A tuple containing the raw response and NKError, with named values. + func checkServerAsync(serverUrl: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + checkServer(serverUrl: serverUrl, + options: options, + taskHandler: taskHandler) { responseData, error in + continuation.resume(returning: ( + responseData: responseData, + error: error + )) + } + } + } + // MARK: - + /// Executes a generic HTTP request using the given relative endpoint path and HTTP method. + /// Commonly used for flexible OCS or WebDAV API calls without dedicated wrappers. + /// + /// Parameters: + /// - endpoint: The relative API path (e.g. "ocs/v2.php/apps/...") to be appended to the base server URL. + /// - account: The Nextcloud account initiating the request. + /// - method: The HTTP method as a string ("GET", "POST", "DELETE", etc). + /// - options: Optional request options such as custom headers, versioning, queue. + /// - taskHandler: Optional closure to access the underlying URLSessionTask. + /// - completion: Completion handler returning the account, raw response, and any NKError. func generalWithEndpoint(_ endpoint: String, account: String, method: String, @@ -76,8 +119,49 @@ public extension NextcloudKit { } } + /// Asynchronously performs a generic request using the specified endpoint and method. + /// - Parameters: + /// - endpoint: Relative path to the server API. + /// - account: The account initiating the request. + /// - method: HTTP method string. + /// - options: Optional request configuration. + /// - taskHandler: Closure to access the URLSessionTask. + /// - Returns: A tuple with named values: account, raw response, and error. + func generalWithEndpointAsync(_ endpoint: String, + account: String, + method: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + generalWithEndpoint(endpoint, + account: account, + method: method, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + // MARK: - + /// Retrieves the list of external sites configured in the Nextcloud instance. + /// These are typically links to external services or resources displayed in the web UI. + /// + /// Parameters: + /// - account: The Nextcloud account making the request. + /// - options: Optional request options for custom headers, versioning, queue, etc. + /// - taskHandler: Closure to access the URLSessionTask (default is no-op). + /// - completion: Completion handler returning the account, list of external sites, response, and any NKError. func getExternalSite(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -116,14 +200,31 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the list of external sites for the specified account. + /// - Parameters: + /// - account: The Nextcloud account making the request. + /// - options: Optional request configuration. + /// - taskHandler: Closure to access the URLSessionTask. + /// - Returns: A tuple containing account, external sites array, raw response, and error. func getExternalSiteAsync(account: String, options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, externalSite: [NKExternalSite], responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation { continuation in + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + externalSite: [NKExternalSite], + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in getExternalSite(account: account, options: options, taskHandler: taskHandler) { account, externalSite, responseData, error in - continuation.resume(returning: (account, externalSite, responseData, error)) + continuation.resume(returning: ( + account: account, + externalSite: externalSite, + responseData: responseData, + error: error + )) } } } @@ -148,6 +249,13 @@ public extension NextcloudKit { case failure(NKError) } + /// Retrieves the status information of a Nextcloud server. + /// + /// Parameters: + /// - serverUrl: The base URL of the Nextcloud server (e.g., https://cloud.example.com). + /// - options: Optional request configuration (e.g., headers, queue, etc.). + /// - taskHandler: Callback for the underlying URLSessionTask. + /// - completion: Returns the raw response and a `ServerInfoResult` containing server status information or an error. func getServerStatus(serverUrl: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -199,20 +307,42 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the status information of a Nextcloud server. + /// - Parameters: + /// - serverUrl: The server's base URL (e.g., https://cloud.example.com). + /// - options: Optional request configuration (e.g., version, queue, headers). + /// - taskHandler: Optional callback to monitor the underlying URLSessionTask. + /// - Returns: A tuple containing the AFDataResponse and the ServerInfoResult. func getServerStatusAsync(serverUrl: String, options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (responseData: AFDataResponse?, result: ServerInfoResult) { - await withUnsafeContinuation { continuation in + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + responseData: AFDataResponse?, + result: ServerInfoResult + ) { + await withCheckedContinuation { continuation in getServerStatus(serverUrl: serverUrl, options: options, taskHandler: taskHandler) { responseData, result in - continuation.resume(returning: (responseData, result)) + continuation.resume(returning: ( + responseData: responseData, + result: result + )) } } } // MARK: - + /// Downloads a file preview (thumbnail) from the specified URL for a given Nextcloud account. + /// + /// Parameters: + /// - url: The full URL of the preview image to download. + /// - account: The Nextcloud account used for the request. + /// - etag: Optional entity tag used for cache validation. + /// - options: Optional request configuration (e.g., headers, queue, version). + /// - taskHandler: Callback for the underlying `URLSessionTask`. + /// - completion: Returns the account, raw response data, and an `NKError` representing the result of the operation. func downloadPreview(url: URL, account: String, etag: String? = nil, @@ -242,16 +372,54 @@ public extension NextcloudKit { } } + /// Asynchronously downloads a preview image (e.g., thumbnail) from the provided URL. + /// - Parameters: + /// - url: The full URL of the preview image. + /// - account: The Nextcloud account making the request. + /// - etag: Optional ETag used to validate cache. + /// - options: Additional request options including version, headers, and queues. + /// - taskHandler: Optional handler for monitoring the URLSessionTask. + /// - Returns: A tuple containing the account identifier, response data, and an `NKError` object. func downloadPreviewAsync(url: URL, account: String, - 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)) + etag: String? = nil, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + downloadPreview(url: url, + account: account, + etag: etag, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) } - }) + } } + /// Downloads a preview (thumbnail) of a file with specified dimensions and parameters. + /// + /// Parameters: + /// - fileId: The identifier of the file to generate a preview for. + /// - width: The desired width of the preview image (default is 1024). + /// - height: The desired height of the preview image (default is 1024). + /// - etag: Optional entity tag used for caching validation. + /// - crop: Indicates whether the image should be cropped (1 = true, default). + /// - cropMode: The cropping mode (default is "cover"). + /// - forceIcon: If set to 1, forces icon generation (default is 0). + /// - mimeFallback: If set to 1, fallback to MIME-type icon if preview is unavailable (default is 0). + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request configuration (headers, versioning, etc.). + /// - taskHandler: Callback for the `URLSessionTask`. + /// - completion: Returns the account, final width and height used, etag, response data, and any error encountered. func downloadPreview(fileId: String, width: Int = 1024, height: Int = 1024, @@ -291,17 +459,77 @@ public extension NextcloudKit { } } + /// Asynchronously downloads a preview by file ID with specified dimensions and parameters. + /// - Parameters: + /// - fileId: The unique identifier of the file. + /// - width: Desired preview width (default: 1024). + /// - height: Desired preview height (default: 1024). + /// - etag: Optional ETag for cache validation. + /// - crop: Crop flag (default: 1). + /// - cropMode: Crop mode (default: "cover"). + /// - forceIcon: Force icon flag (default: 0). + /// - mimeFallback: MIME fallback flag (default: 0). + /// - account: The account identifier. + /// - options: Optional request options. + /// - taskHandler: Optional closure to handle the URLSessionTask. + /// - Returns: A tuple containing account, dimensions, ETag, response and error. func downloadPreviewAsync(fileId: String, + width: Int = 1024, + height: Int = 1024, etag: String? = nil, + crop: Int = 1, + cropMode: String = "cover", + forceIcon: Int = 0, + mimeFallback: Int = 0, account: String, - 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)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + width: Int, + height: Int, + etag: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + downloadPreview(fileId: fileId, + width: width, + height: height, + etag: etag, + crop: crop, + cropMode: cropMode, + forceIcon: forceIcon, + mimeFallback: mimeFallback, + account: account, + options: options, + taskHandler: taskHandler) { account, w, h, tag, responseData, error in + continuation.resume(returning: ( + account: account, + width: w, + height: h, + etag: tag, + responseData: responseData, + error: error + )) } - }) + } } + /// Downloads a preview (thumbnail) for a file located in the trashbin. + /// + /// Parameters: + /// - fileId: The identifier of the trashed file. + /// - width: Desired width of the preview image (default is 512). + /// - height: Desired height of the preview image (default is 512). + /// - crop: Indicates whether the image should be cropped (1 = true, default). + /// - cropMode: The cropping mode (e.g., "cover"). + /// - forceIcon: Forces use of the filetype icon instead of generating a preview (0 = false, default). + /// - mimeFallback: Uses MIME-type fallback if preview is unavailable (0 = false, default). + /// - account: The Nextcloud account making the request. + /// - options: Request customization options (headers, queue, version, etc.). + /// - taskHandler: Callback to inspect the underlying URLSessionTask. + /// - completion: Returns the account, final width/height, preview response data, and NKError. func downloadTrashPreview(fileId: String, width: Int = 512, height: Int = 512, @@ -335,6 +563,70 @@ public extension NextcloudKit { } } + /// Asynchronously downloads a preview of a trashed file using specified rendering options. + /// - Parameters: + /// - fileId: The identifier of the trashed file. + /// - width: Desired width of the preview image. + /// - height: Desired height of the preview image. + /// - crop: Whether to crop the image (1 = true, 0 = false). + /// - cropMode: Cropping mode to apply (e.g., "cover"). + /// - forceIcon: Whether to return an icon instead of a generated preview. + /// - mimeFallback: Whether to fallback to MIME-type icon if no preview is available. + /// - account: Account making the preview request. + /// - options: Optional request configuration (queue, headers, etc.). + /// - taskHandler: Handler to observe the URLSessionTask. + /// - Returns: A tuple with account, final width and height used, responseData, and NKError. + func downloadTrashPreviewAsync(fileId: String, + width: Int = 512, + height: Int = 512, + crop: Int = 1, + cropMode: String = "cover", + forceIcon: Int = 0, + mimeFallback: Int = 0, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + width: Int, + height: Int, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + downloadTrashPreview(fileId: fileId, + width: width, + height: height, + crop: crop, + cropMode: cropMode, + forceIcon: forceIcon, + mimeFallback: mimeFallback, + account: account, + options: options, + taskHandler: taskHandler) { account, resolvedWidth, resolvedHeight, responseData, error in + continuation.resume(returning: ( + account: account, + width: resolvedWidth, + height: resolvedHeight, + responseData: responseData, + error: error + )) + } + } + } + + /// Downloads a user's avatar image from the server and optionally stores it locally. + /// + /// Parameters: + /// - user: The user identifier for whom the avatar is requested. + /// - fileNameLocalPath: The local file path where the avatar image will be saved. + /// - sizeImage: The size of the avatar to request (in pixels). + /// - avatarSizeRounded: If greater than 0, the avatar will be rounded to this size (in pixels). + /// - etag: Optional ETag string to validate the cache. + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request options (queue, headers, etc.). + /// - taskHandler: Callback for the underlying URLSessionTask. + /// - completion: Returns the account, avatar image, original image, ETag, response data, and NKError. func downloadAvatar(user: String, fileNameLocalPath: String, sizeImage: Int, @@ -436,6 +728,17 @@ public extension NextcloudKit { } } + /// Asynchronously downloads an avatar image for the specified user. + /// - Parameters: + /// - user: The username associated with the avatar. + /// - fileNameLocalPath: Path on disk to save the avatar. + /// - sizeImage: Desired size (in pixels) of the avatar image. + /// - avatarSizeRounded: Optional rounding for avatar (e.g., 128px). + /// - etag: Optional ETag for cache validation. + /// - account: The Nextcloud account to perform the download. + /// - options: Request configuration (queue, headers, etc.). + /// - taskHandler: Optional observer for the download task. + /// - Returns: A tuple with account, final avatar image, original image, resulting ETag, response and NKError. func downloadAvatarAsync(user: String, fileNameLocalPath: String, sizeImage: Int, @@ -443,8 +746,16 @@ 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) { - await withUnsafeContinuation { continuation in + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + imageAvatar: UIImage?, + imageOriginal: UIImage?, + etag: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in downloadAvatar(user: user, fileNameLocalPath: fileNameLocalPath, sizeImage: sizeImage, @@ -453,11 +764,26 @@ public extension NextcloudKit { account: account, options: options, taskHandler: taskHandler) { account, imageAvatar, imageOriginal, etag, responseData, error in - continuation.resume(returning: (account, imageAvatar, imageOriginal, etag, responseData, error)) + continuation.resume(returning: ( + account: account, + imageAvatar: imageAvatar, + imageOriginal: imageOriginal, + etag: etag, + responseData: responseData, + error: error + )) } } } + /// Downloads generic raw content from a given server URL using the specified account. + /// + /// Parameters: + /// - serverUrl: The full URL string of the content to be downloaded. + /// - account: The Nextcloud account to use for the request. + /// - options: Optional configuration including headers, queue, and version. + /// - taskHandler: Optional callback for monitoring the URLSessionTask. + /// - completion: Returns the account, response data, and NKError representing the outcome. func downloadContent(serverUrl: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -483,8 +809,46 @@ public extension NextcloudKit { } } + /// Asynchronously downloads raw content from a specified URL. + /// - Parameters: + /// - serverUrl: The direct URL of the content to download. + /// - account: The Nextcloud account used to authenticate the request. + /// - options: Request customization such as headers, queue, etc. + /// - taskHandler: Optional monitoring for the URLSession task. + /// - Returns: A tuple containing the account, response data, and resulting NKError. + func downloadContentAsync(serverUrl: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + downloadContent(serverUrl: serverUrl, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + // MARK: - + /// Retrieves user profile metadata for a specific user in the Nextcloud instance. + /// + /// Parameters: + /// - account: The account used to perform the request. + /// - userId: The user identifier whose metadata is being retrieved. + /// - options: Additional request configuration (e.g., headers, API version, execution queue). + /// - taskHandler: Optional callback invoked with the underlying URLSessionTask. + /// - completion: Returns the account, parsed user profile (`NKUserProfile`), response metadata, and any `NKError` encountered. func getUserMetadata(account: String, userId: String, options: NKRequestOptions = NKRequestOptions(), @@ -520,16 +884,45 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the metadata for a specific user profile. + /// - Parameters: + /// - account: The Nextcloud account making the request. + /// - userId: The ID of the user whose metadata is being requested. + /// - options: Optional request configuration (headers, version, etc.). + /// - taskHandler: Optional handler for observing the URLSessionTask. + /// - Returns: A tuple with the account, user profile, response data, and resulting error. func getUserMetadataAsync(account: String, userId: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, userProfile: NKUserProfile?, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation({ continuation in - NextcloudKit.shared.getUserMetadata(account: account, userId: userId) { account, userProfile, responseData, error in - continuation.resume(returning: (account: account, userProfile: userProfile, responseData: responseData, error: error)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + userProfile: NKUserProfile?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getUserMetadata(account: account, + userId: userId, + options: options, + taskHandler: taskHandler) { account, userProfile, responseData, error in + continuation.resume(returning: ( + account: account, + userProfile: userProfile, + responseData: responseData, + error: error + )) } - }) + } } + /// Fetches the metadata of the currently authenticated user. + /// + /// Parameters: + /// - account: The Nextcloud account performing the request. + /// - options: Additional request configuration (e.g., custom headers, API version, execution queue). + /// - taskHandler: Optional callback invoked with the underlying URLSessionTask. + /// - completion: Returns the account, parsed user profile (`NKUserProfile`), response metadata, and any `NKError` encountered. func getUserProfile(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -564,6 +957,35 @@ public extension NextcloudKit { } } + /// Asynchronously fetches the profile metadata of the currently logged-in user. + /// - Parameters: + /// - account: The Nextcloud account making the request. + /// - options: Optional request configuration (e.g. headers, version, etc.). + /// - taskHandler: Optional handler for observing the URLSessionTask. + /// - Returns: A tuple containing the account, user profile object, full response data, and any NKError. + func getUserProfileAsync(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + userProfile: NKUserProfile?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getUserProfile(account: account, + options: options, + taskHandler: taskHandler) { account, userProfile, responseData, error in + continuation.resume(returning: ( + account: account, + userProfile: userProfile, + responseData: responseData, + error: error + )) + } + } + } + private func getUserProfile(json: JSON) -> NKUserProfile? { let ocs = json["ocs"] let data = ocs["data"] @@ -610,6 +1032,15 @@ public extension NextcloudKit { } // MARK: - + /// Checks the remote wipe status for a specific account and token. + /// + /// Parameters: + /// - serverUrl: The base server URL to perform the request. + /// - token: The authentication or wipe token to validate. + /// - account: The Nextcloud account performing the request. + /// - options: Optional configuration for the request (e.g., headers, version, queue). + /// - taskHandler: Optional callback to observe the underlying URLSessionTask. + /// - completion: Returns the account, wipe status (true if a wipe is required), raw response data, and NKError if any. func getRemoteWipeStatus(serverUrl: String, token: String, account: String, @@ -645,6 +1076,50 @@ public extension NextcloudKit { } } + /// Asynchronously checks the remote wipe status for the given account and token. + /// - Parameters: + /// - serverUrl: Base server URL used for the API request. + /// - token: Token used to query remote wipe status. + /// - account: Nextcloud account identifier. + /// - options: Request options such as headers, version, and dispatch queue. + /// - taskHandler: Callback for observing the URLSessionTask, if needed. + /// - Returns: A tuple with the account, wipe status flag, response data, and NKError. + func getRemoteWipeStatusAsync(serverUrl: String, + token: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + wipe: Bool, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getRemoteWipeStatus(serverUrl: serverUrl, + token: token, + account: account, + options: options, + taskHandler: taskHandler) { account, wipe, responseData, error in + continuation.resume(returning: ( + account: account, + wipe: wipe, + responseData: responseData, + error: error + )) + } + } + } + + /// Notifies the server that the remote wipe operation has been completed. + /// + /// Parameters: + /// - serverUrl: The base server URL used to send the wipe completion notification. + /// - token: The remote wipe token associated with the account. + /// - account: The Nextcloud account that performed the wipe. + /// - options: Optional configuration for request (headers, queue, version, etc.). + /// - taskHandler: Callback for the underlying URLSessionTask if monitoring is needed. + /// - completion: Returns the account, raw response data, and NKError. func setRemoteWipeCompletition(serverUrl: String, token: String, account: String, @@ -678,8 +1153,53 @@ public extension NextcloudKit { } } + /// Asynchronously notifies the server that remote wipe has been completed for the given account and token. + /// - Parameters: + /// - serverUrl: The base URL of the Nextcloud server. + /// - token: Remote wipe token associated with the account. + /// - account: Identifier of the Nextcloud account. + /// - options: Configuration object for headers, versioning, and dispatching. + /// - taskHandler: Optional observer for the created URLSessionTask. + /// - Returns: A tuple with account, raw response data, and NKError. + func setRemoteWipeCompletitionAsync(serverUrl: String, + token: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + setRemoteWipeCompletition(serverUrl: serverUrl, + token: token, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + // MARK: - + /// Retrieves a list of activities for the current account. + /// + /// Parameters: + /// - since: The timestamp (as Unix epoch) to fetch activities after. + /// - limit: The maximum number of activities to retrieve. + /// - objectId: Optional object ID to filter activities (e.g., file ID). + /// - objectType: Optional object type to filter (e.g., "files"). + /// - previews: Whether to include preview data for activities. + /// - account: The Nextcloud account requesting the activity feed. + /// - options: Optional request configuration (headers, queue, version, etc.). + /// - taskHandler: Callback for the underlying URLSessionTask. + /// - completion: Returns the account, array of NKActivity objects, the timestamp of the first known activity, last returned activity, raw response, and NKError. func getActivity(since: Int, limit: Int, objectId: String?, @@ -776,8 +1296,64 @@ public extension NextcloudKit { } } + /// Asynchronously fetches the list of activities from the server. + /// + /// - Parameters: + /// - since: Epoch timestamp for filtering activities (only newer ones will be returned). + /// - limit: Maximum number of activities to retrieve. + /// - objectId: Optional object ID to filter (e.g., file or folder ID). + /// - objectType: Optional object type (e.g., "files"). + /// - previews: Whether to include thumbnails/previews for the activities. + /// - account: The Nextcloud account to use for authentication. + /// - options: Request customization including queue and headers. + /// - taskHandler: Optional callback for URLSession task monitoring. + /// - Returns: A tuple containing account, activities array, first known activity timestamp, last given activity timestamp, full response, and error. + func getActivityAsync(since: Int, + limit: Int, + objectId: String?, + objectType: String?, + previews: Bool, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + activities: [NKActivity], + activityFirstKnown: Int, + activityLastGiven: Int, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getActivity(since: since, + limit: limit, + objectId: objectId, + objectType: objectType, + previews: previews, + account: account, + options: options, + taskHandler: taskHandler) { account, activities, firstKnown, lastGiven, responseData, error in + continuation.resume(returning: ( + account: account, + activities: activities, + activityFirstKnown: firstKnown, + activityLastGiven: lastGiven, + responseData: responseData, + error: error + )) + } + } + } + // MARK: - + /// Retrieves all notifications associated with the current account. + /// + /// Parameters: + /// - account: The Nextcloud account from which to retrieve notifications. + /// - options: Optional request configuration (headers, queue, version, etc.). + /// - taskHandler: Callback for the underlying URLSessionTask. + /// - completion: Returns the account, list of NKNotifications, raw response data, and NKError. func getNotifications(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -845,6 +1421,46 @@ public extension NextcloudKit { } } + /// Asynchronously fetches notifications for the given account. + /// + /// - Parameters: + /// - account: The Nextcloud account used to authenticate the request. + /// - options: Request configuration including queue and headers. + /// - taskHandler: Optional callback to monitor the URLSessionTask. + /// - Returns: A tuple containing the account, notifications array (optional), response data (optional), and the resulting NKError. + func getNotificationsAsync(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + notifications: [NKNotifications]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getNotifications(account: account, + options: options, + taskHandler: taskHandler) { account, notifications, responseData, error in + continuation.resume(returning: ( + account: account, + notifications: notifications, + responseData: responseData, + error: error + )) + } + } + } + + /// Performs an action on a specific notification by ID (e.g., mark as read or delete). + /// + /// Parameters: + /// - serverUrl: Optional custom server URL override. If nil, the default account URL is used. + /// - idNotification: The ID of the notification to act upon. + /// - method: The HTTP method to use for the action (e.g., "DELETE" or "POST"). + /// - account: The account associated with the notification. + /// - options: Optional request configuration (headers, queue, version, etc.). + /// - taskHandler: Callback for the underlying URLSessionTask. + /// - completion: Returns the account, raw response data, and NKError result. func setNotification(serverUrl: String?, idNotification: Int, method: String, @@ -882,8 +1498,53 @@ public extension NextcloudKit { } } + /// Asynchronously sets or deletes a notification by its ID. + /// + /// - Parameters: + /// - serverUrl: Optional server URL override for the request. + /// - idNotification: The unique identifier of the notification to process. + /// - method: HTTP method to execute ("POST", "DELETE", etc.). + /// - account: The account context for the operation. + /// - options: Request options including queue, headers, etc. + /// - taskHandler: Optional callback to monitor the task. + /// - Returns: A tuple containing the account, response data, and NKError. + func setNotificationAsync(serverUrl: String?, + idNotification: Int, + method: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + setNotification(serverUrl: serverUrl, + idNotification: idNotification, + method: method, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + // MARK: - + // Fetches a direct download URL for a given file ID. + // + // Parameters: + // - fileId: The unique identifier of the file to download. + // - account: The account used to perform the request. + // - options: Optional request configuration (headers, queue, version, etc.). + // - taskHandler: Callback triggered with the URLSessionTask created. + // - completion: Returns the account, the direct download URL (if available), raw response data, and NKError result. func getDirectDownload(fileId: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -917,8 +1578,49 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the direct download link for a specified file. + /// + /// - Parameters: + /// - fileId: The file identifier for which to get the direct download URL. + /// - account: The account to use for the request. + /// - options: Optional request settings (e.g., headers, versioning). + /// - taskHandler: Callback to observe the URLSessionTask. + /// - Returns: A tuple containing the account, the direct download URL, the raw response, and NKError. + func getDirectDownloadAsync(fileId: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + url: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getDirectDownload(fileId: fileId, + account: account, + options: options, + taskHandler: taskHandler) { account, url, responseData, error in + continuation.resume(returning: ( + account: account, + url: url, + responseData: responseData, + error: error + )) + } + } + } + // MARK: - + // Sends client diagnostics data to the remote server. + // + // Parameters: + // - data: The raw diagnostic payload to be sent. + // - account: The account used for the request. + // - options: Optional request configuration (e.g., headers, queue, version). + // - taskHandler: Callback triggered with the underlying URLSessionTask. + // - completion: Returns the account, raw response data, and NKError result. func sendClientDiagnosticsRemoteOperation(data: Data, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -955,20 +1657,33 @@ public extension NextcloudKit { } } - func sendClientDiagnosticsRemoteOperationAsync( - data: Data, + /// Asynchronously sends diagnostic data to the server. + /// + /// - Parameters: + /// - data: Raw diagnostic information to upload. + /// - account: The account associated with the request. + /// - options: Optional request configuration parameters. + /// - taskHandler: Callback for tracking the associated URLSessionTask. + /// - Returns: A tuple containing the account, the response data, and the NKError result. + func sendClientDiagnosticsRemoteOperationAsync(data: Data, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( account: String, - options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } - ) async -> (responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation { continuation in - sendClientDiagnosticsRemoteOperation( - data: data, - account: account, - options: options, - taskHandler: taskHandler - ) { _, responseData, error in - continuation.resume(returning: (responseData, error)) + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + sendClientDiagnosticsRemoteOperation(data: data, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) } } } diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index e9eb9268..1b9bdf6a 100644 --- a/Sources/NextcloudKit/NextcloudKit+Assistant.swift +++ b/Sources/NextcloudKit/NextcloudKit+Assistant.swift @@ -7,6 +7,14 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Retrieves the list of supported text processing task types from the server. + /// These types define the kinds of operations (e.g., summarization, translation) supported by the assistant API. + /// + /// Parameters: + /// - account: The Nextcloud account initiating the request. + /// - options: Optional configuration for the HTTP request. + /// - taskHandler: Optional closure to access the underlying URLSessionTask. + /// - completion: Completion handler providing the account, available task types, response, and NKError. func textProcessingGetTypes(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -40,6 +48,47 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves available text processing task types from the server. + /// - Parameters: + /// - account: The Nextcloud account initiating the request. + /// - options: Optional request options. + /// - taskHandler: Closure to access the session task. + /// - Returns: A tuple with named values for account, list of task types, response, and error. + func textProcessingGetTypesAsync(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + types: [NKTextProcessingTaskType]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + textProcessingGetTypes(account: account, + options: options, + taskHandler: taskHandler) { account, types, responseData, error in + continuation.resume(returning: ( + account: account, + types: types, + responseData: responseData, + error: error + )) + } + } + } + + /// Schedules a new text processing task on the server (e.g., translation, summary, etc.). + /// The request includes the input text, the type of task to execute, and a unique identifier. + /// + /// Parameters: + /// - input: The raw input string to be processed. + /// - typeId: The identifier of the task type (e.g., "summarize", "translate"). + /// - appId: The application identifier (default is "assistant"). + /// - identifier: A client-side unique string to track this task. + /// - account: The Nextcloud account executing the request. + /// - options: Optional request configuration (headers, queue, etc.). + /// - taskHandler: Optional closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, resulting task object, response, and any NKError. func textProcessingSchedule(input: String, typeId: String, appId: String = "assistant", @@ -78,6 +127,56 @@ public extension NextcloudKit { } } + /// Asynchronously schedules a text processing task on the server. + /// - Parameters: + /// - input: Input string to process. + /// - typeId: Task type identifier. + /// - appId: Optional app ID, defaults to "assistant". + /// - identifier: Unique task identifier. + /// - account: Nextcloud account. + /// - options: Request configuration. + /// - taskHandler: Optional access to the URLSessionTask. + /// - Returns: A tuple with named values for account, task object, raw response, and error. + func textProcessingScheduleAsync(input: String, + typeId: String, + appId: String = "assistant", + identifier: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + task: NKTextProcessingTask?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + textProcessingSchedule(input: input, + typeId: typeId, + appId: appId, + identifier: identifier, + account: account, + options: options, + taskHandler: taskHandler) { account, task, responseData, error in + continuation.resume(returning: ( + account: account, + task: task, + responseData: responseData, + error: error + )) + } + } + } + + /// Retrieves the current status and data of a previously scheduled text processing task. + /// Useful for polling or checking the result of a long-running task by its unique ID. + /// + /// Parameters: + /// - taskId: The server-side ID of the text processing task to retrieve. + /// - account: The Nextcloud account making the request. + /// - options: Optional request configuration. + /// - taskHandler: Optional closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, task object, raw response, and NKError. func textProcessingGetTask(taskId: Int, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -112,6 +211,47 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the details of a specific text processing task. + /// - Parameters: + /// - taskId: The ID of the task to fetch. + /// - account: The account used for the request. + /// - options: Optional configuration. + /// - taskHandler: Closure to access the session task. + /// - Returns: A tuple with named values for account, task object, response, and error. + func textProcessingGetTaskAsync(taskId: Int, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + task: NKTextProcessingTask?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + textProcessingGetTask(taskId: taskId, + account: account, + options: options, + taskHandler: taskHandler) { account, task, responseData, error in + continuation.resume(returning: ( + account: account, + task: task, + responseData: responseData, + error: error + )) + } + } + } + + /// Deletes a specific text processing task on the server. + /// This is used to cancel or clean up tasks that are no longer needed. + /// + /// Parameters: + /// - taskId: The ID of the task to be deleted. + /// - account: The Nextcloud account making the request. + /// - options: Optional request configuration. + /// - taskHandler: Optional closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, deleted task object, raw response, and NKError. func textProcessingDeleteTask(taskId: Int, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -146,6 +286,47 @@ public extension NextcloudKit { } } + /// Asynchronously deletes a scheduled text processing task from the server. + /// - Parameters: + /// - taskId: ID of the task to delete. + /// - account: Account executing the deletion. + /// - options: Request options. + /// - taskHandler: Callback for the underlying URLSessionTask. + /// - Returns: A tuple with named values for account, deleted task object, response, and error. + func textProcessingDeleteTaskAsync(taskId: Int, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + task: NKTextProcessingTask?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + textProcessingDeleteTask(taskId: taskId, + account: account, + options: options, + taskHandler: taskHandler) { account, task, responseData, error in + continuation.resume(returning: ( + account: account, + task: task, + responseData: responseData, + error: error + )) + } + } + } + + /// Retrieves a list of all text processing tasks associated with a specific app ID. + /// This includes both pending and completed tasks, useful for tracking the assistant's activity. + /// + /// Parameters: + /// - appId: Identifier of the application requesting the tasks (e.g., "assistant"). + /// - account: The Nextcloud account making the request. + /// - options: Optional HTTP request configuration. + /// - taskHandler: Optional closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, task list, raw response, and NKError. func textProcessingTaskList(appId: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -179,6 +360,36 @@ public extension NextcloudKit { } } } -} - + /// Asynchronously retrieves all text processing tasks associated with a specific app ID. + /// - Parameters: + /// - appId: The application identifier to filter the task list. + /// - account: The account performing the request. + /// - options: Optional request configuration. + /// - taskHandler: Callback for the URLSessionTask. + /// - Returns: A tuple with named values for account, task list, response, and error. + func textProcessingTaskListAsync(appId: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + task: [NKTextProcessingTask]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + textProcessingTaskList(appId: appId, + account: account, + options: options, + taskHandler: taskHandler) { account, task, responseData, error in + continuation.resume(returning: ( + account: account, + task: task, + responseData: responseData, + error: error + )) + } + } + } +} diff --git a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift index e3460146..1f544c76 100644 --- a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift +++ b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift @@ -7,6 +7,15 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Retrieves the list of supported task types for a specific account and task category. + /// Typically used to discover available AI or text processing capabilities. + /// + /// Parameters: + /// - account: The Nextcloud account making the request. + /// - supportedTaskType: Type of tasks to retrieve, default is "Text". + /// - options: Optional HTTP request configuration. + /// - taskHandler: Optional closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, list of supported types, raw response, and NKError. func textProcessingGetTypesV2(account: String, supportedTaskType: String = "Text", options: NKRequestOptions = NKRequestOptions(), @@ -45,6 +54,48 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the supported task types for the given account and category. + /// - Parameters: + /// - account: Account performing the request. + /// - supportedTaskType: The task category to filter by (default: "Text"). + /// - options: Optional configuration. + /// - taskHandler: Callback for the underlying URLSessionTask. + /// - Returns: A tuple with named values for account, supported types, response, and error. + func textProcessingGetTypesV2Async(account: String, + supportedTaskType: String = "Text", + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + types: [TaskTypeData]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + textProcessingGetTypesV2(account: account, + supportedTaskType: supportedTaskType, + options: options, + taskHandler: taskHandler) { account, types, responseData, error in + continuation.resume(returning: ( + account: account, + types: types, + responseData: responseData, + error: error + )) + } + } + } + + /// Schedules a new text processing task for a specific account and task type. + /// Useful for initiating assistant-based text analysis, generation, or transformation. + /// + /// Parameters: + /// - input: The input text to be processed. + /// - taskType: The specific task type to execute (e.g., summarization, sentiment analysis). + /// - account: The Nextcloud account initiating the task. + /// - options: Optional HTTP request configuration. + /// - taskHandler: Optional closure to access the underlying URLSessionTask. + /// - completion: Completion handler returning the account, scheduled task, raw response, and NKError. func textProcessingScheduleV2(input: String, taskType: TaskTypeData, account: String, @@ -83,6 +134,50 @@ public extension NextcloudKit { } } + /// Asynchronously schedules a new text processing task using the specified task type. + /// - Parameters: + /// - input: Input text to be processed. + /// - taskType: Type of task to be executed. + /// - account: The account performing the scheduling. + /// - options: Optional configuration. + /// - taskHandler: Callback to access the associated URLSessionTask. + /// - Returns: A tuple with named values for account, scheduled task, response, and error. + func textProcessingScheduleV2Async(input: String, + taskType: TaskTypeData, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + task: AssistantTask?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + textProcessingScheduleV2(input: input, + taskType: taskType, + account: account, + options: options, + taskHandler: taskHandler) { account, task, responseData, error in + continuation.resume(returning: ( + account: account, + task: task, + responseData: responseData, + error: error + )) + } + } + } + + /// Retrieves all scheduled text processing tasks of a specific type for the given account. + /// Useful for listing and tracking tasks like summarization, transcription, or classification. + /// + /// Parameters: + /// - taskType: Identifier of the task type to filter tasks (e.g., "Text"). + /// - account: The Nextcloud account performing the request. + /// - options: Optional HTTP request configuration. + /// - taskHandler: Optional closure to access the underlying URLSessionTask. + /// - completion: Completion handler returning the account, list of tasks, raw response, and NKError. func textProcessingGetTasksV2(taskType: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -117,6 +212,47 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves a list of scheduled text processing tasks for a specific type. + /// - Parameters: + /// - taskType: Type of the tasks to query. + /// - account: The account performing the query. + /// - options: Optional configuration. + /// - taskHandler: Callback to access the associated URLSessionTask. + /// - Returns: A tuple with named values for account, task list, response, and error. + func textProcessingGetTasksV2Async(taskType: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + tasks: TaskList?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + textProcessingGetTasksV2(taskType: taskType, + account: account, + options: options, + taskHandler: taskHandler) { account, tasks, responseData, error in + continuation.resume(returning: ( + account: account, + tasks: tasks, + responseData: responseData, + error: error + )) + } + } + } + + /// Deletes a scheduled text processing task with a specific identifier. + /// Useful for canceling tasks that are no longer needed or invalid. + /// + /// Parameters: + /// - taskId: The unique identifier of the task to delete. + /// - account: The Nextcloud account executing the deletion. + /// - options: Optional HTTP request configuration. + /// - taskHandler: Optional closure to access the underlying URLSessionTask. + /// - completion: Completion handler returning the account, raw response, and NKError. func textProcessingDeleteTaskV2(taskId: Int64, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -148,6 +284,36 @@ public extension NextcloudKit { } } } + + /// Asynchronously deletes a text processing task by ID for the specified account. + /// - Parameters: + /// - taskId: ID of the task to be deleted. + /// - account: The account performing the operation. + /// - options: Optional configuration. + /// - taskHandler: Callback to access the associated URLSessionTask. + /// - Returns: A tuple with named values for account, response, and error. + func textProcessingDeleteTaskV2Async(taskId: Int64, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + textProcessingDeleteTaskV2(taskId: taskId, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } } diff --git a/Sources/NextcloudKit/NextcloudKit+Comments.swift b/Sources/NextcloudKit/NextcloudKit+Comments.swift index da6e4ead..25d25a2d 100644 --- a/Sources/NextcloudKit/NextcloudKit+Comments.swift +++ b/Sources/NextcloudKit/NextcloudKit+Comments.swift @@ -6,6 +6,15 @@ import Foundation import Alamofire public extension NextcloudKit { + /// Retrieves all comments associated with a specific file from the server. + /// This is typically used in collaboration features to display user discussions or annotations. + /// + /// - Parameters: + /// - fileId: Identifier of the file whose comments are being retrieved. + /// - account: The Nextcloud account requesting the comments. + /// - options: Optional request customization (headers, timeout, etc.). + /// - taskHandler: Optional closure to access the underlying URLSessionTask. + /// - completion: Completion handler returning the account, comment list, raw response, and NKError. func getComments(fileId: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -51,6 +60,48 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the list of comments for a given file ID. + /// - Parameters: + /// - fileId: File identifier to fetch comments for. + /// - account: The account executing the request. + /// - options: Optional configuration for the HTTP request. + /// - taskHandler: Callback for accessing the URLSessionTask. + /// - Returns: A tuple with named values for account, comment list, response, and error. + func getCommentsAsync(fileId: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + items: [NKComments]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getComments(fileId: fileId, + account: account, + options: options, + taskHandler: taskHandler) { account, items, responseData, error in + continuation.resume(returning: ( + account: account, + items: items, + responseData: responseData, + error: error + )) + } + } + } + + /// Adds a new comment to a specific file. + /// Useful for enabling collaboration or user discussions directly on file items. + /// + /// - Parameters: + /// - fileId: Identifier of the file to which the comment will be added. + /// - message: The content of the comment to post. + /// - account: The Nextcloud account posting the comment. + /// - options: Optional HTTP configuration (headers, timeout, etc.). + /// - taskHandler: Optional callback to access the URLSessionTask. + /// - completion: Completion handler with account, response, and error. func putComments(fileId: String, message: String, account: String, @@ -90,6 +141,50 @@ public extension NextcloudKit { } } + /// Asynchronously posts a new comment to a file. + /// - Parameters: + /// - fileId: The file to comment on. + /// - message: The comment body. + /// - account: The account sending the request. + /// - options: Optional network options. + /// - taskHandler: Callback to monitor the network task. + /// - Returns: A tuple with account, response data, and any resulting error. + func putCommentsAsync(fileId: String, + message: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + putComments(fileId: fileId, + message: message, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + + /// Updates the content of an existing comment on a file. + /// Useful for editing or correcting previously posted comments. + /// + /// - Parameters: + /// - fileId: Identifier of the file that contains the comment. + /// - messageId: Identifier of the specific comment to be updated. + /// - message: The new content to replace the old comment. + /// - account: The Nextcloud account performing the update. + /// - options: Optional HTTP configuration (e.g., headers, timeout). + /// - taskHandler: Optional callback to inspect the created URLSessionTask. + /// - completion: Completion handler returning account, response, and NKError. func updateComments(fileId: String, messageId: String, message: String, @@ -131,6 +226,52 @@ public extension NextcloudKit { } } + /// Asynchronously updates a specific comment on a file. + /// - Parameters: + /// - fileId: File containing the comment. + /// - messageId: ID of the comment to be updated. + /// - message: New content of the comment. + /// - account: User account executing the update. + /// - options: Optional request configuration. + /// - taskHandler: Callback for accessing the request task. + /// - Returns: A tuple with account, response data, and error. + func updateCommentsAsync(fileId: String, + messageId: String, + message: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + updateComments(fileId: fileId, + messageId: messageId, + message: message, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + + /// Deletes a specific comment from a file on the server for a given Nextcloud account. + /// It performs an HTTP request (typically DELETE) and returns the result through a completion handler. + /// + /// - Parameters: + /// - fileId: The identifier of the file the comment belongs to. + /// - messageId: The identifier of the comment to be deleted. + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request options such as custom headers or retry policy (default is empty). + /// - taskHandler: A closure to access the underlying URLSessionTask, useful for progress or cancellation. + /// - completion: Completion handler returning the account, the raw response (if any), and an NKError. func deleteComments(fileId: String, messageId: String, account: String, @@ -158,6 +299,48 @@ public extension NextcloudKit { } } + /// Asynchronously deletes a comment from a file. + /// - Parameters: + /// - fileId: File containing the comment. + /// - messageId: ID of the comment to be deleted. + /// - account: User account performing the deletion. + /// - options: Additional configuration for the HTTP request. + /// - taskHandler: Optional handler for the URLSessionTask. + /// - Returns: A tuple with account, server response, and an NKError. + func deleteCommentsAsync(fileId: String, + messageId: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + deleteComments(fileId: fileId, + messageId: messageId, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + + /// Marks all comments on a given file as read for the specified Nextcloud account. + /// It performs an HTTP request (likely POST or PUT) to update the read status on the server. + /// + /// - Parameters: + /// - fileId: The identifier of the file whose comments should be marked as read. + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request options (default is empty). + /// - taskHandler: A closure to access the underlying URLSessionTask (default is no-op). + /// - completion: Completion handler returning the account, the raw response, and any NKError. func markAsReadComments(fileId: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -196,4 +379,34 @@ public extension NextcloudKit { } } } + + /// Asynchronously marks all comments on a file as read. + /// - Parameters: + /// - fileId: File whose comments should be marked as read. + /// - account: The account executing the read marking. + /// - options: Optional configuration for the request. + /// - taskHandler: Optional handler for the URLSessionTask. + /// - Returns: A tuple containing the account, response data, and any NKError. + func markAsReadCommentsAsync(fileId: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + markAsReadComments(fileId: fileId, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } } diff --git a/Sources/NextcloudKit/NextcloudKit+Dashboard.swift b/Sources/NextcloudKit/NextcloudKit+Dashboard.swift index d21249e2..0f9c15e5 100644 --- a/Sources/NextcloudKit/NextcloudKit+Dashboard.swift +++ b/Sources/NextcloudKit/NextcloudKit+Dashboard.swift @@ -7,6 +7,15 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Retrieves the list of dashboard widgets available for the specified Nextcloud account. + /// This typically calls the dashboard API endpoint and returns a list of `NCCDashboardWidget` items. + /// + /// Parameters: + /// - account: The Nextcloud account performing the request. + /// - options: Optional request options such as custom headers or retry policy (default is empty). + /// - request: A closure that receives the underlying Alamofire `DataRequest`, useful for inspection or mutation. + /// - taskHandler: A closure to access the `URLSessionTask` for progress or cancellation control. + /// - completion: Completion handler returning the account, list of widgets, the raw response, and any NKError. func getDashboardWidget(account: String, options: NKRequestOptions = NKRequestOptions(), request: @escaping (DataRequest?) -> Void = { _ in }, @@ -42,29 +51,48 @@ public extension NextcloudKit { options.queue.async { request(dashboardRequest) } } + /// Asynchronously fetches the dashboard widgets available for a specific account. + /// - Parameters: + /// - account: The account from which to fetch the widgets. + /// - options: Optional configuration for the request. + /// - request: Optional handler to capture the `DataRequest`. + /// - taskHandler: Optional handler for the `URLSessionTask`. + /// - Returns: A tuple with the account, list of widgets, raw response, and NKError. func getDashboardWidgetAsync(account: String, options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, - dashboardWidgets: [NCCDashboardWidget]?, - responseData: AFDataResponse?, - request: DataRequest?, - task: URLSessionTask?, - error: NKError) { + request: @escaping (DataRequest?) -> Void = { _ in }, + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + dashboardWidgets: [NCCDashboardWidget]?, + responseData: AFDataResponse?, + error: NKError + ) { await withCheckedContinuation { continuation in - var capturedRequest: DataRequest? - var capturedTask: URLSessionTask? - - getDashboardWidget(account: account, options: options, request: { req in - capturedRequest = req - }, taskHandler: { task in - capturedTask = task - taskHandler(task) // lo inoltriamo comunque al chiamante - }, completion: { account, widgets, response, error in - continuation.resume(returning: (account, widgets, response, capturedRequest, capturedTask, error)) - }) + getDashboardWidget(account: account, + options: options, + request: request, + taskHandler: taskHandler) { account, dashboardWidgets, responseData, error in + continuation.resume(returning: ( + account: account, + dashboardWidgets: dashboardWidgets, + responseData: responseData, + error: error + )) + } } } + /// Retrieves the list of dashboard application widgets for the specified account and item string. + /// This is typically used to fetch available dashboard apps filtered by `items` (e.g., "weather,tasks"). + /// + /// Parameters: + /// - items: A comma-separated string representing widget types or categories to fetch. + /// - account: The Nextcloud account performing the request. + /// - options: Optional request options (default is empty). + /// - request: A closure that receives the underlying Alamofire `DataRequest`, useful for inspection or mutation. + /// - taskHandler: A closure to access the `URLSessionTask` for progress or cancellation. + /// - completion: Completion handler returning the account, list of applications, response, and error. func getDashboardWidgetsApplication(_ items: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -101,27 +129,38 @@ public extension NextcloudKit { options.queue.async { request(dashboardRequest) } } + /// Asynchronously fetches dashboard widgets tied to specific applications. + /// - Parameters: + /// - items: A comma-separated list of app IDs (e.g., "files,calendar"). + /// - account: The account performing the request. + /// - options: Optional request configuration. + /// - request: Handler for the `DataRequest` (if needed). + /// - taskHandler: Handler for the underlying `URLSessionTask`. + /// - Returns: A tuple with account, dashboard applications, response data, and NKError. func getDashboardWidgetsApplicationAsync(_ items: String, account: String, options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, - dashboardApplications: [NCCDashboardApplication]?, - responseData: AFDataResponse?, - request: DataRequest?, - task: URLSessionTask?, - error: NKError) { + request: @escaping (DataRequest?) -> Void = { _ in }, + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + dashboardApplications: [NCCDashboardApplication]?, + responseData: AFDataResponse?, + error: NKError + ) { await withCheckedContinuation { continuation in - var capturedRequest: DataRequest? - var capturedTask: URLSessionTask? - - getDashboardWidgetsApplication(items, account: account, options: options, request: { req in - capturedRequest = req - }, taskHandler: { task in - capturedTask = task - taskHandler(task) // propaghiamo al chiamante - }, completion: { account, apps, response, error in - continuation.resume(returning: (account, apps, response, capturedRequest, capturedTask, error )) - }) + getDashboardWidgetsApplication(items, + account: account, + options: options, + request: request, + taskHandler: taskHandler) { account, dashboardApplications, responseData, error in + continuation.resume(returning: ( + account: account, + dashboardApplications: dashboardApplications, + responseData: responseData, + error: error + )) + } } } } diff --git a/Sources/NextcloudKit/NextcloudKit+Download.swift b/Sources/NextcloudKit/NextcloudKit+Download.swift index c6159139..0a3f9972 100644 --- a/Sources/NextcloudKit/NextcloudKit+Download.swift +++ b/Sources/NextcloudKit/NextcloudKit+Download.swift @@ -7,6 +7,18 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Downloads a remote file and stores it at a local path for the specified Nextcloud account. + /// It provides detailed progress, headers, and metadata such as ETag, last modified date, and content length. + /// + /// Parameters: + /// - serverUrlFileName: A value representing the remote file URL or path (typically String or URL). + /// - fileNameLocalPath: The local filesystem path where the file should be saved. + /// - account: The Nextcloud account performing the download. + /// - options: Optional request options (default is empty). + /// - requestHandler: Closure to access the Alamofire `DownloadRequest` (for customization, inspection, etc.). + /// - taskHandler: Closure to access the underlying `URLSessionTask` (e.g. for progress or cancellation). + /// - progressHandler: Closure that receives periodic progress updates. + /// - completionHandler: Completion closure returning metadata: account, ETag, modification date, content length, headers, AFError, and NKError. func download(serverUrlFileName: Any, fileNameLocalPath: String, account: String, @@ -69,4 +81,51 @@ public extension NextcloudKit { options.queue.async { requestHandler(request) } } + + /// Asynchronously downloads a file to the specified local path, with optional progress and task tracking. + /// - Parameters: + /// - serverUrlFileName: A URL or object convertible to a URL string. + /// - fileNameLocalPath: Destination path for the local file. + /// - account: The Nextcloud account used for the request. + /// - options: Optional request configuration. + /// - requestHandler: Handler for accessing the `DownloadRequest`. + /// - taskHandler: Handler for monitoring the `URLSessionTask`. + /// - progressHandler: Progress tracking callback. + /// - Returns: A tuple with account, etag, date, content length, headers, Alamofire error, and internal NKError. + func downloadAsync(serverUrlFileName: Any, + fileNameLocalPath: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + requestHandler: @escaping (_ request: DownloadRequest) -> Void = { _ in }, + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, + progressHandler: @escaping (_ progress: Progress) -> Void = { _ in } + ) async -> ( + account: String, + etag: String?, + date: Date?, + length: Int64, + headers: [AnyHashable: Any]?, + afError: AFError?, + nkError: NKError + ) { + await withCheckedContinuation { continuation in + download(serverUrlFileName: serverUrlFileName, + fileNameLocalPath: fileNameLocalPath, + account: account, + options: options, + requestHandler: requestHandler, + taskHandler: taskHandler, + progressHandler: progressHandler) { account, etag, date, length, headers, afError, nkError in + continuation.resume(returning: ( + account: account, + etag: etag, + date: date, + length: length, + headers: headers, + afError: afError, + nkError: nkError + )) + } + } + } } diff --git a/Sources/NextcloudKit/NextcloudKit+E2EE.swift b/Sources/NextcloudKit/NextcloudKit+E2EE.swift index ec2eb9af..92343616 100644 --- a/Sources/NextcloudKit/NextcloudKit+E2EE.swift +++ b/Sources/NextcloudKit/NextcloudKit+E2EE.swift @@ -7,6 +7,16 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Marks or unmarks a folder as End-to-End Encrypted (E2EE) for a given Nextcloud account. + /// Depending on the `delete` flag, this function either enables or disables the E2EE status for the folder. + /// + /// - Parameters: + /// - fileId: The identifier of the folder to mark/unmark. + /// - delete: If `true`, removes the E2EE mark; if `false`, adds the E2EE mark. + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request options (default is empty). + /// - taskHandler: Closure to access the `URLSessionTask` (default is no-op). + /// - completion: Completion handler returning the account, raw response, and any NKError. func markE2EEFolder(fileId: String, delete: Bool, account: String, @@ -45,17 +55,52 @@ public extension NextcloudKit { } } + /// Asynchronously marks or unmarks a folder for end-to-end encryption. + /// - Parameters: + /// - fileId: The ID of the folder. + /// - delete: Whether to remove the E2EE marker (true) or set it (false). + /// - account: The Nextcloud account used for the request. + /// - options: Request configuration and context. + /// - taskHandler: Optional monitoring of the underlying URLSessionTask. + /// - Returns: A tuple with account, responseData and NKError. func markE2EEFolderAsync(fileId: String, delete: Bool, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation({ continuation in - NextcloudKit.shared.markE2EEFolder(fileId: fileId, delete: delete, account: account, options: options) { account, responseData, error in - continuation.resume(returning: (account: account, responseData: responseData, error: error)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + markE2EEFolder(fileId: fileId, + delete: delete, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) } - }) + } } + /// Locks or unlocks a folder for End-to-End Encryption (E2EE) using a provided token and counter. + /// Depending on the HTTP `method` (e.g. "LOCK", "UNLOCK", "PUT"), the operation will either lock the folder, + /// refresh the E2EE token, or perform another action defined by the server API. + /// + /// - Parameters: + /// - fileId: The identifier of the folder to lock or unlock. + /// - e2eToken: Optional E2EE token used to lock the folder (nil if unlocking). + /// - e2eCounter: Optional counter value for token freshness or verification. + /// - method: The HTTP method to use for the request (e.g., "LOCK", "PUT", etc.). + /// - account: The Nextcloud account performing the request. + /// - options: Optional request options (default is empty). + /// - taskHandler: Closure to access the `URLSessionTask` (default is no-op). + /// - completion: Completion handler returning the account, updated E2EE token, raw response, and any NKError. func lockE2EEFolder(fileId: String, e2eToken: String?, e2eCounter: String?, @@ -106,20 +151,59 @@ public extension NextcloudKit { } } + /// Asynchronously locks or unlocks a folder for end-to-end encryption. + /// - Parameters: + /// - fileId: The ID of the folder. + /// - e2eToken: Optional encryption token to include in the request. + /// - e2eCounter: Optional counter string to include. + /// - method: HTTP method ("LOCK" or "UNLOCK"). + /// - account: The Nextcloud account used for the request. + /// - options: Request configuration and context. + /// - taskHandler: Optional monitoring of the underlying URLSessionTask. + /// - Returns: A tuple with account, returned e2eToken, responseData and NKError. func lockE2EEFolderAsync(fileId: String, e2eToken: String?, e2eCounter: String?, method: String, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, e2eToken: String?, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation({ continuation in - NextcloudKit.shared.lockE2EEFolder(fileId: fileId, e2eToken: e2eToken, e2eCounter: e2eCounter, method: method, account: account, options: options) { account, e2eToken, responseData, error in - continuation.resume(returning: (account: account, e2eToken: e2eToken, responseData: responseData, error: error)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + e2eToken: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + lockE2EEFolder(fileId: fileId, + e2eToken: e2eToken, + e2eCounter: e2eCounter, + method: method, + account: account, + options: options, + taskHandler: taskHandler) { account, token, responseData, error in + continuation.resume(returning: ( + account: account, + e2eToken: token, + responseData: responseData, + error: error + )) } - }) + } } - + /// Retrieves E2EE metadata and signature for a specific file from the Nextcloud E2EE API. + /// It supports different API versions via the `options.version` property (default is "v1"). + /// This request is authenticated and validated, and returns both metadata and signature + /// (from header `X-NC-E2EE-SIGNATURE`) if the operation is successful. + /// + /// - Parameters: + /// - fileId: The file identifier to retrieve metadata for. + /// - e2eToken: Optional E2EE token used for authorization or context. + /// - account: The Nextcloud account performing the request. + /// - options: Optional request options (includes version, queue, task description, etc.). + /// - taskHandler: Closure to access the URLSessionTask for progress or control. + /// - completion: Completion handler returning the account, metadata string, signature string, response, and NKError. func getE2EEMetadata(fileId: String, e2eToken: String?, account: String, @@ -163,25 +247,57 @@ public extension NextcloudKit { } } - // Async wrapper for getE2EEMetadata + /// Asynchronously fetches the E2EE metadata and signature for a given file. + /// - Parameters: + /// - fileId: The ID of the file. + /// - e2eToken: Optional encryption token to include. + /// - account: The Nextcloud account used for the request. + /// - options: Request configuration and context. + /// - taskHandler: Optional monitoring of the underlying URLSessionTask. + /// - Returns: A tuple with account, metadata, signature, response data and error result. func getE2EEMetadataAsync(fileId: String, - e2eToken: String? = nil, + e2eToken: String?, account: String, options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, e2eMetadata: String?, signature: String?, responseData: AFDataResponse?, error: NKError) { + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + e2eMetadata: String?, + signature: String?, + responseData: AFDataResponse?, + error: NKError + ) { await withCheckedContinuation { continuation in - getE2EEMetadata( - fileId: fileId, - e2eToken: e2eToken, - account: account, - options: options, - taskHandler: taskHandler - ) { account, e2eMetadata, signature, responseData, error in - continuation.resume(returning: (account: account, e2eMetadata: e2eMetadata, signature: signature, responseData: responseData, error: error)) + getE2EEMetadata(fileId: fileId, + e2eToken: e2eToken, + account: account, + options: options, + taskHandler: taskHandler) { account, metadata, signature, responseData, error in + continuation.resume(returning: ( + account: account, + e2eMetadata: metadata, + signature: signature, + responseData: responseData, + error: error + )) } } } + /// Uploads E2EE metadata for a specific file on the Nextcloud server, using the specified HTTP method. + /// The request includes the E2E token and optional metadata and signature. The server may return the + /// stored metadata back in the response. + /// + /// - Parameters: + /// - fileId: The identifier of the file to update metadata for. + /// - e2eToken: Required token used to authorize the E2EE update. + /// - e2eMetadata: Optional encrypted metadata payload to be stored. + /// - signature: Optional signature for integrity/authentication (added to header). + /// - method: The HTTP method to use (e.g., "PUT", "POST"). + /// - account: The Nextcloud account performing the request. + /// - options: Optional request options (includes version, queue, etc.). + /// - taskHandler: Closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, stored metadata (if any), response, and NKError. func putE2EEMetadata(fileId: String, e2eToken: String, e2eMetadata: String?, @@ -233,22 +349,62 @@ public extension NextcloudKit { } } + /// Asynchronously stores E2EE metadata on the server for the specified file. + /// - Parameters: + /// - fileId: The file identifier. + /// - e2eToken: The encryption token required for authorization. + /// - e2eMetadata: Optional metadata to store. + /// - signature: Optional digital signature to validate the metadata. + /// - method: The HTTP method to be used ("POST", "PUT"). + /// - account: The Nextcloud account to use. + /// - options: Optional request context and headers. + /// - taskHandler: Optional monitoring of the URLSessionTask. + /// - Returns: A tuple with account, metadata, response data, and error result. func putE2EEMetadataAsync(fileId: String, e2eToken: String, e2eMetadata: String?, signature: String?, method: String, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, metadata: String?, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation({ continuation in - NextcloudKit.shared.putE2EEMetadata(fileId: fileId, e2eToken: e2eToken, e2eMetadata: e2eMetadata, signature: signature, method: method, account: account, options: options) { account, metadata, responseData, error in - continuation.resume(returning: (account: account, metadata: metadata, responseData: responseData, error: error)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + metadata: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + putE2EEMetadata(fileId: fileId, + e2eToken: e2eToken, + e2eMetadata: e2eMetadata, + signature: signature, + method: method, + account: account, + options: options, + taskHandler: taskHandler) { account, metadata, responseData, error in + continuation.resume(returning: ( + account: account, + metadata: metadata, + responseData: responseData, + error: error + )) } - }) + } } // MARK: - + /// Retrieves the public E2EE certificate (public key) for the given account or a specified user. + /// If `user` is nil, the certificate for the current account is returned. + /// If `user` is provided, the request fetches the public key of that user using a `users=[...]` query parameter. + /// + /// - Parameters: + /// - user: Optional username to fetch the public key for. If nil, fetches the current user's key. + /// - account: The Nextcloud account performing the request. + /// - options: Optional request options (includes version, task description, queue, etc.). + /// - taskHandler: Closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, the certificate string, the certificate user (if applicable), the raw response, and any NKError. func getE2EECertificate(user: String? = nil, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -300,16 +456,51 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the public E2EE certificate. + /// If `user` is provided, retrieves the certificate for that user. + /// If `user` is nil, retrieves the certificate for the current session user. + /// - Parameters: + /// - user: Optional user ID to fetch the certificate for. + /// - account: The account to use for the request. + /// - options: Optional request context (headers, queue, etc.). + /// - taskHandler: Optional observer for the URLSession task. + /// - Returns: A tuple containing account, current user’s certificate, optional target user’s certificate, response data, and an error if any. func getE2EECertificateAsync(user: String? = nil, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, certificate: String?, certificateUser: String?, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation({ continuation in - NextcloudKit.shared.getE2EECertificate(user: user, account: account, options: options) { account, certificate, certificateUser, responseData, error in - continuation.resume(returning: (account: account, certificate: certificate, certificateUser: certificateUser, responseData: responseData, error: error)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + certificate: String?, + certificateUser: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getE2EECertificate(user: user, + account: account, + options: options, + taskHandler: taskHandler) { account, certificate, certificateUser, responseData, error in + continuation.resume(returning: ( + account: account, + certificate: certificate, + certificateUser: certificateUser, + responseData: responseData, + error: error + )) } - }) + } } + /// Retrieves the private E2EE key for the current account from the Nextcloud server. + /// This key is typically encrypted and used for decrypting user data locally. + /// The endpoint used is versioned via `options.version` (default: "v1"). + /// + /// - Parameters: + /// - account: The Nextcloud account requesting the private key. + /// - options: Optional request options (includes version, queue, etc.). + /// - taskHandler: Closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, private key string, raw response, and NKError. func getE2EEPrivateKey(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -346,6 +537,44 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the private E2EE key for the current user. + /// - Parameters: + /// - account: The Nextcloud account to authenticate the request. + /// - options: Optional request options (API version, headers, etc.). + /// - taskHandler: Optional callback for task creation. + /// - Returns: A tuple containing the account, the private key string (if available), the response, and the error. + func getE2EEPrivateKeyAsync(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + privateKey: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getE2EEPrivateKey(account: account, + options: options, + taskHandler: taskHandler) { account, privateKey, responseData, error in + continuation.resume(returning: ( + account: account, + privateKey: privateKey, + responseData: responseData, + error: error + )) + } + } + } + + /// Retrieves the server's E2EE public key for the current Nextcloud instance. + /// This key is used by clients to encrypt data that will be sent to the server. + /// The request targets the `server-key` endpoint and returns the PEM-formatted public key. + /// + /// - Parameters: + /// - account: The Nextcloud account performing the request. + /// - options: Optional request options (includes version, queue, etc.). + /// - taskHandler: Closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, public key string, raw response, and NKError. func getE2EEPublicKey(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -382,6 +611,45 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the server's public key for end-to-end encryption. + /// - Parameters: + /// - account: The Nextcloud account used for the request. + /// - options: Optional request configuration (API version, headers, etc.). + /// - taskHandler: Optional monitoring of the underlying URLSessionTask. + /// - Returns: A tuple with account, publicKey string, AFDataResponse, and NKError. + func getE2EEPublicKeyAsync(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + publicKey: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getE2EEPublicKey(account: account, + options: options, + taskHandler: taskHandler) { account, publicKey, responseData, error in + continuation.resume(returning: ( + account: account, + publicKey: publicKey, + responseData: responseData, + error: error + )) + } + } + } + + /// Sends a certificate signing request (CSR) to the server in order to obtain a signed E2EE public certificate. + /// The server responds with a signed public key associated with the account. + /// The request is sent via HTTP POST to the `/public-key` endpoint. + /// + /// - Parameters: + /// - certificate: The CSR (Certificate Signing Request) in string format to be signed by the server. + /// - account: The Nextcloud account performing the request. + /// - options: Optional request options (e.g., version, queue, headers). + /// - taskHandler: Closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, signed certificate string, response, and NKError. func signE2EECertificate(certificate: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -421,6 +689,48 @@ public extension NextcloudKit { } } + /// Asynchronously submits a CSR (Certificate Signing Request) to obtain a signed E2EE certificate. + /// - Parameters: + /// - certificate: The CSR string to be signed. + /// - account: The Nextcloud account used for the request. + /// - options: Optional request configuration. + /// - taskHandler: Optional monitoring of the URLSessionTask. + /// - Returns: A tuple containing the account, signed certificate, response data, and error. + func signE2EECertificateAsync(certificate: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + certificate: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + signE2EECertificate(certificate: certificate, + account: account, + options: options, + taskHandler: taskHandler) { account, certificate, responseData, error in + continuation.resume(returning: ( + account: account, + certificate: certificate, + responseData: responseData, + error: error + )) + } + } + } + + /// Stores the user's E2EE private key securely on the Nextcloud server. + /// This is typically done during initial key setup or key backup. + /// The private key is sent as a POST parameter to the `/private-key` endpoint. + /// + /// - Parameters: + /// - privateKey: The PEM-formatted private key string to be stored on the server. + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request options (versioning, queue dispatch, headers, etc.). + /// - taskHandler: Closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, stored private key (as echoed back), response, and NKError. func storeE2EEPrivateKey(privateKey: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -459,6 +769,47 @@ public extension NextcloudKit { } } + /// Asynchronously stores the E2EE private key on the server for the specified account. + /// - Parameters: + /// - privateKey: The private key to be saved. + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request configuration. + /// - taskHandler: Optional monitoring of the URLSessionTask. + /// - Returns: A tuple containing the account, echoed private key, response data, and error. + func storeE2EEPrivateKeyAsync(privateKey: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + privateKey: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + storeE2EEPrivateKey(privateKey: privateKey, + account: account, + options: options, + taskHandler: taskHandler) { account, privateKey, responseData, error in + continuation.resume(returning: ( + account: account, + privateKey: privateKey, + responseData: responseData, + error: error + )) + } + } + } + + /// Deletes the currently stored E2EE public certificate from the Nextcloud server. + /// This is typically used during key revocation or reinitialization of E2EE. + /// The request targets the `public-key` endpoint with the HTTP DELETE method. + /// + /// - Parameters: + /// - account: The Nextcloud account requesting the deletion of the certificate. + /// - options: Optional request options (e.g., version, dispatch queue, headers). + /// - taskHandler: Closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, raw response, and NKError. func deleteE2EECertificate(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -487,6 +838,42 @@ public extension NextcloudKit { } } + /// Asynchronously deletes the E2EE public certificate from the server for the given account. + /// - Parameters: + /// - account: The Nextcloud account to remove the certificate from. + /// - options: Optional request configuration. + /// - taskHandler: Optional monitoring of the URLSessionTask. + /// - Returns: A tuple containing the account, response data, and error. + func deleteE2EECertificateAsync(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + deleteE2EECertificate(account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + + /// Deletes the user's E2EE private key stored on the Nextcloud server. + /// This operation is typically performed when revoking access to the encrypted data, + /// or during account reset scenarios. + /// + /// - Parameters: + /// - account: The Nextcloud account requesting the deletion of its private key. + /// - options: Optional request options (API version, dispatch queue, headers). + /// - taskHandler: Closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, raw response, and NKError. func deleteE2EEPrivateKey(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -515,4 +902,31 @@ public extension NextcloudKit { } } } + + /// Asynchronously deletes the E2EE private key from the server for the specified account. + /// - Parameters: + /// - account: The Nextcloud account for which the private key will be deleted. + /// - options: Optional request configuration and headers. + /// - taskHandler: Optional monitoring of the URLSessionTask. + /// - Returns: A tuple containing the account, response data, and error. + func deleteE2EEPrivateKeyAsync(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + deleteE2EEPrivateKey(account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } } diff --git a/Sources/NextcloudKit/NextcloudKit+FilesLock.swift b/Sources/NextcloudKit/NextcloudKit+FilesLock.swift index 516399ea..215d4e96 100644 --- a/Sources/NextcloudKit/NextcloudKit+FilesLock.swift +++ b/Sources/NextcloudKit/NextcloudKit+FilesLock.swift @@ -7,6 +7,17 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Sends a WebDAV LOCK or UNLOCK request for a file on the server, + /// depending on the `shouldLock` flag. This is used to prevent or release + /// concurrent edits on a file. + /// + /// Parameters: + /// - serverUrlFileName: Fully qualified and encoded URL of the file to lock/unlock. + /// - shouldLock: Pass `true` to lock the file, `false` to unlock it. + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request options (e.g. headers, queue). + /// - taskHandler: Closure to access the URLSessionTask. + /// - completion: Completion handler returning the account, response, and NKError. func lockUnlockFile(serverUrlFileName: String, shouldLock: Bool, account: String, @@ -37,4 +48,37 @@ public extension NextcloudKit { } } } + + /// Asynchronously locks or unlocks a file on the server via WebDAV. + /// - Parameters: + /// - serverUrlFileName: The server-side full URL of the file to lock or unlock. + /// - shouldLock: `true` to lock the file, `false` to unlock it. + /// - account: The Nextcloud account performing the action. + /// - options: Optional request configuration (headers, queue, etc.). + /// - taskHandler: Optional monitoring of the `URLSessionTask`. + /// - Returns: A tuple containing the account, the server response, and any error encountered. + func lockUnlockFileAsync(serverUrlFileName: String, + shouldLock: Bool, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + lockUnlockFile(serverUrlFileName: serverUrlFileName, + shouldLock: shouldLock, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } } diff --git a/Sources/NextcloudKit/NextcloudKit+Groupfolders.swift b/Sources/NextcloudKit/NextcloudKit+Groupfolders.swift index 2f2987b1..cb877563 100644 --- a/Sources/NextcloudKit/NextcloudKit+Groupfolders.swift +++ b/Sources/NextcloudKit/NextcloudKit+Groupfolders.swift @@ -8,6 +8,15 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Retrieves the list of available group folders for the given Nextcloud account. + /// Group folders are shared spaces available across users and groups, + /// managed via the groupfolders app. + /// + /// Parameters: + /// - account: The Nextcloud account requesting the list of group folders. + /// - options: Optional request options (e.g., API version, custom headers, queue). + /// - taskHandler: Closure to access the underlying URLSessionTask. + /// - completion: Completion handler returning the account, list of group folders, response, and any NKError. func getGroupfolders(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -46,6 +55,35 @@ public extension NextcloudKit { } } } + + /// Asynchronously retrieves the list of Groupfolders associated with the given account. + /// - Parameters: + /// - account: The Nextcloud account identifier. + /// - options: Optional request configuration (headers, queue, etc.). + /// - taskHandler: Optional monitoring of the `URLSessionTask`. + /// - Returns: A tuple containing the account, an optional array of `NKGroupfolders`, the response data, and an `NKError`. + func getGroupfoldersAsync(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + results: [NKGroupfolders]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getGroupfolders(account: account, + options: options, + taskHandler: taskHandler) { account, results, responseData, error in + continuation.resume(returning: ( + account: account, + results: results, + responseData: responseData, + error: error + )) + } + } + } } public class NKGroupfolders: NSObject { diff --git a/Sources/NextcloudKit/NextcloudKit+Hovercard.swift b/Sources/NextcloudKit/NextcloudKit+Hovercard.swift index 9c582f83..e0939897 100644 --- a/Sources/NextcloudKit/NextcloudKit+Hovercard.swift +++ b/Sources/NextcloudKit/NextcloudKit+Hovercard.swift @@ -7,6 +7,13 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Retrieves the hovercard information for a specific user from the Nextcloud server. + /// - Parameters: + /// - userId: The identifier of the user whose hovercard is being requested. + /// - account: The Nextcloud account used to perform the request. + /// - options: Optional request options for customizing the API call. + /// - taskHandler: Closure for observing the underlying `URLSessionTask`. + /// - completion: Completion handler returning the account, the `NKHovercard` result, raw response data, and any error encountered. func getHovercard(for userId: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -41,6 +48,38 @@ public extension NextcloudKit { } } } + + /// Asynchronously retrieves the hovercard information for a specific user from the Nextcloud server. + /// - Parameters: + /// - userId: The identifier of the user whose hovercard is being requested. + /// - account: The Nextcloud account used to perform the request. + /// - options: Optional request options for customizing the API call. + /// - taskHandler: Closure for observing the underlying `URLSessionTask`. + /// - Returns: A tuple containing the account, the `NKHovercard` result, raw response data, and any error encountered. + func getHovercardAsync(for userId: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + result: NKHovercard?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getHovercard(for: userId, + account: account, + options: options, + taskHandler: taskHandler) { account, result, responseData, error in + continuation.resume(returning: ( + account: account, + result: result, + responseData: responseData, + error: error + )) + } + } + } } public class NKHovercard: NSObject { diff --git a/Sources/NextcloudKit/NextcloudKit+Livephoto.swift b/Sources/NextcloudKit/NextcloudKit+Livephoto.swift index 313f3bc4..c3d50552 100644 --- a/Sources/NextcloudKit/NextcloudKit+Livephoto.swift +++ b/Sources/NextcloudKit/NextcloudKit+Livephoto.swift @@ -6,6 +6,15 @@ import Foundation import Alamofire public extension NextcloudKit { + /// Associates a Live Photo video file with a photo on the server. + /// + /// Parameters: + /// - serverUrlfileNamePath: The full server path to the original photo. + /// - livePhotoFile: The local path to the Live Photo video file (.mov). + /// - account: The account performing the operation. + /// - options: Optional request configuration (e.g., headers, queue, version). + /// - taskHandler: Callback for tracking the underlying URLSessionTask. + /// - completion: Returns the account, raw response data, and NKError result. func setLivephoto(serverUrlfileNamePath: String, livePhotoFile: String, account: String, @@ -46,14 +55,37 @@ public extension NextcloudKit { } } + /// Asynchronously attaches a Live Photo video file to an existing image on the server. + /// + /// - Parameters: + /// - serverUrlfileNamePath: The full server-side path of the target image. + /// - livePhotoFile: Local file path of the Live Photo (.mov). + /// - account: The Nextcloud account to use for the request. + /// - options: Optional request context and headers. + /// - taskHandler: Optional callback to observe the URLSessionTask. + /// - Returns: A tuple with the account, response data, and NKError result. func setLivephotoAsync(serverUrlfileNamePath: String, livePhotoFile: String, account: String, - 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)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + setLivephoto(serverUrlfileNamePath: serverUrlfileNamePath, + livePhotoFile: livePhotoFile, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) } - }) + } } } diff --git a/Sources/NextcloudKit/NextcloudKit+Login.swift b/Sources/NextcloudKit/NextcloudKit+Login.swift index 575111f7..f6e1e9ef 100644 --- a/Sources/NextcloudKit/NextcloudKit+Login.swift +++ b/Sources/NextcloudKit/NextcloudKit+Login.swift @@ -8,6 +8,17 @@ import SwiftyJSON public extension NextcloudKit { // MARK: - App Password + + /// Retrieves an app password (token) for the given user credentials and server URL. + /// + /// Parameters: + /// - url: The base server URL (e.g., https://cloud.example.com). + /// - user: The username for authentication. + /// - password: The user's password. + /// - userAgent: Optional user-agent string to include in the request. + /// - options: Optional request configuration (headers, queue, etc.). + /// - taskHandler: Callback for observing the underlying URLSessionTask. + /// - completion: Returns the token string (if any), raw response data, and NKError result. func getAppPassword(url: String, user: String, password: String, @@ -47,6 +58,54 @@ public extension NextcloudKit { } } + /// Asynchronously fetches an app password for the provided user credentials. + /// + /// - Parameters: + /// - url: The base URL of the Nextcloud server. + /// - user: The user login name. + /// - password: The user’s password. + /// - userAgent: Optional custom user agent for the request. + /// - options: Optional request configuration. + /// - taskHandler: Callback to observe the task, if needed. + /// - Returns: A tuple containing the token, response data, and error result. + func getAppPasswordAsync(url: String, + user: String, + password: String, + userAgent: String? = nil, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + token: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getAppPassword(url: url, + user: user, + password: password, + userAgent: userAgent, + options: options, + taskHandler: taskHandler) { token, responseData, error in + continuation.resume(returning: ( + token: token, + responseData: responseData, + error: error + )) + } + } + } + + /// Deletes the app password (token) for a specific account using basic authentication. + /// + /// Parameters: + /// - serverUrl: The full server URL (e.g., https://cloud.example.com). + /// - username: The username associated with the app password. + /// - password: The password or app password used for authentication. + /// - userAgent: Optional user-agent string for the request. + /// - account: The logical account identifier used in the app. + /// - options: Optional request configuration (headers, queues, etc.). + /// - taskHandler: Callback to observe the underlying URLSessionTask. + /// - completion: Returns the raw response and a possible NKError result. func deleteAppPassword(serverUrl: String, username: String, password: String, @@ -87,6 +146,44 @@ public extension NextcloudKit { } } + /// Asynchronously deletes the current app password/token from the server. + /// + /// - Parameters: + /// - serverUrl: Full URL of the Nextcloud server. + /// - username: The user identifier. + /// - password: The password or token used for deletion authorization. + /// - userAgent: Optional string to customize the User-Agent header. + /// - account: Logical account identifier. + /// - options: Configuration options for the request. + /// - taskHandler: Optional callback for observing the URLSessionTask. + /// - Returns: A tuple containing the response and a possible error. + func deleteAppPasswordAsync(serverUrl: String, + username: String, + password: String, + userAgent: String? = nil, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + deleteAppPassword(serverUrl: serverUrl, + username: username, + password: password, + userAgent: userAgent, + account: account, + options: options, + taskHandler: taskHandler) { responseData, error in + continuation.resume(returning: ( + responseData: responseData, + error: error + )) + } + } + } + // MARK: - Login Flow V2 /// @@ -122,6 +219,19 @@ public extension NextcloudKit { } } + /// Starts the Login Flow v2 process by requesting a login token and associated parameters from the server. + /// + /// - Parameters: + /// - serverUrl: The base URL of the Nextcloud server used to initiate the login flow. + /// - options: An optional `NKRequestOptions` object containing configuration such as API version, custom headers, and execution queue. + /// - taskHandler: A closure that provides the `URLSessionTask` used for the request. Useful for monitoring or cancellation. + /// + /// - Completion: + /// - token: The login token used for polling the login status. + /// - endpoint: The endpoint URL to be polled to check login completion. + /// - login: A user-visible login URL that can be presented to complete authentication. + /// - responseData: The raw `AFDataResponse` received from the server. + /// - error: An `NKError` object representing success or failure of the operation. func getLoginFlowV2(serverUrl: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -155,6 +265,45 @@ public extension NextcloudKit { } } + /// Asynchronously initiates the Login Flow v2 process to obtain authentication parameters. + /// - Parameters: + /// - serverUrl: The base URL of the Nextcloud server. + /// - options: Optional request configuration for API version, queue, etc. + /// - taskHandler: Optional callback to observe the `URLSessionTask`. + /// - Returns: A tuple containing the login token, polling endpoint, login URL, response data, and any encountered error. + func getLoginFlowV2Async(serverUrl: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + token: String?, + endpoint: String?, + login: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getLoginFlowV2(serverUrl: serverUrl, + options: options, + taskHandler: taskHandler) { token, endpoint, login, responseData, error in + continuation.resume(returning: ( + token: token, + endpoint: endpoint, + login: login, + responseData: responseData, + error: error + )) + } + } + } + + /// Polls the login flow V2 endpoint to retrieve login credentials (OAuth-style). + /// + /// Parameters: + /// - token: The login flow token to poll for. + /// - endpoint: The base URL endpoint (e.g., https://cloud.example.com). + /// - options: Optional request configuration (version, headers, queues, etc.). + /// - taskHandler: Callback to observe the underlying URLSessionTask. + /// - completion: Returns the discovered server URL, loginName, appPassword, the raw response data, and any NKError. func getLoginFlowV2Poll(token: String, endpoint: String, options: NKRequestOptions = NKRequestOptions(), @@ -187,4 +336,39 @@ public extension NextcloudKit { } } } + + /// Asynchronously polls the login flow V2 endpoint for login credentials. + /// + /// - Parameters: + /// - token: The token used in the login flow process. + /// - endpoint: Full base endpoint URL to call the polling API. + /// - options: Request configuration such as version, headers, queue. + /// - taskHandler: Optional callback to observe the underlying URLSessionTask. + /// - Returns: A tuple with server URL, login name, app password, raw response, and NKError. + func getLoginFlowV2PollAsync(token: String, + endpoint: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + server: String?, + loginName: String?, + appPassword: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getLoginFlowV2Poll(token: token, + endpoint: endpoint, + options: options, + taskHandler: taskHandler) { server, loginName, appPassword, responseData, error in + continuation.resume(returning: ( + server: server, + loginName: loginName, + appPassword: appPassword, + responseData: responseData, + error: error + )) + } + } + } } diff --git a/Sources/NextcloudKit/NextcloudKit+NCText.swift b/Sources/NextcloudKit/NextcloudKit+NCText.swift index 2f8426fd..03b61d5b 100644 --- a/Sources/NextcloudKit/NextcloudKit+NCText.swift +++ b/Sources/NextcloudKit/NextcloudKit+NCText.swift @@ -7,6 +7,13 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Retrieves the list of editors and creators for collaborative text editing. + /// + /// Parameters: + /// - account: The account from which to fetch the editor details. + /// - options: Optional request configuration such as headers, queue, or API version. + /// - taskHandler: Callback to track the underlying URLSessionTask. + /// - completion: Returns the account, array of editors, array of creators, the raw response data, and NKError. func textObtainEditorDetails(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -50,18 +57,48 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves details of users involved in collaborative editing (editors and creators). + /// + /// - Parameters: + /// - account: The Nextcloud account from which the information is retrieved. + /// - options: Configuration for the request, including headers and execution queue. + /// - taskHandler: Optional callback to monitor the underlying network task. + /// - Returns: A tuple containing the account, list of editors, list of creators, raw response, and NKError. func textObtainEditorDetailsAsync(account: String, - options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, editors: [NKEditorDetailsEditor]?, creators: [NKEditorDetailsCreator]?, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation { continuation in + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + editors: [NKEditorDetailsEditor]?, + creators: [NKEditorDetailsCreator]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in textObtainEditorDetails(account: account, options: options, taskHandler: taskHandler) { account, editors, creators, responseData, error in - continuation.resume(returning: (account, editors, creators, responseData, error)) + continuation.resume(returning: ( + account: account, + editors: editors, + creators: creators, + responseData: responseData, + error: error + )) } } } + /// Opens a file using the specified text editor and returns the access URL. + /// + /// Parameters: + /// - fileNamePath: The path of the file to open on the server. + /// - fileId: Optional file identifier used to reference the file more precisely. + /// - editor: The identifier of the text editor to use. + /// - account: The account initiating the file open request. + /// - options: Optional configuration for the request (headers, API version, etc.). + /// - taskHandler: Callback triggered with the underlying URLSessionTask. + /// - completion: Returns the account, the resulting file editor URL, raw response data, and an NKError. func textOpenFile(fileNamePath: String, fileId: String? = nil, editor: String, @@ -98,6 +135,52 @@ public extension NextcloudKit { } } + /// Asynchronously opens a file in the specified text editor and retrieves the access URL. + /// + /// - Parameters: + /// - fileNamePath: Path of the file on the server. + /// - fileId: Optional file ID to assist in uniquely identifying the file. + /// - editor: Identifier of the text editor to be used. + /// - account: Account performing the operation. + /// - options: Configuration options for the request. + /// - taskHandler: Optional monitoring for the underlying URLSessionTask. + /// - Returns: A tuple containing the account, resulting URL, raw response data, and NKError. + func textOpenFileAsync(fileNamePath: String, + fileId: String? = nil, + editor: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + url: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + textOpenFile(fileNamePath: fileNamePath, + fileId: fileId, + editor: editor, + account: account, + options: options, + taskHandler: taskHandler) { account, url, responseData, error in + continuation.resume(returning: ( + account: account, + url: url, + responseData: responseData, + error: error + )) + } + } + } + + /// Retrieves the list of available editor templates for the given account. + /// + /// Parameters: + /// - account: The account requesting the list of templates. + /// - options: Optional request configuration such as headers, queue, or API version. + /// - taskHandler: Callback triggered with the underlying URLSessionTask. + /// - completion: Returns the account, an optional array of NKEditorTemplate, the raw response, and an NKError. func textGetListOfTemplates(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -137,15 +220,47 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves a list of editor templates for the specified account. + /// + /// - Parameters: + /// - account: The account requesting the templates. + /// - options: Request configuration options (queue, headers, etc.). + /// - taskHandler: Optional callback to monitor the underlying URLSessionTask. + /// - Returns: A tuple containing the account, list of templates (if any), raw response, and error information. func textGetListOfTemplatesAsync(account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, templates: [NKEditorTemplate]?, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation({ continuation in - textGetListOfTemplates(account: account) { account, templates, responseData, error in - continuation.resume(returning: (account: account, templates: templates, responseData: responseData, error: error)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + templates: [NKEditorTemplate]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + textGetListOfTemplates(account: account, + options: options, + taskHandler: taskHandler) { account, templates, responseData, error in + continuation.resume(returning: ( + account: account, + templates: templates, + responseData: responseData, + error: error + )) } - }) + } } + /// Creates a new file using a specific editor, creator, and template. + /// + /// Parameters: + /// - fileNamePath: The full destination path where the new file will be created. + /// - editorId: The identifier of the editor to use (e.g., "richdocuments"). + /// - creatorId: The identifier of the creator (e.g., "document", "spreadsheet"). + /// - templateId: The identifier of the template to use for this file. + /// - account: The account performing the operation. + /// - options: Optional request configuration (headers, queue, version, etc.). + /// - taskHandler: Callback to monitor the underlying URLSessionTask. + /// - completion: Returns the account, the resulting file URL (if any), the raw response, and NKError. func textCreateFile(fileNamePath: String, editorId: String, creatorId: String, @@ -184,4 +299,46 @@ public extension NextcloudKit { } } } + + /// Asynchronously creates a new file from a template using the specified editor and creator. + /// + /// - Parameters: + /// - fileNamePath: Destination path where the new file will be saved. + /// - editorId: The editor's unique identifier (e.g., "richdocuments"). + /// - creatorId: The creator's identifier (e.g., "document"). + /// - templateId: The template to use for the new file. + /// - account: The Nextcloud account used for the operation. + /// - options: Optional request settings (e.g., headers, queue, etc.). + /// - taskHandler: Optional callback to observe the URLSessionTask. + /// - Returns: A tuple containing the account, the resulting file URL, raw response data, and NKError. + func textCreateFileAsync(fileNamePath: String, + editorId: String, + creatorId: String, + templateId: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + url: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + textCreateFile(fileNamePath: fileNamePath, + editorId: editorId, + creatorId: creatorId, + templateId: templateId, + account: account, + options: options, + taskHandler: taskHandler) { account, url, responseData, error in + continuation.resume(returning: ( + account: account, + url: url, + responseData: responseData, + error: error + )) + } + } + } } diff --git a/Sources/NextcloudKit/NextcloudKit+PushNotification.swift b/Sources/NextcloudKit/NextcloudKit+PushNotification.swift index 5435b31a..aa3078ae 100644 --- a/Sources/NextcloudKit/NextcloudKit+PushNotification.swift +++ b/Sources/NextcloudKit/NextcloudKit+PushNotification.swift @@ -7,6 +7,17 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Subscribes the current device to push notifications. + /// + /// Parameters: + /// - serverUrl: The base server URL for the Nextcloud instance. + /// - pushTokenHash: Hashed device push token, used for identification. + /// - devicePublicKey: The public key of the device for encryption/authentication. + /// - proxyServerUrl: The URL of the proxy push server. + /// - account: The Nextcloud account performing the subscription. + /// - options: Optional request configuration (headers, version, etc.). + /// - taskHandler: Callback to monitor the `URLSessionTask`. + /// - completion: Returns the account, device identifier, push signature, public key, response data, and NKError. func subscribingPushNotification(serverUrl: String, pushTokenHash: String, devicePublicKey: String, @@ -50,13 +61,24 @@ public extension NextcloudKit { } } - func subscribingPushNotificationAsync( - serverUrl: String, - pushTokenHash: String, - devicePublicKey: String, - proxyServerUrl: String, - account: String, - options: NKRequestOptions = NKRequestOptions() + /// Asynchronously subscribes a device to push notifications on the server. + /// + /// - Parameters: + /// - serverUrl: Base URL of the Nextcloud server. + /// - pushTokenHash: Hashed representation of the device push token. + /// - devicePublicKey: Public key for the device used for secure messaging. + /// - proxyServerUrl: URL to the push proxy server. + /// - account: The Nextcloud account performing the request. + /// - options: Request customization (e.g., queue, headers). + /// - taskHandler: Optional URLSession task observer. + /// - Returns: A tuple containing the account, device identifier, signature, public key, response data, and NKError. + func subscribingPushNotificationAsync(serverUrl: String, + pushTokenHash: String, + devicePublicKey: String, + proxyServerUrl: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } ) async -> ( account: String, deviceIdentifier: String?, @@ -66,30 +88,33 @@ public extension NextcloudKit { error: NKError ) { await withCheckedContinuation { continuation in - subscribingPushNotification( - serverUrl: serverUrl, - pushTokenHash: pushTokenHash, - devicePublicKey: devicePublicKey, - proxyServerUrl: proxyServerUrl, - account: account, - options: options, - taskHandler: { _ in }, - completion: { account, deviceIdentifier, signature, publicKey, responseData, error in - continuation.resume( - returning: ( - account, - deviceIdentifier, - signature, - publicKey, - responseData, - error - ) - ) - } - ) + subscribingPushNotification(serverUrl: serverUrl, + pushTokenHash: pushTokenHash, + devicePublicKey: devicePublicKey, + proxyServerUrl: proxyServerUrl, + account: account, + options: options, + taskHandler: taskHandler) { account, deviceIdentifier, signature, publicKey, responseData, error in + continuation.resume(returning: ( + account: account, + deviceIdentifier: deviceIdentifier, + signature: signature, + publicKey: publicKey, + responseData: responseData, + error: error + )) + } } } + /// Unsubscribes the current device from push notifications. + /// + /// Parameters: + /// - serverUrl: The base server URL of the Nextcloud instance. + /// - account: The Nextcloud account performing the unsubscription. + /// - options: Optional request configuration (headers, queue, etc.). + /// - taskHandler: Callback to monitor the `URLSessionTask`. + /// - completion: Returns the account, raw response data, and NKError. func unsubscribingPushNotification(serverUrl: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -116,34 +141,49 @@ public extension NextcloudKit { } } - func unsubscribingPushNotificationAsync( - serverUrl: String, - account: String, - options: NKRequestOptions = NKRequestOptions() + /// Asynchronously unsubscribes a device from push notifications. + /// + /// - Parameters: + /// - serverUrl: Base URL of the Nextcloud server. + /// - account: The Nextcloud account performing the request. + /// - options: Request customization (e.g., headers, queue, version). + /// - taskHandler: Optional observer for the underlying `URLSessionTask`. + /// - Returns: A tuple containing the account, response data, and NKError. + func unsubscribingPushNotificationAsync(serverUrl: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } ) async -> ( account: String, responseData: AFDataResponse?, error: NKError ) { await withCheckedContinuation { continuation in - unsubscribingPushNotification( - serverUrl: serverUrl, - account: account, - options: options, - taskHandler: { _ in }, - completion: { account, responseData, error in - continuation.resume( - returning: ( - account, - responseData, - error - ) - ) - } - ) + unsubscribingPushNotification(serverUrl: serverUrl, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } } } + /// Subscribes a device to the push proxy server for receiving push notifications. + /// + /// Parameters: + /// - proxyServerUrl: The URL of the push proxy server. + /// - pushToken: The token representing the push notification subscription. + /// - deviceIdentifier: A unique identifier for the device. + /// - signature: A signature to validate the subscription. + /// - publicKey: The public key associated with the device. + /// - account: The Nextcloud account performing the subscription. + /// - options: Optional request customization. + /// - taskHandler: Callback for tracking the underlying URLSessionTask. + /// - completion: Returns the account, raw response data, and NKError. func subscribingPushProxy(proxyServerUrl: String, pushToken: String, deviceIdentifier: String, @@ -181,33 +221,60 @@ public extension NextcloudKit { } } - func subscribingPushProxyAsync( - proxyServerUrl: String, - pushToken: String, - deviceIdentifier: String, - signature: String, - publicKey: String, + /// Asynchronously subscribes a device to the push proxy for push notifications. + /// + /// - Parameters: + /// - proxyServerUrl: URL of the push proxy server. + /// - pushToken: Token representing the device's push subscription. + /// - deviceIdentifier: Unique identifier for the device. + /// - signature: Digital signature for verification. + /// - publicKey: Public key associated with the subscription. + /// - account: The Nextcloud account performing the operation. + /// - options: Request customization (headers, queue, etc.). + /// - taskHandler: Callback for monitoring the URLSessionTask. + /// - Returns: A tuple containing the account, response data, and NKError. + func subscribingPushProxyAsync(proxyServerUrl: String, + pushToken: String, + deviceIdentifier: String, + signature: String, + publicKey: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, - responseData: AFDataResponse?, - error: NKError) { + responseData: AFDataResponse?, + error: NKError + ) { await withCheckedContinuation { continuation in - subscribingPushProxy( - proxyServerUrl: proxyServerUrl, - pushToken: pushToken, - deviceIdentifier: deviceIdentifier, - signature: signature, - publicKey: publicKey, - account: account, - options: options, - taskHandler: { _ in }, - completion: { account, responseData, error in - continuation.resume(returning: (account, responseData, error)) - } - ) + subscribingPushProxy(proxyServerUrl: proxyServerUrl, + pushToken: pushToken, + deviceIdentifier: deviceIdentifier, + signature: signature, + publicKey: publicKey, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } } } + /// Unsubscribes a device from the push proxy server. + /// + /// Parameters: + /// - proxyServerUrl: The URL of the push proxy server. + /// - deviceIdentifier: A unique identifier for the device. + /// - signature: A cryptographic signature to authenticate the request. + /// - publicKey: The public key associated with the device. + /// - account: The Nextcloud account initiating the request. + /// - options: Optional configuration for the request (queue, headers, version, etc.). + /// - taskHandler: Callback triggered when the underlying URLSessionTask is created. + /// - completion: Completion handler with account, response data, and NKError result. func unsubscribingPushProxy(proxyServerUrl: String, deviceIdentifier: String, signature: String, @@ -243,29 +310,43 @@ public extension NextcloudKit { } } - func unsubscribingPushProxyAsync( - proxyServerUrl: String, - deviceIdentifier: String, - signature: String, - publicKey: String, + /// Asynchronously unsubscribes a device from the push proxy server. + /// + /// - Parameters: + /// - proxyServerUrl: The URL of the push proxy server. + /// - deviceIdentifier: A unique identifier for the device. + /// - signature: A cryptographic signature for validation. + /// - publicKey: Public key used for authentication. + /// - account: The Nextcloud account performing the unsubscription. + /// - options: Optional configuration for headers, queue, etc. + /// - taskHandler: Optional callback for monitoring the URLSessionTask. + /// - Returns: A tuple with the account, the raw AF response data, and an NKError result. + func unsubscribingPushProxyAsync(proxyServerUrl: String, + deviceIdentifier: String, + signature: String, + publicKey: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( account: String, - options: NKRequestOptions = NKRequestOptions() - ) async -> (account: String, - responseData: AFDataResponse?, - error: NKError) { + responseData: AFDataResponse?, + error: NKError + ) { await withCheckedContinuation { continuation in - unsubscribingPushProxy( - proxyServerUrl: proxyServerUrl, - deviceIdentifier: deviceIdentifier, - signature: signature, - publicKey: publicKey, - account: account, - options: options, - taskHandler: { _ in }, - completion: { account, responseData, error in - continuation.resume(returning: (account, responseData, error)) - } - ) + unsubscribingPushProxy(proxyServerUrl: proxyServerUrl, + deviceIdentifier: deviceIdentifier, + signature: signature, + publicKey: publicKey, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } } } } diff --git a/Sources/NextcloudKit/NextcloudKit+RecommendedFiles.swift b/Sources/NextcloudKit/NextcloudKit+RecommendedFiles.swift index 1935cde8..2b5a2121 100644 --- a/Sources/NextcloudKit/NextcloudKit+RecommendedFiles.swift +++ b/Sources/NextcloudKit/NextcloudKit+RecommendedFiles.swift @@ -7,6 +7,15 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Retrieves a list of recommended files from the server. + /// + /// Parameters: + /// - account: The Nextcloud account used to perform the request. + /// - options: Optional configuration for headers, queue, versioning, etc. + /// - request: Optional callback to observe or manipulate the underlying DataRequest. + /// - taskHandler: Callback triggered when the URLSessionTask is created. + /// - completion: Completion handler returning the account, the list of recommendations, + /// the raw response data, and an NKError result. func getRecommendedFiles(account: String, options: NKRequestOptions = NKRequestOptions(), request: @escaping (DataRequest?) -> Void = { _ in }, @@ -48,12 +57,36 @@ public extension NextcloudKit { options.queue.async { request(tosRequest) } } + /// Asynchronously fetches a list of recommended files for the given account. + /// + /// - Parameters: + /// - account: The Nextcloud account requesting the recommendations. + /// - options: Optional configuration for queue, headers, etc. + /// - request: Optional callback to capture the DataRequest object. + /// - taskHandler: Optional handler for the URLSessionTask. + /// - Returns: A tuple containing the account, list of recommended files, raw response data, and NKError result. func getRecommendedFilesAsync(account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, recommendations: [NKRecommendation]?, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation({ continuation in - NextcloudKit.shared.getRecommendedFiles(account: account, options: options) { account, recommendations, responseData, error in - continuation.resume(returning: (account: account, recommendations: recommendations, responseData: responseData, error: error)) + options: NKRequestOptions = NKRequestOptions(), + request: @escaping (DataRequest?) -> Void = { _ in }, + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + recommendations: [NKRecommendation]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getRecommendedFiles(account: account, + options: options, + request: request, + taskHandler: taskHandler) { account, recommendations, responseData, error in + continuation.resume(returning: ( + account: account, + recommendations: recommendations, + responseData: responseData, + error: error + )) } - }) + } } } diff --git a/Sources/NextcloudKit/NextcloudKit+Richdocuments.swift b/Sources/NextcloudKit/NextcloudKit+Richdocuments.swift index 58005003..39133729 100644 --- a/Sources/NextcloudKit/NextcloudKit+Richdocuments.swift +++ b/Sources/NextcloudKit/NextcloudKit+Richdocuments.swift @@ -7,6 +7,15 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Requests a URL for editing or viewing a file via the Richdocuments (Collabora/OnlyOffice) app. + /// + /// Parameters: + /// - fileID: The unique identifier of the file for which the document URL is requested. + /// - account: The Nextcloud account performing the request. + /// - options: Optional configuration such as custom headers, queue, or API version. + /// - taskHandler: Callback invoked when the underlying URLSessionTask is created. + /// - completion: Completion handler returning the account, document URL (if available), + /// the raw HTTP response, and an NKError object. func createUrlRichdocuments(fileID: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -40,6 +49,47 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the URL for opening a file in Richdocuments (e.g., Collabora or OnlyOffice). + /// + /// - Parameters: + /// - fileID: The identifier of the target file. + /// - account: The Nextcloud account used for the operation. + /// - options: Request configuration (headers, queue, version, etc.). + /// - taskHandler: Optional handler to observe the URLSessionTask. + /// - Returns: A tuple containing the account, the richdocument URL (if any), the raw response data, and any NKError. + func createUrlRichdocumentsAsync(fileID: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + url: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + createUrlRichdocuments(fileID: fileID, + account: account, + options: options, + taskHandler: taskHandler) { account, url, responseData, error in + continuation.resume(returning: ( + account: account, + url: url, + responseData: responseData, + error: error + )) + } + } + } + + /// Retrieves the list of Richdocuments templates of a given type (e.g., "document", "spreadsheet"). + /// + /// Parameters: + /// - typeTemplate: The type of template to retrieve (e.g., "document", "presentation"). + /// - account: The Nextcloud account performing the request. + /// - options: Optional configuration (headers, queue, API version, etc.). + /// - taskHandler: Callback invoked when the underlying URLSessionTask is created. + /// - completion: Completion handler returning the account, array of templates, response data, and NKError. func getTemplatesRichdocuments(typeTemplate: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -84,16 +134,48 @@ public extension NextcloudKit { } } + /// Asynchronously fetches Richdocuments templates filtered by type. + /// + /// - Parameters: + /// - typeTemplate: The type of template to retrieve (e.g., "document"). + /// - account: The Nextcloud account for which templates are requested. + /// - options: Optional request configuration. + /// - taskHandler: Optional handler to observe the `URLSessionTask`. + /// - Returns: A tuple containing the account, array of templates, raw response data, and any NKError. func getTemplatesRichdocumentsAsync(typeTemplate: String, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, templates: [NKRichdocumentsTemplate]?, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation({ continuation in - NextcloudKit.shared.getTemplatesRichdocuments(typeTemplate: typeTemplate, account: account, options: options) { account, templates, responseData, error in - continuation.resume(returning: (account: account, templates: templates, responseData: responseData, error: error)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + templates: [NKRichdocumentsTemplate]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getTemplatesRichdocuments(typeTemplate: typeTemplate, + account: account, + options: options, + taskHandler: taskHandler) { account, templates, responseData, error in + continuation.resume(returning: ( + account: account, + templates: templates, + responseData: responseData, + error: error + )) } - }) + } } + /// Creates a new Richdocuments file using a specific template. + /// + /// Parameters: + /// - path: The target path where the new document should be created. + /// - templateId: The ID of the Richdocuments template to use. + /// - account: The Nextcloud account performing the request. + /// - options: Optional request configuration (headers, queue, API version, etc.). + /// - taskHandler: Callback invoked when the underlying URLSessionTask is created. + /// - completion: Completion handler returning the account, resulting file URL, raw response, and NKError. func createRichdocuments(path: String, templateId: String, account: String, @@ -128,6 +210,50 @@ public extension NextcloudKit { } } + /// Asynchronously creates a new Richdocuments file from a given template. + /// + /// - Parameters: + /// - path: Destination path for the new document. + /// - templateId: Template ID used to generate the new file. + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request parameters. + /// - taskHandler: Optional monitoring of the underlying task. + /// - Returns: A tuple with account, resulting URL (if successful), raw response, and error result. + func createRichdocumentsAsync(path: String, + templateId: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + url: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + createRichdocuments(path: path, + templateId: templateId, + account: account, + options: options, + taskHandler: taskHandler) { account, url, responseData, error in + continuation.resume(returning: ( + account: account, + url: url, + responseData: responseData, + error: error + )) + } + } + } + + /// Creates a new Richdocuments file based on a default asset (no template). + /// + /// Parameters: + /// - path: The destination path where the asset will be created. + /// - account: The Nextcloud account initiating the creation. + /// - options: Optional configuration for the request (e.g. headers, queue, API version). + /// - taskHandler: Callback invoked when the underlying URLSessionTask is created. + /// - completion: Completion handler returning account, resulting file URL, raw response data, and NKError. func createAssetRichdocuments(path: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -156,4 +282,37 @@ public extension NextcloudKit { } } } + + /// Asynchronously creates a Richdocuments asset file at a specified path. + /// + /// - Parameters: + /// - path: Target path for the asset document. + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request customization. + /// - taskHandler: Optional monitoring of the underlying task. + /// - Returns: A tuple with account, resulting URL, raw response, and error. + func createAssetRichdocumentsAsync(path: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + url: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + createAssetRichdocuments(path: path, + account: account, + options: options, + taskHandler: taskHandler) { account, url, responseData, error in + continuation.resume(returning: ( + account: account, + url: url, + responseData: responseData, + error: error + )) + } + } + } } diff --git a/Sources/NextcloudKit/NextcloudKit+Search.swift b/Sources/NextcloudKit/NextcloudKit+Search.swift index 13b7b05e..0bd64619 100644 --- a/Sources/NextcloudKit/NextcloudKit+Search.swift +++ b/Sources/NextcloudKit/NextcloudKit+Search.swift @@ -9,19 +9,20 @@ import SwiftyJSON public extension NextcloudKit { /// Available NC >= 20 - /// Search many different datasources in the cloud and combine them into one result. - /// - /// - Warning: Providers are requested concurrently. Not filtering will result in a high network load. - /// - /// - SeeAlso: - /// [Nextcloud Search API](https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/search.html) + /// Performs a unified search using multiple providers and returns results asynchronously. /// /// - Parameters: - /// - term: The search term - /// - options: Additional request options - /// - filter: Filter search provider that should be searched. Default is all available provider.. - /// - update: Callback, notifying that a search provider return its result. Does not include previous results. - /// - completion: Callback, notifying that all search providers have been searched. The search is done. Includes all search results. + /// - term: The search term to query. + /// - timeout: The individual request timeout per provider. + /// - timeoutProvider: The maximum time allowed for each provider before being cancelled. + /// - account: The Nextcloud account performing the search. + /// - options: Optional configuration for the request (headers, queue, etc.). + /// - filter: A closure to filter which `NKSearchProvider` are enabled. + /// - request: Callback to access and inspect the underlying `DataRequest?`. + /// - taskHandler: Callback triggered when a `URLSessionTask` is created. + /// - providers: Callback providing the list of providers that will be queried. + /// - update: Called for every result update from a provider. + /// - completion: Called when all providers are finished, returns the response and status. func unifiedSearch(term: String, timeout: TimeInterval = 30, timeoutProvider: TimeInterval = 60, @@ -76,21 +77,70 @@ public extension NextcloudKit { request(requestUnifiedSearch) } - /// Available NC >= 20 - /// Search many different datasources in the cloud and combine them into one result. + /// Asynchronously performs a unified search and returns the final search response. /// - /// - SeeAlso: - /// [Nextcloud Search API](https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/search.html) + /// - Parameters: + /// - term: The string to search for. + /// - timeout: Per-provider timeout in seconds. + /// - timeoutProvider: Overall timeout for a provider. + /// - account: The account used to authenticate the request. + /// - options: Optional parameters for the search. + /// - filter: Closure to filter the search providers. + /// - request: Callback with the underlying `DataRequest?`. + /// - taskHandler: Monitors the task creation. + /// - providers: Callback that reports which providers are used. + /// - update: Callback triggered as results come in from providers. + /// - Returns: Final completion with account, raw response data, and NKError. + func unifiedSearchAsync(term: String, + timeout: TimeInterval = 30, + timeoutProvider: TimeInterval = 60, + account: String, + options: NKRequestOptions = NKRequestOptions(), + filter: @escaping (NKSearchProvider) -> Bool = { _ in true }, + request: @escaping (DataRequest?) -> Void, + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, + providers: @escaping (_ account: String, _ searchProviders: [NKSearchProvider]?) -> Void, + update: @escaping (_ account: String, _ searchResult: NKSearchResult?, _ provider: NKSearchProvider, _ error: NKError) -> Void + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + unifiedSearch(term: term, + timeout: timeout, + timeoutProvider: timeoutProvider, + account: account, + options: options, + filter: filter, + request: request, + taskHandler: taskHandler, + providers: providers, + update: update) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + + /// Available NC >= 20 + /// Performs a search using a specified provider with pagination and timeout support. /// /// - Parameters: - /// - id: provider id - /// - term: The search term - /// - limit: limit (pagination) - /// - cursor: cursor (pagination) - /// - options: Additional request options - /// - timeout: Filter search provider that should be searched. Default is all available provider.. - /// - update: Callback, notifying that a search provider return its result. Does not include previous results. - /// - completion: Callback, notifying that all search results. + /// - id: The identifier of the search provider to use. + /// - term: The search term. + /// - limit: Optional maximum number of results to return. + /// - cursor: Optional pagination cursor for subsequent requests. + /// - timeout: The timeout interval for the search request. + /// - account: The Nextcloud account performing the search. + /// - options: Optional request configuration such as headers and queue. + /// - taskHandler: Callback to observe the underlying URLSessionTask. + /// - completion: Completion handler returning the account, search results, raw response, and NKError. + /// + /// - Returns: The underlying DataRequest object if the request was started, otherwise nil. func searchProvider(_ id: String, term: String, limit: Int? = nil, @@ -148,6 +198,52 @@ public extension NextcloudKit { return requestSearchProvider } + + /// Asynchronously performs a search request using the specified provider. + /// + /// - Parameters: + /// - id: The identifier of the search provider to use. + /// - term: The search query string. + /// - limit: Optional limit for number of results. + /// - cursor: Optional pagination cursor. + /// - timeout: The timeout for the request. + /// - account: The Nextcloud account performing the request. + /// - options: Optional configuration options for the request. + /// - taskHandler: Callback to observe the created task. + /// + /// - Returns: A tuple containing the account, search result, response data, and error. + func searchProviderAsync(_ id: String, + term: String, + limit: Int? = nil, + cursor: Int? = nil, + timeout: TimeInterval = 60, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + searchResult: NKSearchResult?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + _ = searchProvider(id, + term: term, + limit: limit, + cursor: cursor, + timeout: timeout, + account: account, + options: options, + taskHandler: taskHandler) { account, result, responseData, error in + continuation.resume(returning: ( + account: account, + searchResult: result, + responseData: responseData, + error: error + )) + } + } + } } public class NKSearchResult: NSObject { diff --git a/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift b/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift index fb23d2ca..a2f78314 100644 --- a/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift +++ b/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift @@ -11,6 +11,14 @@ public extension NextcloudKit { "ocs/v2.php/apps/files_downloadlimit/api/v1/\(token)/limit" } + /// Retrieves the current download limit for a shared file based on its public share token. + /// + /// Parameters: + /// - account: The Nextcloud account identifier. + /// - token: The public share token associated with the file or folder. + /// - completion: A closure returning: + /// - NKDownloadLimit?: The current download limit information, or `nil` if not available. + /// - NKError: An object representing success or error during the request. func getDownloadLimit(account: String, token: String, completion: @escaping (NKDownloadLimit?, NKError) -> Void) { let endpoint = makeEndpoint(with: token) let options = NKRequestOptions() @@ -76,6 +84,36 @@ public extension NextcloudKit { } } + /// Retrieves the current download limit for a shared file using its public token. + /// + /// Parameters: + /// - account: The account associated with the Nextcloud session. + /// - token: The public share token used to identify the shared file. + /// + /// Returns: A tuple containing: + /// - downloadLimit: The current NKDownloadLimit object if available. + /// - error: The NKError representing success or failure of the request. + func getDownloadLimitAsync(account: String, token: String) async -> ( + downloadLimit: NKDownloadLimit?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getDownloadLimit(account: account, token: token) { limit, error in + continuation.resume(returning: ( + downloadLimit: limit, + error: error + )) + } + } + } + + /// Removes the download limit for a shared file using its public share token. + /// + /// Parameters: + /// - account: The Nextcloud account identifier. + /// - token: The public share token associated with the file or folder. + /// - completion: A closure returning: + /// - NKError: An object representing the success or failure of the request. func removeShareDownloadLimit(account: String, token: String, completion: @escaping (_ error: NKError) -> Void) { let endpoint = makeEndpoint(with: token) let options = NKRequestOptions() @@ -108,6 +146,29 @@ public extension NextcloudKit { } } + /// Asynchronously removes the download limit for a public shared file or folder. + /// + /// Parameters: + /// - account: The Nextcloud account used for the request. + /// - token: The public token representing the shared resource. + /// + /// Returns: An NKError that indicates the outcome of the operation. + func removeShareDownloadLimitAsync(account: String, token: String) async -> NKError { + await withCheckedContinuation { continuation in + removeShareDownloadLimit(account: account, token: token) { error in + continuation.resume(returning: error) + } + } + } + + /// Sets a download limit for a public shared file or folder. + /// + /// Parameters: + /// - account: The Nextcloud account associated with the request. + /// - token: The public share token identifying the shared resource. + /// - limit: The new download limit to be set. + /// - completion: A closure returning: + /// - error: An NKError representing the success or failure of the operation. func setShareDownloadLimit(account: String, token: String, limit: Int, completion: @escaping (_ error: NKError) -> Void) { let endpoint = makeEndpoint(with: token) let options = NKRequestOptions() @@ -145,4 +206,20 @@ public extension NextcloudKit { } } } + + /// Asynchronously sets a download limit for a public shared file or folder. + /// + /// Parameters: + /// - account: The Nextcloud account used for the request. + /// - token: The public share token of the resource. + /// - limit: The maximum number of downloads to allow. + /// + /// Returns: An NKError indicating whether the operation was successful. + func setShareDownloadLimitAsync(account: String, token: String, limit: Int) async -> NKError { + await withCheckedContinuation { continuation in + setShareDownloadLimit(account: account, token: token, limit: limit) { error in + continuation.resume(returning: error) + } + } + } } diff --git a/Sources/NextcloudKit/NextcloudKit+Upload.swift b/Sources/NextcloudKit/NextcloudKit+Upload.swift index 05aebc41..15241b57 100644 --- a/Sources/NextcloudKit/NextcloudKit+Upload.swift +++ b/Sources/NextcloudKit/NextcloudKit+Upload.swift @@ -7,6 +7,27 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Uploads a file to the Nextcloud server. + /// + /// - Parameters: + /// - serverUrlFileName: The remote server URL or path where the file will be uploaded. + /// - fileNameLocalPath: The local file path to be uploaded. + /// - dateCreationFile: Optional creation date to include in headers (X-OC-CTime). + /// - dateModificationFile: Optional modification date to include in headers (X-OC-MTime). + /// - overwrite: If true, the remote file will be overwritten if it already exists. + /// - account: The account associated with the upload session. + /// - options: Optional configuration for the request (headers, queue, timeout, etc.). + /// - requestHandler: Called with the created UploadRequest. + /// - taskHandler: Called with the underlying URLSessionTask when it's created. + /// - progressHandler: Called periodically with upload progress. + /// - completionHandler: Called at the end of the upload with: + /// - account: The account used. + /// - ocId: The server-side file identifier. + /// - etag: The entity tag for versioning. + /// - date: The server date of the operation. + /// - size: The total uploaded size in bytes. + /// - headers: The response headers. + /// - nkError: The result status. func upload(serverUrlFileName: Any, fileNameLocalPath: String, dateCreationFile: Date? = nil, @@ -55,7 +76,7 @@ public extension NextcloudKit { } .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 { @@ -72,7 +93,7 @@ public extension NextcloudKit { 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 @@ -88,17 +109,98 @@ public extension NextcloudKit { options.queue.async { requestHandler(request) } } + /// Asynchronously uploads a file to the Nextcloud server. + /// /// - Parameters: - /// - directory: The local directory where is the file to be split - /// - fileName: The name of the file to be splites - /// - date: If exist the date of file - /// - creationDate: If exist the creation date of file - /// - serverUrl: The serverURL where the file will be deposited once reassembled - /// - chunkFolder: The name of temp folder, usually NSUUID().uuidString - /// - filesChunk: The struct it will contain all file names with the increment size still to be sent. - /// Example filename: "3","4","5" .... size: 30000000, 40000000, 43000000 - /// - chunkSizeInMB: Size in MB of chunk + /// - serverUrlFileName: The remote server URL or path where the file will be uploaded. + /// - fileNameLocalPath: The local file path to be uploaded. + /// - dateCreationFile: Optional creation date to include in headers (X-OC-CTime). + /// - dateModificationFile: Optional modification date to include in headers (X-OC-MTime). + /// - overwrite: If true, the remote file will be overwritten if it already exists. + /// - account: The account associated with the upload session. + /// - options: Optional configuration for the request (headers, queue, timeout, etc.). + /// - requestHandler: Called with the created UploadRequest. + /// - taskHandler: Called with the underlying URLSessionTask when it's created. + /// - progressHandler: Called periodically with upload progress. + /// + /// - Returns: A tuple containing: + /// - account: The account used for the upload. + /// - ocId: The remote file identifier returned by the server. + /// - etag: The file etag returned by the server. + /// - date: The server timestamp. + /// - size: The size of the uploaded file in bytes. + /// - headers: The raw HTTP response headers. + /// - error: The NKError result of the upload. + func uploadAsync(serverUrlFileName: Any, + fileNameLocalPath: String, + dateCreationFile: Date? = nil, + dateModificationFile: Date? = nil, + overwrite: Bool = false, + account: String, + options: NKRequestOptions = NKRequestOptions(), + requestHandler: @escaping (_ request: UploadRequest) -> Void = { _ in }, + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, + progressHandler: @escaping (_ progress: Progress) -> Void = { _ in } + ) async -> ( + account: String, + ocId: String?, + etag: String?, + date: Date?, + size: Int64, + headers: [AnyHashable: Any]?, + error: NKError + ) { + await withCheckedContinuation { continuation in + upload(serverUrlFileName: serverUrlFileName, + fileNameLocalPath: fileNameLocalPath, + dateCreationFile: dateCreationFile, + dateModificationFile: dateModificationFile, + overwrite: overwrite, + account: account, + options: options, + requestHandler: requestHandler, + taskHandler: taskHandler, + progressHandler: progressHandler) { account, ocId, etag, date, size, headers, error in + continuation.resume(returning: ( + account: account, + ocId: ocId, + etag: etag, + date: date, + size: size, + headers: headers, + error: error + )) + } + } + } + /// Uploads a file in multiple chunks to the Nextcloud server using TUS-like behavior. + /// + /// - Parameters: + /// - directory: The local directory containing the original file. + /// - fileChunksOutputDirectory: Optional custom output directory for chunks (default is same as `directory`). + /// - fileName: Name of the original file to split and upload. + /// - destinationFileName: Optional custom filename to be used on the server. + /// - date: The modification date to be set on the uploaded file. + /// - creationDate: The creation date to be set on the uploaded file. + /// - serverUrl: The destination server path. + /// - chunkFolder: A temporary folder name (usually a UUID). + /// - filesChunk: List of chunk identifiers and their expected sizes. + /// - chunkSize: Size of each chunk in bytes. + /// - account: The Nextcloud account used for authentication. + /// - options: Request options (headers, queue, etc.). + /// - numChunks: Callback invoked with total number of chunks. + /// - counterChunk: Callback invoked with the index of the chunk being uploaded. + /// - start: Called when chunk upload begins, with the full chunk list. + /// - requestHandler: Handler to inspect the upload request. + /// - taskHandler: Handler to inspect the upload task. + /// - progressHandler: Progress callback with expected bytes, transferred bytes, and fraction completed. + /// - uploaded: Called each time a chunk is successfully uploaded. + /// - completion: Called when all chunks are uploaded and reassembled. Returns: + /// - account: The user account used. + /// - filesChunk: Remaining chunks (if any). + /// - file: The final `NKFile` metadata for the uploaded file. + /// - error: Upload result as `NKError`. func uploadChunk(directory: String, fileChunksOutputDirectory: String? = nil, fileName: String, @@ -119,7 +221,6 @@ public extension NextcloudKit { 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?, _ error: NKError) -> Void) { - guard let nkSession = nkCommonInstance.nksessions.session(forAccount: account) else { return completion(account, nil, nil, .urlError) } @@ -260,4 +361,67 @@ public extension NextcloudKit { } } } + + /// Asynchronously uploads a file in chunks and assembles it on the Nextcloud server. + /// + /// - Parameters: Same as the sync version. + /// - Returns: A tuple containing: + /// - account: The user account used. + /// - remainingChunks: Remaining chunks if any failed (or nil if success). + /// - file: The final file metadata object. + /// - error: Upload result as `NKError`. + func uploadChunkAsync(directory: String, + fileChunksOutputDirectory: String? = nil, + fileName: String, + destinationFileName: String? = nil, + date: Date?, + creationDate: Date?, + serverUrl: String, + chunkFolder: String, + filesChunk: [(fileName: String, size: Int64)], + chunkSize: Int, + account: String, + options: NKRequestOptions = NKRequestOptions(), + numChunks: @escaping (_ num: Int) -> Void = { _ in }, + counterChunk: @escaping (_ counter: Int) -> Void = { _ in }, + start: @escaping (_ filesChunk: [(fileName: String, size: Int64)]) -> Void = { _ in }, + requestHandler: @escaping (_ request: UploadRequest) -> Void = { _ in }, + 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 } + ) async -> ( + account: String, + remainingChunks: [(fileName: String, size: Int64)]?, + file: NKFile?, + error: NKError + ) { + await withCheckedContinuation { continuation in + uploadChunk(directory: directory, + fileChunksOutputDirectory: fileChunksOutputDirectory, + fileName: fileName, + destinationFileName: destinationFileName, + date: date, + creationDate: creationDate, + serverUrl: serverUrl, + chunkFolder: chunkFolder, + filesChunk: filesChunk, + chunkSize: chunkSize, + account: account, + options: options, + numChunks: numChunks, + counterChunk: counterChunk, + start: start, + requestHandler: requestHandler, + taskHandler: taskHandler, + progressHandler: progressHandler, + uploaded: uploaded) { account, remaining, file, error in + continuation.resume(returning: ( + account: account, + remainingChunks: remaining, + file: file, + error: error + )) + } + } + } } diff --git a/Sources/NextcloudKit/NextcloudKit+UserStatus.swift b/Sources/NextcloudKit/NextcloudKit+UserStatus.swift index 06465885..6ba1101b 100644 --- a/Sources/NextcloudKit/NextcloudKit+UserStatus.swift +++ b/Sources/NextcloudKit/NextcloudKit+UserStatus.swift @@ -7,6 +7,25 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Retrieves the user status from the Nextcloud server. + /// + /// - Parameters: + /// - userId: Optional user ID to query (if `nil`, fetches the status for the authenticated user). + /// - account: The Nextcloud account identifier. + /// - options: Optional request options (headers, queue, version, etc.). + /// - taskHandler: Callback for monitoring the `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The user account used. + /// - clearAt: Optional expiration `Date` of the status. + /// - icon: Optional status icon name. + /// - message: Optional status message. + /// - messageId: Optional ID of the predefined message. + /// - messageIsPredefined: Indicates whether the message is predefined. + /// - status: Optional raw status value. + /// - statusIsUserDefined: Indicates if the status was set manually by the user. + /// - userId: The actual user ID returned by the server. + /// - responseData: Raw response data from the server. + /// - error: Result as `NKError`. func getUserStatus(userId: String? = nil, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -55,20 +74,58 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the user status from the Nextcloud server. + /// + /// - Parameters: Same as the sync version. + /// - Returns: A tuple with explicitly named values describing the user status. func getUserStatusAsync(userId: String? = nil, account: String, options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, clearAt: Date?, icon: String?, message: String?, messageId: String?, messageIsPredefined: Bool, status: String?, statusIsUserDefined: Bool, userId: String?, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation { continuation in + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + clearAt: Date?, + icon: String?, + message: String?, + messageId: String?, + messageIsPredefined: Bool, + status: String?, + statusIsUserDefined: Bool, + userId: String?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in getUserStatus(userId: userId, account: account, options: options, taskHandler: taskHandler) { account, clearAt, icon, message, messageId, messageIsPredefined, status, statusIsUserDefined, userId, responseData, error in - continuation.resume(returning: (account, clearAt, icon, message, messageId, messageIsPredefined, status, statusIsUserDefined, userId, responseData, error)) + continuation.resume(returning: ( + account: account, + clearAt: clearAt, + icon: icon, + message: message, + messageId: messageId, + messageIsPredefined: messageIsPredefined, + status: status, + statusIsUserDefined: statusIsUserDefined, + userId: userId, + responseData: responseData, + error: error + )) } } } + + /// Sets the current user status on the Nextcloud server. + /// + /// Parameters: + /// - status: The raw status value to be set (e.g. "online", "away", etc.). + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request configuration. + /// - taskHandler: Callback for the underlying `URLSessionTask`. + /// - completion: Returns the account, the raw response data, and any resulting NKError. func setUserStatus(status: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -104,6 +161,45 @@ public extension NextcloudKit { } } + /// Asynchronously sets the current user status on the Nextcloud server. + /// + /// - Parameters: Same as the sync version. + /// - Returns: A tuple with the account, responseData, and NKError. + func setUserStatusAsync(status: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + setUserStatus(status: status, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + + /// Sets a predefined custom message for the user's status on the Nextcloud server. + /// + /// - Parameters: + /// - messageId: The identifier of the predefined message to be set. + /// - clearAt: A UNIX timestamp (in seconds) after which the message should expire (use `0` for no expiration). + /// - account: The account identifier used to authenticate the request. + /// - options: Optional request configuration, including headers, queue, and API version. + /// - taskHandler: Callback invoked with the `URLSessionTask` when the request is created. + /// - completion: Completion handler called with: + /// - account: The account used in the operation. + /// - responseData: The raw server response. + /// - error: A `NKError` indicating success or failure. func setCustomMessagePredefined(messageId: String, clearAt: Double, account: String, @@ -144,6 +240,53 @@ public extension NextcloudKit { } } + /// Asynchronously sets a predefined custom message with optional expiration. + /// + /// Parameters: + /// - messageId: The identifier of the predefined message to set. + /// - clearAt: Expiration timestamp (UNIX time) after which the message is cleared (optional, use 0 to skip). + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request configuration (headers, queue, etc.). + /// - taskHandler: Callback for monitoring the underlying URLSessionTask. + /// - Returns: A tuple containing the account identifier, the raw response, and any resulting NKError. + func setCustomMessagePredefinedAsync(messageId: String, + clearAt: Double, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + setCustomMessagePredefined(messageId: messageId, + clearAt: clearAt, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + + /// Sets a custom user-defined status message on the Nextcloud server. + /// + /// Parameters: + /// - statusIcon: Optional icon name representing the status. + /// - message: The custom status message string to display to other users. + /// - clearAt: Expiration timestamp (in UNIX time format) for the status message; use 0 to disable automatic clearing. + /// - account: The Nextcloud account identifier on which to apply the status. + /// - options: Optional request configuration such as headers, queue, or task description. + /// - taskHandler: Closure invoked when the URLSessionTask is created, used for task tracking or debugging. + /// - completion: Completion handler returning: + /// - account: The account identifier used. + /// - responseData: The raw Alamofire response data (if any). + /// - error: An `NKError` indicating success or failure. func setCustomMessageUserDefined(statusIcon: String?, message: String, clearAt: Double, @@ -188,6 +331,54 @@ public extension NextcloudKit { } } + /// Asynchronously sets a custom user-defined status message. + /// + /// Parameters: + /// - statusIcon: Optional icon to display with the message. + /// - message: The custom status message string. + /// - clearAt: Timestamp (UNIX time) when the message should expire (use 0 to skip). + /// - account: The Nextcloud account performing the operation. + /// - options: Request options such as headers, task description, queue. + /// - taskHandler: Callback for URLSessionTask monitoring. + /// + /// Returns: A tuple containing account, responseData, and any resulting NKError. + func setCustomMessageUserDefinedAsync(statusIcon: String?, + message: String, + clearAt: Double, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + setCustomMessageUserDefined(statusIcon: statusIcon, + message: message, + clearAt: clearAt, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + + /// Clears any custom or predefined user status message currently set on the Nextcloud server. + /// + /// Parameters: + /// - account: The Nextcloud account identifier whose status message should be cleared. + /// - options: Optional request configuration such as custom headers, dispatch queue, or task description. + /// - taskHandler: Closure called when the `URLSessionTask` is created, useful for debugging or tracking purposes. + /// - completion: Completion handler returning: + /// - account: The account identifier used. + /// - responseData: The raw Alamofire response object, if available. + /// - error: An `NKError` representing the result of the operation (success or failure). func clearMessage(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -220,6 +411,49 @@ public extension NextcloudKit { } } + /// Asynchronously clears any user status message (custom or predefined) on the Nextcloud server. + /// + /// Parameters: + /// - account: The Nextcloud account identifier whose status message will be cleared. + /// - options: Optional `NKRequestOptions` to customize the request (e.g., headers, queue). + /// - taskHandler: Callback triggered upon creation of the `URLSessionTask`. + /// + /// Returns: A tuple containing: + /// - account: The account identifier used for the operation. + /// - responseData: The raw `AFDataResponse` object returned by Alamofire. + /// - error: The resulting `NKError`, either `.success` or a failure case. + func clearMessageAsync(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + clearMessage(account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) + } + } + } + + /// Retrieves the list of predefined user statuses (e.g., "Available", "Away", "Do not disturb") from the Nextcloud server. + /// + /// - Parameters: + /// - account: The Nextcloud account identifier performing the request. + /// - options: Optional `NKRequestOptions` to customize the request (e.g., custom headers, queue). + /// - taskHandler: Callback triggered when the `URLSessionTask` is created. + /// - completion: Completion handler returning: + /// - account: The account identifier used for the request. + /// - userStatuses: An optional array of predefined `NKUserStatus` objects. + /// - responseData: Raw `AFDataResponse` from Alamofire. + /// - error: Resulting `NKError` describing success or failure. func getUserStatusPredefinedStatuses(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -268,6 +502,57 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves the predefined user statuses available on the Nextcloud server. + /// + /// These predefined statuses are managed by the server and include standardized status types + /// (e.g. "online", "away", "do not disturb") which can be selected by users. + /// + /// Parameters: + /// - account: The identifier of the Nextcloud account making the request. + /// - options: Optional request configuration (headers, queue, API version, etc.). + /// - taskHandler: A closure that is called when the URLSessionTask is created. + /// + /// Returns: A tuple containing: + /// - account: The account used for the request. + /// - userStatuses: An optional array of `NKUserStatus` representing the predefined statuses. + /// - responseData: The raw HTTP response returned from the server. + /// - error: The result of the request as an `NKError` object. + func getUserStatusPredefinedStatusesAsync(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + userStatuses: [NKUserStatus]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getUserStatusPredefinedStatuses(account: account, + options: options, + taskHandler: taskHandler) { account, userStatuses, responseData, error in + continuation.resume(returning: ( + account: account, + userStatuses: userStatuses, + responseData: responseData, + error: error + )) + } + } + } + + /// Retrieves a list of user statuses from the Nextcloud server. + /// + /// - Parameters: + /// - limit: The maximum number of statuses to retrieve. + /// - offset: The number of statuses to skip before starting to collect the results. + /// - account: The account identifier used to perform the request. + /// - options: Optional request configuration (headers, queue, etc.). + /// - taskHandler: Callback invoked with the `URLSessionTask` when the request is created. + /// - completion: Completion handler called with: + /// - account: The account used in the operation. + /// - userStatuses: An array of `NKUserStatus` objects, or `nil` if an error occurred. + /// - responseData: The raw server response. + /// - error: A `NKError` indicating success or failure. func getUserStatusRetrieveStatuses(limit: Int, offset: Int, account: String, @@ -319,4 +604,45 @@ public extension NextcloudKit { } } } + + /// Asynchronously retrieves a list of user statuses from the Nextcloud server. + /// + /// - Parameters: + /// - limit: The maximum number of statuses to retrieve. + /// - offset: The number of statuses to skip before collecting results. + /// - account: The account identifier used to perform the request. + /// - options: Optional request configuration (headers, queue, etc.). + /// - taskHandler: Callback invoked with the `URLSessionTask` when the request is created. + /// + /// - Returns: A tuple containing: + /// - account: The account used for the request. + /// - userStatuses: An array of `NKUserStatus` objects, or `nil` if an error occurred. + /// - responseData: The raw server response. + /// - error: A `NKError` describing the result. + func getUserStatusRetrieveStatusesAsync(limit: Int, + offset: Int, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + userStatuses: [NKUserStatus]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getUserStatusRetrieveStatuses(limit: limit, + offset: offset, + account: account, + options: options, + taskHandler: taskHandler) { account, userStatuses, responseData, error in + continuation.resume(returning: ( + account: account, + userStatuses: userStatuses, + responseData: responseData, + error: error + )) + } + } + } } diff --git a/Sources/NextcloudKit/NextcloudKit+WebDAV.swift b/Sources/NextcloudKit/NextcloudKit+WebDAV.swift index 3e8fef6b..64470181 100644 --- a/Sources/NextcloudKit/NextcloudKit+WebDAV.swift +++ b/Sources/NextcloudKit/NextcloudKit+WebDAV.swift @@ -7,6 +7,19 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + /// Creates a folder on the Nextcloud server at the specified path. + /// + /// - Parameters: + /// - serverUrlFileName: The full URL string of the folder to create. + /// - account: The Nextcloud account identifier. + /// - options: Optional request options including headers, timeout, and queue. + /// - taskHandler: Callback triggered with the underlying `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the request. + /// - ocId: Optional file ID assigned by the server for the new folder. + /// - date: Optional date from the server response headers. + /// - responseData: The raw Alamofire response data. + /// - error: The `NKError` result indicating success or failure. func createFolder(serverUrlFileName: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -43,16 +56,48 @@ public extension NextcloudKit { } } + /// Asynchronously creates a folder on the Nextcloud server. + /// + /// - Parameters: Same as the sync version. + /// - Returns: A tuple with account, optional ocId, optional date, responseData, and NKError. func createFolderAsync(serverUrlFileName: String, - account: String, - 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)) + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + ocId: String?, + date: Date?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + createFolder(serverUrlFileName: serverUrlFileName, + account: account, + options: options, + taskHandler: taskHandler) { account, ocId, date, responseData, error in + continuation.resume(returning: ( + account: account, + ocId: ocId, + date: date, + responseData: responseData, + error: error + )) } - }) + } } + /// Deletes a file or folder from the Nextcloud server at the specified URL. + /// + /// - Parameters: + /// - serverUrlFileName: The full URL string of the file or folder to delete. + /// - account: The Nextcloud account identifier. + /// - options: Optional request options including headers, timeout, and queue. + /// - taskHandler: Callback triggered with the underlying `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the request. + /// - responseData: The raw Alamofire response data. + /// - error: The `NKError` result indicating success or failure. func deleteFileOrFolder(serverUrlFileName: String, account: String, options: NKRequestOptions = NKRequestOptions(), @@ -83,16 +128,54 @@ public extension NextcloudKit { } } + /// Asynchronously deletes a file or folder from the Nextcloud server. + /// + /// - Parameters: + /// - serverUrlFileName: The full URL string of the file or folder to delete. + /// - account: The Nextcloud account identifier. + /// - options: Optional request options including headers, timeout, and queue. + /// - taskHandler: Callback triggered with the underlying `URLSessionTask`. + /// + /// - Returns: A tuple containing: + /// - account: The account used for the request. + /// - responseData: The raw Alamofire response data. + /// - error: The `NKError` result indicating success or failure. func deleteFileOrFolderAsync(serverUrlFileName: String, account: String, - 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)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + deleteFileOrFolder(serverUrlFileName: serverUrlFileName, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) } - }) + } } + /// Moves or renames a file or folder on the Nextcloud server. + /// + /// - Parameters: + /// - serverUrlFileNameSource: The full URL string of the source file or folder to move. + /// - serverUrlFileNameDestination: The full URL string of the destination path. + /// - overwrite: Whether to overwrite the destination if it exists. + /// - account: The Nextcloud account identifier. + /// - options: Optional request options including headers, timeout, and queue. + /// - taskHandler: Callback triggered with the underlying `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the request. + /// - responseData: The raw Alamofire response data. + /// - error: The `NKError` result indicating success or failure. func moveFileOrFolder(serverUrlFileNameSource: String, serverUrlFileNameDestination: String, overwrite: Bool, @@ -132,18 +215,60 @@ public extension NextcloudKit { } } + /// Asynchronously moves or renames a file or folder on the Nextcloud server. + /// + /// - Parameters: + /// - serverUrlFileNameSource: The full URL string of the source file or folder to move. + /// - serverUrlFileNameDestination: The full URL string of the destination path. + /// - overwrite: Whether to overwrite the destination if it exists. + /// - account: The Nextcloud account identifier. + /// - options: Optional request options including headers, timeout, and queue. + /// - taskHandler: Callback triggered with the underlying `URLSessionTask`. + /// + /// - Returns: A tuple containing: + /// - account: The account used for the request. + /// - responseData: The raw Alamofire response data. + /// - error: The `NKError` result indicating success or failure. func moveFileOrFolderAsync(serverUrlFileNameSource: String, serverUrlFileNameDestination: String, overwrite: Bool, account: String, - 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)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + moveFileOrFolder(serverUrlFileNameSource: serverUrlFileNameSource, + serverUrlFileNameDestination: serverUrlFileNameDestination, + overwrite: overwrite, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) } - }) + } } + /// Copies a file or folder on the Nextcloud server from a source path to a destination path. + /// + /// - Parameters: + /// - serverUrlFileNameSource: The full URL string of the source file or folder to copy. + /// - serverUrlFileNameDestination: The full URL string of the destination path. + /// - overwrite: A Boolean indicating whether to overwrite the destination if it exists. + /// - account: The Nextcloud account identifier. + /// - options: Optional request configuration including headers, timeout, and queue. + /// - taskHandler: Callback triggered with the underlying `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the request. + /// - responseData: The raw Alamofire response data. + /// - error: An `NKError` indicating success or failure. func copyFileOrFolder(serverUrlFileNameSource: String, serverUrlFileNameDestination: String, overwrite: Bool, @@ -184,18 +309,63 @@ public extension NextcloudKit { } } + /// Asynchronously copies a file or folder on the Nextcloud server. + /// + /// - Parameters: + /// - serverUrlFileNameSource: The full URL string of the source file or folder. + /// - serverUrlFileNameDestination: The full URL string of the destination. + /// - overwrite: Indicates whether to overwrite existing destination. + /// - account: The Nextcloud account identifier. + /// - options: Optional request configuration. + /// - taskHandler: Callback for the underlying `URLSessionTask`. + /// + /// - Returns: A tuple containing: + /// - account: The account used for the request. + /// - responseData: The raw Alamofire response data. + /// - error: The resulting `NKError`. func copyFileOrFolderAsync(serverUrlFileNameSource: String, serverUrlFileNameDestination: String, overwrite: Bool, account: String, - 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)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + copyFileOrFolder(serverUrlFileNameSource: serverUrlFileNameSource, + serverUrlFileNameDestination: serverUrlFileNameDestination, + overwrite: overwrite, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) } - }) + } } + /// Reads the contents of a file or folder on the Nextcloud server. + /// + /// - Parameters: + /// - serverUrlFileName: The full URL string of the file or folder to read. + /// - depth: The depth level for folder traversal (e.g., "0", "1", "infinity"). + /// - showHiddenFiles: Boolean flag indicating whether to show hidden files (default is true). + /// - includeHiddenFiles: An array of specific hidden filenames to include despite hidden status. + /// - requestBody: Optional raw data to send as the request body. + /// - account: The Nextcloud account identifier. + /// - options: Optional request configuration including headers, timeout, and queue. + /// - taskHandler: Callback triggered with the underlying `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the request. + /// - files: An optional array of `NKFile` objects representing the contents. + /// - responseData: The raw Alamofire response data. + /// - error: An `NKError` indicating success or failure. func readFileOrFolder(serverUrlFileName: String, depth: String, showHiddenFiles: Bool = true, @@ -256,19 +426,69 @@ public extension NextcloudKit { } } + /// Asynchronously reads the contents of a file or folder. + /// + /// - Parameters: + /// - serverUrlFileName: The full URL string of the file or folder. + /// - depth: The depth level to traverse. + /// - showHiddenFiles: Whether to show hidden files. + /// - includeHiddenFiles: Specific hidden files to include. + /// - requestBody: Optional request body data. + /// - account: The Nextcloud account identifier. + /// - options: Optional request configuration. + /// - taskHandler: Callback for the underlying URLSessionTask. + /// + /// - Returns: A tuple containing: + /// - account: The account used for the request. + /// - files: Optional array of `NKFile` contents. + /// - responseData: The raw Alamofire response data. + /// - error: The resulting `NKError`. func readFileOrFolderAsync(serverUrlFileName: String, depth: String, showHiddenFiles: Bool = true, + includeHiddenFiles: [String] = [], requestBody: Data? = nil, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, files: [NKFile]?, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation({ continuation in - NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: depth, showHiddenFiles: showHiddenFiles, requestBody: requestBody, account: account, options: options) { account, files, responseData, error in - continuation.resume(returning: (account: account, files: files, responseData: responseData, error: error)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + files: [NKFile]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + readFileOrFolder(serverUrlFileName: serverUrlFileName, + depth: depth, + showHiddenFiles: showHiddenFiles, + includeHiddenFiles: includeHiddenFiles, + requestBody: requestBody, + account: account, + options: options, + taskHandler: taskHandler) { account, files, responseData, error in + continuation.resume(returning: ( + account: account, + files: files, + responseData: responseData, + error: error + )) } - }) + } } + /// Retrieves a file object from the server using either a file ID or a direct link. + /// + /// - Parameters: + /// - fileId: Optional file identifier to fetch the file. + /// - link: Optional direct link to the file. + /// - account: The Nextcloud account identifier. + /// - options: Optional request options (headers, queue, etc.). + /// - taskHandler: Callback triggered with the underlying `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the request. + /// - file: Optional `NKFile` object representing the file. + /// - responseData: Raw Alamofire response data. + /// - error: An `NKError` indicating success or failure. func getFileFromFileId(fileId: String? = nil, link: String? = nil, account: String, @@ -299,6 +519,62 @@ public extension NextcloudKit { } } + /// Asynchronously retrieves a file object using file ID or link. + /// + /// - Parameters: + /// - fileId: Optional file ID. + /// - link: Optional direct link. + /// - account: Nextcloud account identifier. + /// - options: Optional request options. + /// - taskHandler: Callback for URLSessionTask monitoring. + /// + /// - Returns: A tuple containing: + /// - account: The account used. + /// - file: Optional `NKFile` object. + /// - responseData: Raw response data. + /// - error: Resulting `NKError`. + func getFileFromFileIdAsync(fileId: String? = nil, + link: String? = nil, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + file: NKFile?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + getFileFromFileId(fileId: fileId, + link: link, + account: account, + options: options, + taskHandler: taskHandler) { account, file, responseData, error in + continuation.resume(returning: ( + account: account, + file: file, + responseData: responseData, + error: error + )) + } + } + } + + /// Performs a search on the server with a given XML body request. + /// + /// - Parameters: + /// - serverUrl: The base URL of the server. + /// - requestBody: The XML request body as a string. + /// - showHiddenFiles: Whether to include hidden files in the search. + /// - includeHiddenFiles: An array of hidden file names to specifically include. + /// - account: The Nextcloud account identifier. + /// - options: Optional request options (headers, queue, etc.). + /// - taskHandler: Callback triggered with the underlying `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the request. + /// - files: Optional array of `NKFile` results matching the search. + /// - responseData: Raw Alamofire response data. + /// - error: An `NKError` indicating success or failure. func searchBodyRequest(serverUrl: String, requestBody: String, showHiddenFiles: Bool, @@ -316,6 +592,69 @@ public extension NextcloudKit { } } + /// Asynchronously performs a search on the server with an XML request body. + /// + /// - Parameters: + /// - serverUrl: The base URL of the server. + /// - requestBody: The XML request body string. + /// - showHiddenFiles: Flag to include hidden files. + /// - includeHiddenFiles: Array of hidden files to include. + /// - account: The Nextcloud account. + /// - options: Optional request options. + /// - taskHandler: Callback to observe the underlying URLSessionTask. + /// + /// - Returns: A tuple containing: + /// - account: The account used. + /// - files: Optional array of `NKFile` results. + /// - responseData: Raw response data. + /// - error: Resulting `NKError`. + func searchBodyRequestAsync(serverUrl: String, + requestBody: String, + showHiddenFiles: Bool, + includeHiddenFiles: [String] = [], + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + files: [NKFile]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + searchBodyRequest(serverUrl: serverUrl, + requestBody: requestBody, + showHiddenFiles: showHiddenFiles, + includeHiddenFiles: includeHiddenFiles, + account: account, + options: options, + taskHandler: taskHandler) { account, files, responseData, error in + continuation.resume(returning: ( + account: account, + files: files, + responseData: responseData, + error: error + )) + } + } + } + + /// Performs a search on the server with a literal string query. + /// + /// - Parameters: + /// - serverUrl: The base URL of the server. + /// - depth: The depth of the search in the directory hierarchy. + /// - literal: The literal search string to query. + /// - showHiddenFiles: Whether to include hidden files in the search. + /// - includeHiddenFiles: Specific hidden files to include. + /// - account: The Nextcloud account identifier. + /// - options: Optional request options (headers, queue, etc.). + /// - taskHandler: Callback for the underlying `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the request. + /// - files: Optional array of `NKFile` objects matching the search. + /// - responseData: Raw Alamofire response data. + /// - error: An `NKError` indicating success or failure. func searchLiteral(serverUrl: String, depth: String, literal: String, @@ -339,6 +678,72 @@ public extension NextcloudKit { } } + /// Asynchronously performs a literal search on the server. + /// + /// - Parameters: + /// - serverUrl: The server base URL. + /// - depth: Search depth. + /// - literal: Literal query string. + /// - showHiddenFiles: Whether to include hidden files. + /// - includeHiddenFiles: Specific hidden files to include. + /// - account: Nextcloud account identifier. + /// - options: Optional request options. + /// - taskHandler: Callback for the underlying URLSessionTask. + /// + /// - Returns: A tuple containing: + /// - account: The account used for the request. + /// - files: Optional array of NKFile results. + /// - responseData: Raw response data. + /// - error: Resulting NKError. + func searchLiteralAsync(serverUrl: String, + depth: String, + literal: String, + showHiddenFiles: Bool, + includeHiddenFiles: [String] = [], + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + files: [NKFile]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + searchLiteral(serverUrl: serverUrl, + depth: depth, + literal: literal, + showHiddenFiles: showHiddenFiles, + includeHiddenFiles: includeHiddenFiles, + account: account, + options: options, + taskHandler: taskHandler) { account, files, responseData, error in + continuation.resume(returning: ( + account: account, + files: files, + responseData: responseData, + error: error + )) + } + } + } + + /// Searches media files within a specified date range on the server. + /// + /// - Parameters: + /// - path: The directory path to search within (default is empty string for root). + /// - lessDate: The upper bound date filter (files older than this). + /// - greaterDate: The lower bound date filter (files newer than this). + /// - elementDate: The file date attribute to filter on (e.g., "created", "modified"). + /// - limit: Maximum number of files to return. + /// - account: The Nextcloud account identifier. + /// - options: Optional request options (headers, queue, etc.). + /// - taskHandler: Callback for monitoring the underlying `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the request. + /// - files: Optional array of matching `NKFile` objects. + /// - responseData: Raw Alamofire response data. + /// - error: An `NKError` describing success or failure. func searchMedia(path: String = "", lessDate: Any, greaterDate: Any, @@ -379,6 +784,71 @@ public extension NextcloudKit { } } + /// Asynchronously searches media files with date filters. + /// + /// - Parameters: + /// - path: Directory path to search. + /// - lessDate: Upper date bound filter. + /// - greaterDate: Lower date bound filter. + /// - elementDate: File date attribute to filter on. + /// - limit: Maximum number of results. + /// - account: Nextcloud account identifier. + /// - options: Optional request options. + /// - taskHandler: Callback for URLSessionTask monitoring. + /// + /// - Returns: A tuple containing: + /// - account: Account used for the request. + /// - files: Optional array of `NKFile` matching results. + /// - responseData: Raw server response data. + /// - error: Resulting `NKError`. + func searchMediaAsync(path: String = "", + lessDate: Any, + greaterDate: Any, + elementDate: String, + limit: Int, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + files: [NKFile]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + searchMedia(path: path, + lessDate: lessDate, + greaterDate: greaterDate, + elementDate: elementDate, + limit: limit, + account: account, + options: options, + taskHandler: taskHandler) { account, files, responseData, error in + continuation.resume(returning: ( + account: account, + files: files, + responseData: responseData, + error: error + )) + } + } + } + + /// Performs a private search request with a custom HTTP body on the server. + /// + /// - Parameters: + /// - serverUrl: The base URL of the Nextcloud server. + /// - httpBody: The raw HTTP request body data for the search. + /// - showHiddenFiles: Boolean indicating whether to include hidden files in the search. + /// - includeHiddenFiles: Specific hidden files to include despite general hidden files exclusion. + /// - account: The Nextcloud account identifier. + /// - options: Optional request options (headers, queue, version, etc.). + /// - taskHandler: Callback to monitor the created `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the request. + /// - files: Optional array of `NKFile` matching the search. + /// - responseData: Raw response data from Alamofire. + /// - error: An `NKError` indicating success or failure. private func search(serverUrl: String, httpBody: Data, showHiddenFiles: Bool, @@ -428,15 +898,74 @@ public extension NextcloudKit { } } + /// Asynchronously performs a search with a custom HTTP body. + /// + /// - Parameters: + /// - serverUrl: The base URL of the Nextcloud server. + /// - httpBody: The raw HTTP request body data for the search. + /// - showHiddenFiles: Boolean indicating whether to include hidden files in the search. + /// - includeHiddenFiles: Specific hidden files to include despite general hidden files exclusion. + /// - account: The Nextcloud account identifier. + /// - options: Optional request options (headers, queue, version, etc.). + /// - taskHandler: Callback to monitor the created `URLSessionTask`. + /// + /// - Returns: A tuple containing: + /// - account: The account used for the request. + /// - files: Optional array of `NKFile` results. + /// - responseData: Raw response data. + /// - error: Resulting `NKError`. + private func searchAsync(serverUrl: String, + httpBody: Data, + showHiddenFiles: Bool, + includeHiddenFiles: [String], + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + files: [NKFile]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + search(serverUrl: serverUrl, + httpBody: httpBody, + showHiddenFiles: showHiddenFiles, + includeHiddenFiles: includeHiddenFiles, + account: account, + options: options, + taskHandler: taskHandler) { account, files, responseData, error in + continuation.resume(returning: ( + account: account, + files: files, + responseData: responseData, + error: error + )) + } + } + } + + /// Sets or removes a favorite flag on a specified file or folder on the Nextcloud server. + /// + /// - Parameters: + /// - fileName: The full path or URL-encoded name of the file or folder. + /// - favorite: A Boolean value indicating whether to set (`true`) or remove (`false`) the favorite status. + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request configuration (headers, queue, version, etc.). + /// - taskHandler: Callback for monitoring the underlying `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the operation. + /// - responseData: Raw Alamofire response data. + /// - error: An `NKError` indicating success or failure. func setFavorite(fileName: String, favorite: Bool, account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { - /// + // options.contentType = "application/xml" - /// + // guard let nkSession = nkCommonInstance.nksessions.session(forAccount: account), let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { return options.queue.async { completion(account, nil, .urlError) } @@ -469,26 +998,61 @@ public extension NextcloudKit { } } + /// Asynchronously sets or removes a favorite flag on a file or folder. + /// + /// - Parameters: Same as the synchronous version. + /// + /// - Returns: A tuple containing: + /// - account: The account used for the operation. + /// - responseData: Raw response data from Alamofire. + /// - error: The resulting `NKError`. func setFavoriteAsync(fileName: String, favorite: Bool, account: String, - options: NKRequestOptions = NKRequestOptions()) async -> NKError { - await withUnsafeContinuation({ continuation in - NextcloudKit.shared.setFavorite(fileName: fileName, favorite: favorite, account: account) { _, _, error in - continuation.resume(returning: error) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + setFavorite(fileName: fileName, + favorite: favorite, + account: account, + options: options, + taskHandler: taskHandler) { account, responseData, error in + continuation.resume(returning: ( + account: account, + responseData: responseData, + error: error + )) } - }) + } } + /// Lists all favorite files and folders for a user on the Nextcloud server. + /// + /// - Parameters: + /// - showHiddenFiles: Whether to include hidden files in the results. + /// - includeHiddenFiles: Specific hidden files to include even if `showHiddenFiles` is false. + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request configuration (headers, queue, version, etc.). + /// - taskHandler: Callback for monitoring the underlying `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the operation. + /// - files: An optional array of `NKFile` representing the favorite items. + /// - responseData: Raw Alamofire response data. + /// - error: An `NKError` indicating success or failure. func listingFavorites(showHiddenFiles: Bool, includeHiddenFiles: [String] = [], account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, completion: @escaping (_ account: String, _ files: [NKFile]?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { - /// + // options.contentType = "application/xml" - /// + // guard let nkSession = nkCommonInstance.nksessions.session(forAccount: account), let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { return options.queue.async { completion(account, nil, nil, .urlError) } @@ -529,26 +1093,64 @@ public extension NextcloudKit { } } + /// Asynchronously lists all favorite files and folders for a user. + /// + /// - Parameters: Same as the synchronous version. + /// + /// - Returns: A tuple containing: + /// - account: The account used for the operation. + /// - files: An optional array of `NKFile` for the favorite items. + /// - responseData: Raw response data from Alamofire. + /// - error: The resulting `NKError`. func listingFavoritesAsync(showHiddenFiles: Bool, includeHiddenFiles: [String] = [], account: String, - options: NKRequestOptions = NKRequestOptions()) async -> (account: String, files: [NKFile]?, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation({ continuation in - NextcloudKit.shared.listingFavorites(showHiddenFiles: showHiddenFiles, includeHiddenFiles: includeHiddenFiles, account: account, options: options) { account, files, data, error in - continuation.resume(returning: (account, files, data, error)) + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + files: [NKFile]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in + listingFavorites(showHiddenFiles: showHiddenFiles, + includeHiddenFiles: includeHiddenFiles, + account: account, + options: options, + taskHandler: taskHandler) { account, files, responseData, error in + continuation.resume(returning: ( + account: account, + files: files, + responseData: responseData, + error: error + )) } - }) + } } + /// Lists the contents of the trash bin for a user on the Nextcloud server. + /// + /// - Parameters: + /// - filename: Optional specific filename to filter the trash items. + /// - showHiddenFiles: Whether to include hidden files in the trash listing. + /// - account: The Nextcloud account performing the operation. + /// - options: Optional request configuration (headers, queue, version, etc.). + /// - taskHandler: Callback for monitoring the underlying `URLSessionTask`. + /// - completion: Completion handler returning: + /// - account: The account used for the operation. + /// - items: An optional array of `NKTrash` objects representing trashed items. + /// - responseData: Raw Alamofire response data. + /// - error: An `NKError` indicating success or failure. func listingTrash(filename: String? = nil, showHiddenFiles: Bool, account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, completion: @escaping (_ account: String, _ items: [NKTrash]?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { - /// + // options.contentType = "application/xml" - /// + // guard let nkSession = nkCommonInstance.nksessions.session(forAccount: account), var headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { return options.queue.async { completion(account, nil, nil, .urlError) } @@ -593,18 +1195,38 @@ public extension NextcloudKit { } } + /// Asynchronously lists the trash contents for a user. + /// + /// - Parameters: Same as the synchronous version. + /// + /// - Returns: A tuple containing: + /// - account: The account used for the operation. + /// - items: An optional array of `NKTrash` representing trashed items. + /// - responseData: Raw response data from Alamofire. + /// - error: The resulting `NKError`. func listingTrashAsync(filename: String? = nil, showHiddenFiles: Bool, account: String, options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }) async -> (account: String, items: [NKTrash]?, responseData: AFDataResponse?, error: NKError) { - await withUnsafeContinuation { continuation in + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } + ) async -> ( + account: String, + items: [NKTrash]?, + responseData: AFDataResponse?, + error: NKError + ) { + await withCheckedContinuation { continuation in listingTrash(filename: filename, showHiddenFiles: showHiddenFiles, account: account, options: options, taskHandler: taskHandler) { account, items, responseData, error in - continuation.resume(returning: (account, items, responseData, error)) + continuation.resume(returning: ( + account: account, + items: items, + responseData: responseData, + error: error + )) } } } diff --git a/Sources/NextcloudKit/NextcloudKitBackground.swift b/Sources/NextcloudKit/NextcloudKitBackground.swift index 76810f40..0c2ab372 100644 --- a/Sources/NextcloudKit/NextcloudKitBackground.swift +++ b/Sources/NextcloudKit/NextcloudKitBackground.swift @@ -14,6 +14,17 @@ public final class NKBackground: NSObject, URLSessionTaskDelegate, URLSessionDel // MARK: - Download + /// Starts a download task for a file from the server to a local path. + /// + /// - Parameters: + /// - serverUrlFileName: The URL or URL string of the file to download. + /// - fileNameLocalPath: The local file path where the downloaded file will be saved. + /// - taskDescription: Optional description to set on the URLSession task. + /// - account: The Nextcloud account associated with the download. + /// + /// - Returns: A tuple containing: + /// - URLSessionDownloadTask?: The download task if created successfully. + /// - error: An `NKError` indicating success or failure in starting the download. public func download(serverUrlFileName: Any, fileNameLocalPath: String, taskDescription: String? = nil, @@ -63,8 +74,46 @@ public final class NKBackground: NSObject, URLSessionTaskDelegate, URLSessionDel return (task, .success) } + /// Asynchronously starts a download task for a file. + /// + /// - Parameters: Same as the synchronous version. + /// + /// - Returns: A tuple containing: + /// - downloadTask: The `URLSessionDownloadTask?` if successfully created. + /// - error: The `NKError` result. + public func downloadAsync(serverUrlFileName: Any, + fileNameLocalPath: String, + taskDescription: String? = nil, + account: String) async -> ( + downloadTask: URLSessionDownloadTask?, + error: NKError + ) { + await withCheckedContinuation { continuation in + let (task, error) = download(serverUrlFileName: serverUrlFileName, + fileNameLocalPath: fileNameLocalPath, + taskDescription: taskDescription, + account: account) + continuation.resume(returning: (downloadTask: task, error: error)) + } + } + // MARK: - Upload + /// Starts an upload task to send a local file to the server. + /// + /// - Parameters: + /// - serverUrlFileName: The server URL or URL string where the file will be uploaded. + /// - fileNameLocalPath: The local file path of the file to upload. + /// - dateCreationFile: Optional creation date metadata for the file. + /// - dateModificationFile: Optional modification date metadata for the file. + /// - taskDescription: Optional description to set on the URLSession task. + /// - overwrite: Boolean indicating whether to overwrite existing files on the server. + /// - account: The Nextcloud account associated with the upload. + /// - sessionIdentifier: A string identifier for the upload session. + /// + /// - Returns: A tuple containing: + /// - URLSessionUploadTask?: The upload task if created successfully. + /// - error: An `NKError` indicating success or failure in starting the upload. public func upload(serverUrlFileName: Any, fileNameLocalPath: String, dateCreationFile: Date?, @@ -138,6 +187,37 @@ public final class NKBackground: NSObject, URLSessionTaskDelegate, URLSessionDel return (task, .success) } + /// Asynchronously starts an upload task to send a local file. + /// + /// - Parameters: Same as the synchronous version. + /// + /// - Returns: A tuple containing: + /// - uploadTask: The `URLSessionUploadTask?` if successfully created. + /// - error: The `NKError` result. + public func uploadAsync(serverUrlFileName: Any, + fileNameLocalPath: String, + dateCreationFile: Date?, + dateModificationFile: Date?, + taskDescription: String? = nil, + overwrite: Bool = false, + account: String, + sessionIdentifier: String) async -> ( + uploadTask: URLSessionUploadTask?, + error: NKError + ) { + await withCheckedContinuation { continuation in + let (task, error) = upload(serverUrlFileName: serverUrlFileName, + fileNameLocalPath: fileNameLocalPath, + dateCreationFile: dateCreationFile, + dateModificationFile: dateModificationFile, + taskDescription: taskDescription, + overwrite: overwrite, + account: account, + sessionIdentifier: sessionIdentifier) + continuation.resume(returning: (uploadTask: task, error: error)) + } + } + // MARK: - SessionDelegate public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {