Skip to content

Commit

Permalink
refator: Content-type,MultipartForm String값 상수로 정의 #6
Browse files Browse the repository at this point in the history
- MultiPartForm 프로토콜을MultiPartFormProtocol로 변경
- MultipartForm, contentType enum 구현 및 관련 코드 수정
- 오버로딩된 request 함수명 명확하게 개선
- 들여쓰기 컨벤션 개선
  • Loading branch information
llghdud921 committed Jan 6, 2022
1 parent 7bd46d8 commit 513ae69
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 38 deletions.
16 changes: 12 additions & 4 deletions OpenMarket/OpenMarket.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
9A1CFDCA27841CD0003BEC69 /* ProductSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1CFDC927841CD0003BEC69 /* ProductSecret.swift */; };
9A1CFDCC27842755003BEC69 /* ProductModification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1CFDCB27842755003BEC69 /* ProductModification.swift */; };
9A1CFDCE27842E2E003BEC69 /* Currency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1CFDCD27842E2E003BEC69 /* Currency.swift */; };
9A1CFDD1278469D5003BEC69 /* MultipartForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1CFDD0278469D5003BEC69 /* MultipartForm.swift */; };
9A1CFDD1278469D5003BEC69 /* MultipartFormProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1CFDD0278469D5003BEC69 /* MultipartFormProtocol.swift */; };
9A1CFDD3278526B8003BEC69 /* NetworkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1CFDD2278526B8003BEC69 /* NetworkTests.swift */; };
9A1CFDD527852721003BEC69 /* MockSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1CFDD427852721003BEC69 /* MockSession.swift */; };
9AAA3EAE2785726A00D6E407 /* StubProducts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AAA3EAD2785726A00D6E407 /* StubProducts.swift */; };
Expand All @@ -40,6 +40,8 @@
F35B1A8B2785386B0031A755 /* StubParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F35B1A8A2785386B0031A755 /* StubParser.swift */; };
F35B1A8F27853DE60031A755 /* NetworkManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F35B1A8E27853DE60031A755 /* NetworkManagerTests.swift */; };
F39BD72A2782DFA000D4B32D /* Products.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39BD7292782DFA000D4B32D /* Products.swift */; };
F3D3B5AD2787329600943720 /* MultipartForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3D3B5AC2787329600943720 /* MultipartForm.swift */; };
F3D3B5AF278732C700943720 /* ContentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3D3B5AE278732C700943720 /* ContentType.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -62,7 +64,7 @@
9A1CFDC927841CD0003BEC69 /* ProductSecret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductSecret.swift; sourceTree = "<group>"; };
9A1CFDCB27842755003BEC69 /* ProductModification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductModification.swift; sourceTree = "<group>"; };
9A1CFDCD27842E2E003BEC69 /* Currency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Currency.swift; sourceTree = "<group>"; };
9A1CFDD0278469D5003BEC69 /* MultipartForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipartForm.swift; sourceTree = "<group>"; };
9A1CFDD0278469D5003BEC69 /* MultipartFormProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipartFormProtocol.swift; sourceTree = "<group>"; };
9A1CFDD2278526B8003BEC69 /* NetworkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkTests.swift; sourceTree = "<group>"; };
9A1CFDD427852721003BEC69 /* MockSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MockSession.swift; path = "Test Doubles/MockSession.swift"; sourceTree = "<group>"; };
9AAA3EAD2785726A00D6E407 /* StubProducts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = StubProducts.swift; path = "Test Doubles/StubProducts.swift"; sourceTree = "<group>"; };
Expand All @@ -89,6 +91,8 @@
F35B1A8A2785386B0031A755 /* StubParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = StubParser.swift; path = "Test Doubles/StubParser.swift"; sourceTree = "<group>"; };
F35B1A8E27853DE60031A755 /* NetworkManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManagerTests.swift; sourceTree = "<group>"; };
F39BD7292782DFA000D4B32D /* Products.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Products.swift; sourceTree = "<group>"; };
F3D3B5AC2787329600943720 /* MultipartForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipartForm.swift; sourceTree = "<group>"; };
F3D3B5AE278732C700943720 /* ContentType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentType.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -112,7 +116,7 @@
9A1CFDCF278469A6003BEC69 /* Protocol */ = {
isa = PBXGroup;
children = (
9A1CFDD0278469D5003BEC69 /* MultipartForm.swift */,
9A1CFDD0278469D5003BEC69 /* MultipartFormProtocol.swift */,
F35B1A86278537BD0031A755 /* Networkable.swift */,
F35B1A882785380D0031A755 /* Parserable.swift */,
);
Expand Down Expand Up @@ -240,6 +244,8 @@
9A1CFDC72783EE09003BEC69 /* Address.swift */,
F35B1A6727840CA80031A755 /* NetworkManager.swift */,
9AAA3F352786B92400D6E407 /* HTTPMethod.swift */,
F3D3B5AC2787329600943720 /* MultipartForm.swift */,
F3D3B5AE278732C700943720 /* ContentType.swift */,
);
path = Network;
sourceTree = "<group>";
Expand Down Expand Up @@ -389,8 +395,10 @@
F39BD72A2782DFA000D4B32D /* Products.swift in Sources */,
F35B1A73278442F40031A755 /* ImageFile.swift in Sources */,
9A1CFDC82783EE09003BEC69 /* Address.swift in Sources */,
9A1CFDD1278469D5003BEC69 /* MultipartForm.swift in Sources */,
F3D3B5AD2787329600943720 /* MultipartForm.swift in Sources */,
9A1CFDD1278469D5003BEC69 /* MultipartFormProtocol.swift in Sources */,
F35B1A892785380D0031A755 /* Parserable.swift in Sources */,
F3D3B5AF278732C700943720 /* ContentType.swift in Sources */,
9A1CFD7A2782E683003BEC69 /* Parser.swift in Sources */,
9A1CFDC62783DD63003BEC69 /* Vendors.swift in Sources */,
9A1CFD7C2782EB88003BEC69 /* ParserError.swift in Sources */,
Expand Down
25 changes: 25 additions & 0 deletions OpenMarket/OpenMarket/Model/Network/ContentType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// ContentType.swift
// OpenMarket
//
// Created by 이호영 on 2022/01/06.
//

import Foundation

enum ContentType {
case contentType
case json
case formData(boundary: String)

var string: String {
switch self {
case .contentType:
return "Content-Type"
case .json:
return "application/json"
case .formData(let boundary):
return "multipart/form-data; boundary=\"\(boundary)\""
}
}
}
34 changes: 34 additions & 0 deletions OpenMarket/OpenMarket/Model/Network/MultipartForm.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// MultipartForm.swift
// OpenMarket
//
// Created by 이호영 on 2022/01/06.
//

import Foundation

enum MultipartForm {
case boundary(baseBoundary: String)
case contentDisposition(name: String)
case value(_ value: Any)
case imageContentDisposition(filename: String)
case imageContentType(imageType: String)
case imageValue(data: Data)

var string: String {
switch self {
case .boundary(let baseBoundary):
return "--\(baseBoundary)\r\n"
case .contentDisposition(let name):
return "Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n"
case .value(let value):
return "\(value)\r\n"
case .imageContentDisposition(let filename):
return "Content-Disposition: form-data; name=\"images[]\"; filename=\"\(filename)\"\r\n"
case .imageContentType(let imageType):
return "Content-Type: \(imageType)\r\n\r\n"
case .imageValue(let data):
return "\(data)\r\n"
}
}
}
57 changes: 31 additions & 26 deletions OpenMarket/OpenMarket/Model/Network/NetworkManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ struct NetworkManager {
self.parser = parser
}

func fetch<T: Decodable>(request: URLRequest,
decodingType: T.Type,
completion: @escaping (Result<T, Error>) -> Void) {

func fetch<T: Decodable>(
request: URLRequest,
decodingType: T.Type,
completion: @escaping (Result<T, Error>) -> Void
) {
network.execute(request: request) { result in
switch result {
case .success(let data):
Expand All @@ -44,23 +45,23 @@ struct NetworkManager {
}

// GET - 상품 리스트 조회
func request(page: UInt, itemsPerPage: UInt) -> URLRequest? {
func requestListSearch(page: UInt, itemsPerPage: UInt) -> URLRequest? {
guard let url = Address.products(page: page, itemsPerPage: itemsPerPage).url else {
return nil
}
return URLRequest(url: url)
}

// GET - 상품 상세 조회
func request(id: UInt) -> URLRequest? {
func requestDetailSearch(id: UInt) -> URLRequest? {
guard let url = Address.product(id: id).url else {
return nil
}
return URLRequest(url: url)
}

// POST - 상품 삭제 Secret 상세 조회
func request<T: Encodable>(data: T, id: UInt, secret: String) -> Result<URLRequest?, Error> {
func requestSecretSearch<T: Encodable>(data: T, id: UInt, secret: String) -> Result<URLRequest?, Error> {
guard let url = Address.secret(id: id, secret: secret).url else {
return .failure(NetworkError.notFoundURL)
}
Expand All @@ -84,7 +85,7 @@ struct NetworkManager {
}

// DELET - 상품 삭제
func request(id: UInt, secret: String) -> URLRequest? {
func requestDelete(id: UInt, secret: String) -> URLRequest? {
guard let url = Address.delete(id: id, secret: secret).url else {
return nil
}
Expand All @@ -97,7 +98,7 @@ struct NetworkManager {
}

// PATCH - 상품 수정
func request<T: Encodable>(data: T, id: UInt) -> Result<URLRequest?, Error> {
func requestModify<T: Encodable>(data: T, id: UInt) -> Result<URLRequest?, Error> {
guard let url = Address.product(id: id).url else {
return .failure(NetworkError.notFoundURL)
}
Expand All @@ -114,31 +115,36 @@ struct NetworkManager {

request.httpMethod = HTTPMethod.patch.rawValue
request.httpBody = encodeData
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue(ContentType.json.string, forHTTPHeaderField: ContentType.contentType.string)
request.addValue("80c47530-58bb-11ec-bf7f-d188f1cd5f22", forHTTPHeaderField: "identifier")

return .success(request)
}

// POST - 상품 등록
func request<T: MultipartForm>(params: T, images: [ImageFile]) -> Result<URLRequest?, Error> {
func requestRegister<T: MultipartFormProtocol>(params: T, images: [ImageFile]) -> Result<URLRequest?, Error> {
guard let url = Address.register.url else {
return .failure(NetworkError.notFoundURL)
}
let request = multipartFormRequest(url: url, params: params, images: images)
let request = requestMultipartForm(url: url, params: params, images: images)
return .success(request)
}

}

extension NetworkManager {
private func multipartFormRequest<T: MultipartForm>(url: URL, params: T, images: [ImageFile]) -> URLRequest {
private func requestMultipartForm<T: MultipartFormProtocol>(
url: URL,
params: T,
images: [ImageFile]
) -> URLRequest {
let boundary = baseBoundary
let encodeBody = createBody(parameters: params.dictionary, images: images, boundary: boundary)
var request = URLRequest(url: url)

request.httpMethod = HTTPMethod.post.rawValue
request.setValue("multipart/form-data; boundary=\"\(boundary)\"", forHTTPHeaderField: "Content-Type")
request.setValue(ContentType.formData(boundary: boundary).string,
forHTTPHeaderField: ContentType.contentType.string)
request.addValue("80c47530-58bb-11ec-bf7f-d188f1cd5f22", forHTTPHeaderField: "identifier")
request.httpBody = encodeBody

Expand All @@ -149,32 +155,31 @@ extension NetworkManager {
var body = Data()
for (key, value) in parameters {
if let value = value {
body.append(convertedMultiPartForm(name: key, value: value, boundary: boundary))
body.append(jsonMultiPartForm(name: key, value: value, boundary: boundary))
} else {
continue
}
}
for image in images {
body.append(convertedMultiPartForm(image: image, boundary: boundary))
body.append(imageMultiPartForm(image: image, boundary: boundary))
}
return body
}

private func convertedMultiPartForm(name: String, value: Any, boundary: String) -> Data {
private func jsonMultiPartForm(name: String, value: Any, boundary: String) -> Data {
var data = Data()
data.append("--\(boundary)\r\n")
data.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n")
data.append("\(value)\r\n")
data.append(MultipartForm.boundary(baseBoundary: boundary).string)
data.append(MultipartForm.contentDisposition(name: name).string)
data.append(MultipartForm.value(value).string)
return data
}

private func convertedMultiPartForm(image: ImageFile, boundary: String) -> Data {
private func imageMultiPartForm(image: ImageFile, boundary: String) -> Data {
var data = Data()
data.append("--\(boundary)\r\n")
data.append("Content-Disposition: form-data; name=\"images[]\"; filename=\"\(image.name)\"\r\n")
data.append("Content-Type: \(image.type.description)\r\n\r\n")
data.append("\(image.data)\r\n")
data.append(MultipartForm.boundary(baseBoundary: boundary).string)
data.append(MultipartForm.imageContentDisposition(filename: image.name).string)
data.append(MultipartForm.imageContentType(imageType: image.type.description).string)
data.append(MultipartForm.imageValue(data: image.data).string)
return data
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

struct ProductRegistration: Codable, MultipartForm {
struct ProductRegistration: Codable, MultipartFormProtocol {
let name: String
let description: String
let price: Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@

import Foundation

protocol MultipartForm {
protocol MultipartFormProtocol {
var dictionary: [String: Any?] { get }
}
12 changes: 6 additions & 6 deletions OpenMarket/OpenMarketTests/NetworkManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class NetworkManagerTests: XCTestCase {
let url = Address.products(page: 1, itemsPerPage: 10).url

//when
let result = sutNetworkManager?.request(page: 1, itemsPerPage: 10)
let result = sutNetworkManager?.requestListSearch(page: 1, itemsPerPage: 10)

//then
XCTAssertNotNil(result)
Expand All @@ -117,7 +117,7 @@ class NetworkManagerTests: XCTestCase {
let url = Address.product(id: 1).url

//when
let result = sutNetworkManager?.request(id: 1)
let result = sutNetworkManager?.requestDetailSearch(id: 1)

//then
XCTAssertNotNil(result)
Expand All @@ -130,7 +130,7 @@ class NetworkManagerTests: XCTestCase {
let url = Address.secret(id: 1, secret: "123").url

//when
let result = sutNetworkManager?.request(data: Data(), id: 1, secret: "123")
let result = sutNetworkManager?.requestSecretSearch(data: Data(), id: 1, secret: "123")

switch result {
case .success(let request):
Expand All @@ -150,7 +150,7 @@ class NetworkManagerTests: XCTestCase {
let url = Address.delete(id: 1, secret: "123").url

//when
let result = sutNetworkManager?.request(id: 1, secret: "123")
let result = sutNetworkManager?.requestDelete(id: 1, secret: "123")

//then
XCTAssertNotNil(result)
Expand All @@ -163,7 +163,7 @@ class NetworkManagerTests: XCTestCase {
let url = Address.product(id: 1).url

//when
let result = sutNetworkManager?.request(data: Data(), id: 1)
let result = sutNetworkManager?.requestModify(data: Data(), id: 1)

switch result {
case .success(let request):
Expand All @@ -184,7 +184,7 @@ class NetworkManagerTests: XCTestCase {
let params = ProductRegistration(name: "", description: "", price: 1, currency: .krw, discountedPrice: nil, stock: nil, secret: "")

//when
let result = sutNetworkManager?.request(params: params, images: [])
let result = sutNetworkManager?.requestRegister(params: params, images: [])

switch result {
case .success(let request):
Expand Down

0 comments on commit 513ae69

Please sign in to comment.