Network를 Framework처럼 구현해보기

Groot·2022년 8월 24일
0

TIL

목록 보기
50/153
post-thumbnail
post-custom-banner

TIL

🌱 난 오늘 무엇을 공부했을까?

📌 Network를 Framework처럼 구현해보기

  • 이번 오픈마켓 프로젝트를 하면서 네트워크 구현을 여러가지 방법으로 고쳐봤는데, 리뷰어의 요청에 맞게 누구나 가져다 쓸 수 있도록 프레임워크 같이 만들어보았다.
import Foundation
import UIKit.UIImage

enum HTTPMethod {
    case get
    case post
    case delete
    case patch
    
    var name: String {
        switch self {
        case .get:
            return "GET"
        case .post:
            return "POST"
        case .delete:
            return "DELETE"
        case .patch:
            return "PATCH"
        }
    }
}

enum HTTPBody {
    case multiPartForm(_ form: MultiPartForm)
    case json(_ json: Data)
}

protocol APIRequest {
    var baseURL: String { get }
    
    var path: String? { get }
    
    var method: HTTPMethod { get }
    
    var headers: [String: String]? { get }
    
    var query: [String: String]? { get }
    
    var body: HTTPBody? { get }
}

extension APIRequest {
    var url: URL? {
        var component = URLComponents(string: self.baseURL + (self.path ?? ""))
        component?.queryItems = query?.reduce([URLQueryItem]())
        {
            $0 + [URLQueryItem(name: $1.key,
                               value: $1.value)]
        }
        
        return component?.url
    }
    
    var urlRequest: URLRequest? {
        guard let url = url else { return nil }
        
        var request = URLRequest(url: url)
        request.httpMethod = method.name
        request.httpBody = createHTTPBody()
        
        self.headers?.forEach
        {
            request.addValue($0.value,
                           forHTTPHeaderField: $0.key)
            
        }
        
        return request
    }
}

//MARK: - MultiPartForm Type

struct MultiPartForm {
    let jsonParameterName: String
    let imageParameterName: String
    let boundary: String
    let jsonData: Data
    let images: [Image]
}

//MARK: - MultiPartForm_Image Type

struct Image {
    let name: String
    let data: Data
    let type: String
}

//MARK: - MultiPartForm method

extension APIRequest {
    private func createHTTPBody() -> Data {
        guard let body = self.body else { return Data() }
        
        switch body {
        case .json(let json):
            return json
        case .multiPartForm(let form):
            return createMultiPartFormBody(form: form)
        }
    }
    
    private func createMultiPartFormBody(form: MultiPartForm) -> Data {
        let lineBreak = "\r\n"
        var requestBody = Data()
        
        requestBody.append(createMultipartFormJsonData(parameterName: form.jsonParameterName,
                                                       boundary: form.boundary,
                                                       json: form.jsonData))
        
        form.images.forEach
        {
            requestBody.append(createMultipartFormImageData(parameterName: form.imageParameterName,
                                                            boundary: form.boundary,
                                                            image: $0))
        }
        
        requestBody.append("\(lineBreak)--\(form.boundary)--\(lineBreak)")
        
        return requestBody
    }
    
    private func createMultipartFormJsonData(parameterName: String, boundary: String, json: Data) -> Data {
        let lineBreak = "\r\n"
        var paramsBody = Data()
        
        paramsBody.append("\(lineBreak)--\(boundary + lineBreak)")
        paramsBody.append("Content-Disposition: form-data; name=\"\(parameterName)\"\(lineBreak)")
        paramsBody.append("Content-Type: application/json \(lineBreak + lineBreak)")
        paramsBody.append(json)
        
        return paramsBody
    }
    
    private func createMultipartFormImageData(parameterName: String, boundary: String, image: Image) -> Data {
        let lineBreak = "\r\n"
        let fileName = image.name + "." + image.type
        let fileType = "image/\(image.type)"
        var imageBody = Data()
        
        imageBody.append("\(lineBreak)--\(boundary + lineBreak)")
        imageBody.append("Content-Disposition: form-data; name=\"\(parameterName)\"; filename=\"\(fileName)\"\(lineBreak)")
        imageBody.append("Content-Type: \(fileType) \(lineBreak + lineBreak)")
        imageBody.append(image.data)
        
        return imageBody
    }
}

결론 : 멀었다.

profile
I Am Groot
post-custom-banner

0개의 댓글