From b6a9e2b8c8cd7954f50555628eaed2eb07959877 Mon Sep 17 00:00:00 2001 From: Nikhil Nigade Date: Sun, 14 Jul 2024 16:19:30 +0530 Subject: [PATCH] Swift 6 Signed-off-by: Nikhil Nigade --- .../xcschemes/DZNetworking.xcscheme | 2 +- Package.swift | 2 +- .../Core/DZURLSession+Requests.swift | 28 ++++++++++------ Sources/DZNetworking/Core/DZURLSession.swift | 2 +- .../DZNetworking/Core/DZUploadSession.swift | 8 +++-- Sources/DZNetworking/Core/TaskHandler.swift | 6 ++-- .../Utilities/DZConcurrencyUtilities.swift | 32 +++++++++++++++++++ 7 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 Sources/DZNetworking/Utilities/DZConcurrencyUtilities.swift diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/DZNetworking.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/DZNetworking.xcscheme index b440d33..f2702d1 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/DZNetworking.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/DZNetworking.xcscheme @@ -1,6 +1,6 @@ Void -public typealias ErrorCallback = (_ error: Error) -> Void +public typealias SuccessCallback = @MainActor (_ responseObject: Any, _ response: HTTPURLResponse) -> Void +public typealias ErrorCallback = @MainActor (_ error: Error) -> Void /// HTTP Methods as a convinience enum public enum HTTPMethod: String, Equatable, Identifiable, Hashable { @@ -124,9 +124,11 @@ extension DZURLSession { /// - onError: called when the request completes successfully. Always called on the main-thread. /// - Returns: Task @discardableResult public func POST(_ uri: String, query: [String: String] = [:], json: Any?, onSuccess: @escaping SuccessCallback, onError: ErrorCallback?) -> _Concurrency.Task { - Task { + let boxed = SendableBox(json) + + return Task { do { - let (responseObject, response) = try await request(with: uri, method: "POST", query: query, body: json) + let (responseObject, response) = try await request(with: uri, method: "POST", query: query, body: boxed.get()) DispatchQueue.main.async { onSuccess(responseObject, response) } } catch { @@ -144,9 +146,11 @@ extension DZURLSession { /// - onError: called when the request completes successfully. Always called on the main-thread. /// - Returns: Task @discardableResult public func PUT(_ uri: String, query: [String: String] = [:], json: Any?, onSuccess: @escaping SuccessCallback, onError: ErrorCallback?) -> _Concurrency.Task { - Task { + let boxed = SendableBox(json) + + return Task { do { - let (responseObject, response) = try await request(with: uri, method: "PUT", query: query, body: json) + let (responseObject, response) = try await request(with: uri, method: "PUT", query: query, body: boxed.get()) DispatchQueue.main.async { onSuccess(responseObject, response) } } catch { @@ -164,9 +168,11 @@ extension DZURLSession { /// - onError: called when the request completes successfully. Always called on the main-thread. /// - Returns: Task @discardableResult public func PATCH(_ uri: String, query: [String: String] = [:], json: Any?, onSuccess: @escaping SuccessCallback, onError: ErrorCallback?) -> _Concurrency.Task { - Task { + let boxed = SendableBox(json) + + return Task { do { - let (responseObject, response) = try await request(with: uri, method: "PATCH", query: query, body: json) + let (responseObject, response) = try await request(with: uri, method: "PATCH", query: query, body: boxed.get()) DispatchQueue.main.async { onSuccess(responseObject, response) } } catch { @@ -184,9 +190,11 @@ extension DZURLSession { /// - onError: called when the request completes successfully. Always called on the main-thread. /// - Returns: Task @discardableResult public func DELETE(_ uri: String, query: [String: String] = [:], body: Any?, onSuccess: @escaping SuccessCallback, onError: ErrorCallback?) -> _Concurrency.Task { - Task { + let boxed = SendableBox(body) + + return Task { do { - let (responseObject, response) = try await request(with: uri, method: "DELETE", query: query, body: body) + let (responseObject, response) = try await request(with: uri, method: "DELETE", query: query, body: boxed.get()) DispatchQueue.main.async { onSuccess(responseObject, response) } } catch { diff --git a/Sources/DZNetworking/Core/DZURLSession.swift b/Sources/DZNetworking/Core/DZURLSession.swift index f6e48ba..82d10f6 100644 --- a/Sources/DZNetworking/Core/DZURLSession.swift +++ b/Sources/DZNetworking/Core/DZURLSession.swift @@ -24,7 +24,7 @@ public typealias RedirectModifierBlock = (_ task: URLSessionTask, _ request: URL /// let (data, _) = try await session.GET("/posts", query: ["userID": "1"]) /// ``` /// -open class DZURLSession: NSObject { +open class DZURLSession: NSObject, @unchecked Sendable { // MARK: Public /// shared instance, configured with `URLSessionConfiguration.default` and a privately managed operation queue diff --git a/Sources/DZNetworking/Core/DZUploadSession.swift b/Sources/DZNetworking/Core/DZUploadSession.swift index 82c9714..5a0bdbe 100644 --- a/Sources/DZNetworking/Core/DZUploadSession.swift +++ b/Sources/DZNetworking/Core/DZUploadSession.swift @@ -24,11 +24,11 @@ import Foundation /// ``` /// public final class DZUploadSession: NSObject { - static public let shared = DZUploadSession() + nonisolated(unsafe) static public let shared = DZUploadSession() public let session = DZURLSession() - public func upload(file: URL, fieldName: String, uri: String, query: [String: String]?, parameters: [String: String]? = nil, contentType: String = "application/octet-stream") async throws -> (Any?, HTTPURLResponse) { + public func upload(file: URL, fieldName: String, uri: String, query: [String: String]?, parameters: [String: String]? = nil, contentType: String = "application/octet-stream") async throws -> (Data?, HTTPURLResponse) { let multiPartData = MultipartFormData() @@ -53,6 +53,10 @@ public final class DZUploadSession: NSObject { let (result, response) = try await session.request(with: uri, method: HTTPMethod.POST.rawValue, query: query, body: multiPartData) + guard let result = result as? Data else { + throw dzError(code: 500, description: NSLocalizedString("Invalid Response", comment: ""), failure: nil) + } + return (result, response) } diff --git a/Sources/DZNetworking/Core/TaskHandler.swift b/Sources/DZNetworking/Core/TaskHandler.swift index 034146e..74a3582 100644 --- a/Sources/DZNetworking/Core/TaskHandler.swift +++ b/Sources/DZNetworking/Core/TaskHandler.swift @@ -39,10 +39,10 @@ internal final class TaskHandlersDictionary { /// TaskHandler manages delegate calls from the URLSession and dispatching of success/failure callbacks as tasks are completed. /// /// This is internally handled by the `DZURLSession` class and shouldn't be required to use directly unless you're subclassing `DZURLSession`. -public final class TaskHandler: NSObject { +public final class TaskHandler: NSObject, @unchecked Sendable { var completionHandler: (() -> Void)? - var handlers = TaskHandlersDictionary() + var handlers = TaskHandlersDictionary() } // MARK: - URLSessionDelegate @@ -74,7 +74,6 @@ extension TaskHandler: URLSessionTaskDelegate { // MARK: - URLSessionDataTaskDelegate extension TaskHandler: URLSessionDataDelegate { - public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) async -> URLSession.ResponseDisposition { return .allow } @@ -104,7 +103,6 @@ extension TaskHandler: URLSessionDataDelegate { handlers[downloadTask] = handler handlers[dataTask] = nil } - } // MARK: - URLSessionDownloadDelegate diff --git a/Sources/DZNetworking/Utilities/DZConcurrencyUtilities.swift b/Sources/DZNetworking/Utilities/DZConcurrencyUtilities.swift new file mode 100644 index 0000000..cc8724b --- /dev/null +++ b/Sources/DZNetworking/Utilities/DZConcurrencyUtilities.swift @@ -0,0 +1,32 @@ +// +// DZConcurrencyUtilities.swift +// Networking +// +// Created by Nikhil Nigade on 09/07/24. +// + +import Foundation + +internal struct SendableBox: @unchecked Sendable { + let wrappedValue: T + + init(_ wrappedValue: T) { + self.wrappedValue = wrappedValue + } + + func get() -> T { + wrappedValue + } +} + +internal struct SendableHashableBox: @unchecked Sendable { + let wrappedValue: T + + init(_ wrappedValue: T) { + self.wrappedValue = wrappedValue + } + + func get() -> T { + wrappedValue + } +}