diff --git a/Sources/SupabaseStorage/StorageApi.swift b/Sources/SupabaseStorage/StorageApi.swift index ea782ad..775e41b 100644 --- a/Sources/SupabaseStorage/StorageApi.swift +++ b/Sources/SupabaseStorage/StorageApi.swift @@ -7,13 +7,12 @@ import Foundation public class StorageApi { var url: String var headers: [String: String] - var http: StorageHTTPClient + var session: StorageHTTPSession - init(url: String, headers: [String: String], http: StorageHTTPClient) { + init(url: String, headers: [String: String], session: StorageHTTPSession) { self.url = url self.headers = headers - self.http = http - // self.headers.merge(["Content-Type": "application/json"]) { $1 } + self.session = session } internal enum HTTPMethod: String { @@ -49,14 +48,18 @@ public class StorageApi { request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: []) } - let (data, response) = try await http.fetch(request) - if let mimeType = response.mimeType { + let (data, response) = try await session.fetch(request) + guard let httpResonse = response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } + + if let mimeType = httpResonse.mimeType { switch mimeType { case "application/json": let json = try JSONSerialization.jsonObject(with: data, options: []) - return try parse(response: json, statusCode: response.statusCode) + return try parse(response: json, statusCode: httpResonse.statusCode) default: - return try parse(response: data, statusCode: response.statusCode) + return try parse(response: data, statusCode: httpResonse.statusCode) } } else { throw StorageError(message: "failed to get response") @@ -89,11 +92,14 @@ public class StorageApi { request.setValue(formData.contentType, forHTTPHeaderField: "Content-Type") - let (data, response) = try await http.upload(request, from: formData.data) + let (data, response) = try await session.upload(request, formData.data) + guard let httpResonse = response as? HTTPURLResponse else { + throw URLError(.badServerResponse) + } if jsonSerialization { let json = try JSONSerialization.jsonObject(with: data, options: []) - return try parse(response: json, statusCode: response.statusCode) + return try parse(response: json, statusCode: httpResonse.statusCode) } if let dataString = String(data: data, encoding: .utf8) { diff --git a/Sources/SupabaseStorage/StorageBucketApi.swift b/Sources/SupabaseStorage/StorageBucketApi.swift index 2e6c61d..46e27c8 100644 --- a/Sources/SupabaseStorage/StorageBucketApi.swift +++ b/Sources/SupabaseStorage/StorageBucketApi.swift @@ -10,8 +10,8 @@ public class StorageBucketApi: StorageApi { /// - Parameters: /// - url: Storage HTTP URL /// - headers: HTTP headers. - override init(url: String, headers: [String: String], http: StorageHTTPClient) { - super.init(url: url, headers: headers, http: http) + override init(url: String, headers: [String: String], session: StorageHTTPSession) { + super.init(url: url, headers: headers, session: session) self.headers.merge(["Content-Type": "application/json"]) { $1 } } diff --git a/Sources/SupabaseStorage/StorageFileApi.swift b/Sources/SupabaseStorage/StorageFileApi.swift index 665fabf..1e09f83 100644 --- a/Sources/SupabaseStorage/StorageFileApi.swift +++ b/Sources/SupabaseStorage/StorageFileApi.swift @@ -23,9 +23,9 @@ public class StorageFileApi: StorageApi { /// - url: Storage HTTP URL /// - headers: HTTP headers. /// - bucketId: The bucket id to operate on. - init(url: String, headers: [String: String], bucketId: String, http: StorageHTTPClient) { + init(url: String, headers: [String: String], bucketId: String, session: StorageHTTPSession) { self.bucketId = bucketId - super.init(url: url, headers: headers, http: http) + super.init(url: url, headers: headers, session: session) } /// Uploads a file to an existing bucket. diff --git a/Sources/SupabaseStorage/StorageHTTPClient.swift b/Sources/SupabaseStorage/StorageHTTPClient.swift index 91463c8..286e313 100644 --- a/Sources/SupabaseStorage/StorageHTTPClient.swift +++ b/Sources/SupabaseStorage/StorageHTTPClient.swift @@ -4,59 +4,25 @@ import Foundation import FoundationNetworking #endif -public protocol StorageHTTPClient { - func fetch(_ request: URLRequest) async throws -> (Data, HTTPURLResponse) - func upload(_ request: URLRequest, from data: Data) async throws -> (Data, HTTPURLResponse) -} - -public struct DefaultStorageHTTPClient: StorageHTTPClient { - public init() {} - - public func fetch(_ request: URLRequest) async throws -> (Data, HTTPURLResponse) { - try await withCheckedThrowingContinuation { continuation in - let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in - if let error = error { - continuation.resume(throwing: error) - return - } - - guard - let data = data, - let httpResponse = response as? HTTPURLResponse - else { - continuation.resume(throwing: URLError(.badServerResponse)) - return - } - - continuation.resume(returning: (data, httpResponse)) - } - - dataTask.resume() - } +public struct StorageHTTPSession: Sendable { + public let fetch: @Sendable (_ request: URLRequest) async throws -> (Data, URLResponse) + public let upload: + @Sendable (_ request: URLRequest, _ data: Data) async throws -> (Data, URLResponse) + + public init( + fetch: @escaping @Sendable (_ request: URLRequest) async throws -> (Data, URLResponse), + upload: @escaping @Sendable (_ request: URLRequest, _ data: Data) async throws -> ( + Data, URLResponse + ) + ) { + self.fetch = fetch + self.upload = upload } - public func upload( - _ request: URLRequest, - from data: Data - ) async throws -> (Data, HTTPURLResponse) { - try await withCheckedThrowingContinuation { continuation in - let task = URLSession.shared.uploadTask(with: request, from: data) { data, response, error in - if let error = error { - continuation.resume(throwing: error) - return - } - - guard - let data = data, - let httpResponse = response as? HTTPURLResponse - else { - continuation.resume(throwing: URLError(.badServerResponse)) - return - } - - continuation.resume(returning: (data, httpResponse)) - } - task.resume() - } + public init() { + self.init( + fetch: { try await URLSession.shared.data(for: $0) }, + upload: { try await URLSession.shared.upload(for: $0, from: $1) } + ) } } diff --git a/Sources/SupabaseStorage/SupabaseStorage.swift b/Sources/SupabaseStorage/SupabaseStorage.swift index 177c97d..c9947e2 100644 --- a/Sources/SupabaseStorage/SupabaseStorage.swift +++ b/Sources/SupabaseStorage/SupabaseStorage.swift @@ -3,14 +3,16 @@ public class SupabaseStorageClient: StorageBucketApi { /// - Parameters: /// - url: Storage HTTP URL /// - headers: HTTP headers. - override public init(url: String, headers: [String: String], http: StorageHTTPClient? = nil) { - super.init(url: url, headers: headers, http: http ?? DefaultStorageHTTPClient()) + override public init( + url: String, headers: [String: String], session: StorageHTTPSession = .init() + ) { + super.init(url: url, headers: headers, session: session) } /// Perform file operation in a bucket. /// - Parameter id: The bucket id to operate on. /// - Returns: StorageFileApi object public func from(id: String) -> StorageFileApi { - StorageFileApi(url: url, headers: headers, bucketId: id, http: http) + StorageFileApi(url: url, headers: headers, bucketId: id, session: session) } }