Skip to content

Commit

Permalink
Merge pull request #5 from stealmh/add-upload
Browse files Browse the repository at this point in the history
Add upload
  • Loading branch information
stealmh authored Jan 3, 2024
2 parents 669deb4 + feeac10 commit 76f3d98
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 6 deletions.
128 changes: 122 additions & 6 deletions Sources/CuteNetwork/Cute.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
//
// Cute.swift
//
//
//
// Created by mino on 2023/11/29.
//

import Foundation
import UIKit

open class Cute<EndPoint: EndPointType>: NSObject, NetworkRouter, URLSessionDelegate {
/// Properties
Expand Down Expand Up @@ -67,7 +68,7 @@ open class Cute<EndPoint: EndPointType>: NSObject, NetworkRouter, URLSessionDele
}
let session = URLSession(configuration: configuration)
do {
let request = try self.buildRequest(from: route)
let request = try self.buildRequest(from: route, boundary: nil)
if logAccess { NetworkLogger.log(request: request) }
task = session.dataTask(with: request, completionHandler: { data, response, error in
completion(data, response, error)
Expand All @@ -82,10 +83,119 @@ open class Cute<EndPoint: EndPointType>: NSObject, NetworkRouter, URLSessionDele
self.task?.cancel()
}
}
//MARK: - Cute + Upload
extension Cute {
/// - petitUpload(_ root: EndPoint, image: UIImage, fileName: String, imageType: String, petitLogVisible: Bool) async throws -> Data 함수를 래핑
open func petitUpload<T: Decodable>(_ root: EndPoint, imageType: ImageInfomation, petitLogVisible: Bool = true) async throws -> T {
do {
let result = try await petitUpload(root,
imageType: imageType,
petitLogVisible: petitLogVisible)

let decoder = JSONDecoder()
let data = try decoder.decode(T.self, from: result)

return data
} catch {
/// [1] `result` error handling
if let networkError = error as? NetworkError { throw networkError }
/// [2] `Decode fail` error handling
else { throw NetworkError.parsingError }
}
}
/// - petitUpload(_ route: EndPoint, image: UIImage, fileName: String, imageType: String, logAccess: Bool, completion: @escaping NetworkRouterCompletion) 함수를 래핑
open func petitUpload(_ root: EndPoint, imageType: ImageInfomation, petitLogVisible: Bool) async throws -> Data {
return try await withCheckedThrowingContinuation({ value in
petitUpload(root,
imageType: imageType,
logAccess: petitLogVisible) { data, response, error in
if let error {
value.resume(throwing: error as? NetworkError ?? NetworkError.custom(message: error.localizedDescription))
}

if let response = response as? HTTPURLResponse {
let result = ResponseHandler.handleNetworkResponse(response)
switch result {
case .success:
guard let data else {
value.resume(throwing: NetworkError.custom(message: "데이터를 받지 못했습니다."))
return
}
value.resume(returning: data)
case .failure(let message):
guard let _ = data else {
value.resume(throwing: NetworkError.custom(message: message))
return
}
}
}
}
})
}
/// dataTask를 걸친 데이터 까지 넘겨주는 역할
open func petitUpload(_ route: EndPoint, imageType: ImageInfomation, logAccess: Bool, completion: @escaping NetworkRouterCompletion) {
guard Reachability.isConnectedToNetwork() else {
completion(nil, nil, NetworkError.noConnectionToInternet)
return
}

let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForResource = 60
if #available(iOS 11, *) {
configuration.waitsForConnectivity = true
}

let session = URLSession(configuration: configuration)

do {
let boundary = "Boundary-\(UUID().uuidString)"
let request = try self.buildRequest(from: route, boundary: boundary)
let multipartBody = createMultipartBody(boundary, imageType.image, imageType.imageName, imageType.imageType)
if logAccess { NetworkLogger.log(request: request) }

task = session.uploadTask(with: request, from: multipartBody, completionHandler: { data, response, error in
completion(data, response, error)
})

} catch {
completion(nil, nil, error)
}

self.task?.resume()
}

// 이미지를 업로드하는 Multipart Form Data 요청 생성
fileprivate func buildMultipartRequest(_ boundary: String, from route: EndPoint) throws -> URLRequest {
var request = URLRequest(url: route.baseURL)
request.httpMethod = route.httpMethod.rawValue
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
if let header = route.headers {
for (key, value) in header {
request.addValue(value, forHTTPHeaderField: key)
}
}
return request
}

fileprivate func createMultipartBody(_ boundary: String, _ image: UIImage, _ fileName: String, _ imageType: String) -> Data {
let boundaryPrefix = "--\(boundary)\r\n"
var uploadData = Data()
if let data = image.jpegData(compressionQuality: 0.8) {
uploadData.append(boundaryPrefix.data(using: .utf8)!)
uploadData.append("Content-Disposition: form-data; name=\"img\"; filename=\"\(fileName).\(imageType)\"\r\n".data(using: .utf8)!)
uploadData.append("Content-Type: image/\(imageType)\r\n\r\n".data(using: .utf8)!)
uploadData.append(data)
uploadData.append("\r\n".data(using: .utf8)!)
uploadData.append("--\(boundary)--".data(using: .utf8)!)
} // adjust compression quality as needed
return uploadData
}

}
//MARK: - Cute extension(fileprivate)
/// Only `Cute` Class used.
fileprivate extension Cute {
func buildRequest(from route: EndPoint) throws -> URLRequest {
func buildRequest(from route: EndPoint, boundary: String?) throws -> URLRequest {
var request = URLRequest(url: route.baseURL.appendingPathComponent(route.path))
request.httpMethod = route.httpMethod.rawValue
do {
Expand All @@ -112,6 +222,11 @@ fileprivate extension Cute {
bodyEncoding: bodyEncoding,
urlParameters: urlParameters,
request: &request)
case .upload:
if let boundary {
let request = try buildMultipartRequest(boundary, from: route)
return request
}
}
return request
} catch {
Expand All @@ -120,9 +235,9 @@ fileprivate extension Cute {
}

func configureParameters(bodyParameters: Parameters?,
bodyEncoding: ParameterEncoding,
urlParameters: Parameters?,
request: inout URLRequest) throws {
bodyEncoding: ParameterEncoding,
urlParameters: Parameters?,
request: inout URLRequest) throws {
do {
try bodyEncoding.encode(urlRequest: &request,
bodyParameters: bodyParameters, urlParameters: urlParameters)
Expand All @@ -136,3 +251,4 @@ fileprivate extension Cute {
headers.forEach { request.setValue($1, forHTTPHeaderField: $0) }
}
}

1 change: 1 addition & 0 deletions Sources/CuteNetwork/HTTPTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ public enum HTTPTask {
bodyEncoding: ParameterEncoding,
urlParameters: Parameters?,
additionHeaders: HTTPHeaders?)
case upload
}
14 changes: 14 additions & 0 deletions Sources/CuteNetwork/ImageInfomation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// ImageInfomation.swift
//
//
// Created by mino on 2024/01/03.
//

import UIKit

public struct ImageInfomation {
let imageName: String
let imageType: String
let image: UIImage
}

0 comments on commit 76f3d98

Please sign in to comment.