From f43b06f1b86a422505a096c652afb7a27f62e059 Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Mon, 5 Feb 2024 13:26:49 +0900 Subject: [PATCH 01/14] =?UTF-8?q?=F0=9F=93=A6=EF=B8=8F=20ProductInfoAPI=20?= =?UTF-8?q?Package=20=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- APIService/Package.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/APIService/Package.swift b/APIService/Package.swift index cbe9406..dfd5c18 100644 --- a/APIService/Package.swift +++ b/APIService/Package.swift @@ -14,6 +14,10 @@ let package = Package( name: "HomeAPISupport", targets: ["HomeAPISupport"] ), + .library( + name: "ProductInfoAPI", + targets: ["ProductInfoAPI"] + ) ], dependencies: [ .package(path: "../Core"), @@ -34,5 +38,12 @@ let package = Package( ], resources: [.process("Mocks")] ), + .target( + name: "ProductInfoAPI", + dependencies: [ + .product(name: "Network", package: "Core"), + .product(name: "Entity", package: "Entity") + ] + ) ] ) From b2a125a1ef3e60d5979c06cbdd4dc4ada16e3de4 Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Mon, 5 Feb 2024 15:59:28 +0900 Subject: [PATCH 02/14] =?UTF-8?q?=E2=9C=A8=20ProductInfoEndPoint=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProductInfoAPI/ProductInfoEndPoint.swift | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift diff --git a/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift b/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift new file mode 100644 index 0000000..755e10b --- /dev/null +++ b/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift @@ -0,0 +1,37 @@ +// +// ProductInfoEndPoint.swift +// +// +// Created by 김응철 on 2/5/24. +// + +import Foundation +import Network + +public enum ProductInfoEndPoint { + case fetchProduct(Int) + case fetchPrices(Int) +} + +extension ProductInfoEndPoint: EndPoint { + public var method: Network.HTTPMethod { + .get + } + + public var path: String { + switch self { + case .fetchProduct(let productId): + "/v2/products/\(productId)" + case .fetchPrices(let productId): + "/v2/products/\(productId)/price-history" + } + } + + public var parameters: Network.HTTPParameter { + .plain + } + + public var headers: [String : String] { + ["Content-Type": "application/json"] + } +} From 8fcad2a0c79046523401316983e556fc604315c7 Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Mon, 5 Feb 2024 16:15:52 +0900 Subject: [PATCH 03/14] =?UTF-8?q?=F0=9F=8C=B1=20ProductInfoPriceResponse?= =?UTF-8?q?=20DTO=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Responses/ProductPriceResponse.swift | 26 +++++++++++++++++++ .../Sources/ProductInfoAPISupport/File.swift | 8 ++++++ 2 files changed, 34 insertions(+) create mode 100644 APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift create mode 100644 APIService/Sources/ProductInfoAPISupport/File.swift diff --git a/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift b/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift new file mode 100644 index 0000000..7c65c39 --- /dev/null +++ b/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift @@ -0,0 +1,26 @@ +// +// ProductPriceResponse.swift +// +// +// Created by 김응철 on 2/5/24. +// + +import Foundation + +public struct ProductPriceResponse: Decodable { + let date: String + let price: Int + + enum CodingKeys: CodingKey { + case date + case price + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + // TODO: 서버에서 내려주는 날짜 형식으로 바꾸기 + let dateString = try container.decode(Date.self, forKey: .date).formatted() + self.date = dateString + self.price = try container.decode(Int.self, forKey: .price) + } +} diff --git a/APIService/Sources/ProductInfoAPISupport/File.swift b/APIService/Sources/ProductInfoAPISupport/File.swift new file mode 100644 index 0000000..f6e090f --- /dev/null +++ b/APIService/Sources/ProductInfoAPISupport/File.swift @@ -0,0 +1,8 @@ +// +// File.swift +// +// +// Created by 김응철 on 2/5/24. +// + +import Foundation From 77936936d65c4e923d5358e801b433ccb0ff9cb4 Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Mon, 5 Feb 2024 16:38:48 +0900 Subject: [PATCH 04/14] =?UTF-8?q?=F0=9F=8C=B1=20Mock=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- APIService/Package.swift | 17 +++++++++++++--- .../ProductInfoAPI/ProductInfoEndPoint.swift | 20 +++++++++++-------- .../Responses/ProductPriceResponse.swift | 8 ++++---- .../Sources/ProductInfoAPISupport/File.swift | 8 -------- .../Mocks/ProductPriceResponse.json | 14 +++++++++++++ 5 files changed, 44 insertions(+), 23 deletions(-) delete mode 100644 APIService/Sources/ProductInfoAPISupport/File.swift create mode 100644 APIService/Sources/ProductInfoAPISupport/Mocks/ProductPriceResponse.json diff --git a/APIService/Package.swift b/APIService/Package.swift index dfd5c18..db73612 100644 --- a/APIService/Package.swift +++ b/APIService/Package.swift @@ -17,7 +17,11 @@ let package = Package( .library( name: "ProductInfoAPI", targets: ["ProductInfoAPI"] - ) + ), + .library( + name: "ProductInfoAPISupport", + targets: ["ProductInfoAPISupport"] + ), ], dependencies: [ .package(path: "../Core"), @@ -42,8 +46,15 @@ let package = Package( name: "ProductInfoAPI", dependencies: [ .product(name: "Network", package: "Core"), - .product(name: "Entity", package: "Entity") + .product(name: "Entity", package: "Entity"), ] - ) + ), + .target( + name: "ProductInfoAPISupport", + dependencies: [ + "ProductInfoAPI", + ], + resources: [.process("Mocks")] + ), ] ) diff --git a/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift b/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift index 755e10b..d9a946e 100644 --- a/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift +++ b/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift @@ -8,30 +8,34 @@ import Foundation import Network +// MARK: - ProductInfoEndPoint + public enum ProductInfoEndPoint { case fetchProduct(Int) case fetchPrices(Int) } +// MARK: EndPoint + extension ProductInfoEndPoint: EndPoint { public var method: Network.HTTPMethod { .get } - + public var path: String { switch self { - case .fetchProduct(let productId): - "/v2/products/\(productId)" - case .fetchPrices(let productId): - "/v2/products/\(productId)/price-history" + case .fetchProduct(let productID): + "/v2/products/\(productID)" + case .fetchPrices(let productID): + "/v2/products/\(productID)/price-history" } } - + public var parameters: Network.HTTPParameter { .plain } - - public var headers: [String : String] { + + public var headers: [String: String] { ["Content-Type": "application/json"] } } diff --git a/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift b/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift index 7c65c39..8e041ec 100644 --- a/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift +++ b/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift @@ -10,17 +10,17 @@ import Foundation public struct ProductPriceResponse: Decodable { let date: String let price: Int - + enum CodingKeys: CodingKey { case date case price } - + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // TODO: 서버에서 내려주는 날짜 형식으로 바꾸기 let dateString = try container.decode(Date.self, forKey: .date).formatted() - self.date = dateString - self.price = try container.decode(Int.self, forKey: .price) + date = dateString + price = try container.decode(Int.self, forKey: .price) } } diff --git a/APIService/Sources/ProductInfoAPISupport/File.swift b/APIService/Sources/ProductInfoAPISupport/File.swift deleted file mode 100644 index f6e090f..0000000 --- a/APIService/Sources/ProductInfoAPISupport/File.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// File.swift -// -// -// Created by 김응철 on 2/5/24. -// - -import Foundation diff --git a/APIService/Sources/ProductInfoAPISupport/Mocks/ProductPriceResponse.json b/APIService/Sources/ProductInfoAPISupport/Mocks/ProductPriceResponse.json new file mode 100644 index 0000000..de1c90f --- /dev/null +++ b/APIService/Sources/ProductInfoAPISupport/Mocks/ProductPriceResponse.json @@ -0,0 +1,14 @@ +[ + { + "date": "2023.12" + "price": 1100 +}, +{ + "date": "2024.01" + "price": 1200 +}, +{ + "date": "2024.02" + "price": 1250 +} + ] From 3d346bc856ac947a5cf3708c10c9307d100ca084 Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Mon, 5 Feb 2024 17:33:12 +0900 Subject: [PATCH 05/14] =?UTF-8?q?=F0=9F=8C=B1=20ProductInfoURLProtocol=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProductInfoURLProtocol.swift | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 APIService/Sources/ProductInfoAPISupport/ProductInfoURLProtocol.swift diff --git a/APIService/Sources/ProductInfoAPISupport/ProductInfoURLProtocol.swift b/APIService/Sources/ProductInfoAPISupport/ProductInfoURLProtocol.swift new file mode 100644 index 0000000..ec1a6b9 --- /dev/null +++ b/APIService/Sources/ProductInfoAPISupport/ProductInfoURLProtocol.swift @@ -0,0 +1,46 @@ +// +// ProductInfoURLProtocol.swift +// +// +// Created by 김응철 on 2/5/24. +// + +import Foundation +import ProductInfoAPI +import Network + +public final class ProductInfoURLProtocol: URLProtocol { + private lazy var mockData: [String: Data?] = [ + ProductInfoEndPoint.fetchProduct(0).path : loadMockData(fileName: "HomeProductResponse"), + ProductInfoEndPoint.fetchPrices(0).path : loadMockData(fileName: "ProductPriceResponse") + ] + + public override class func canInit(with request: URLRequest) -> Bool { + true + } + + public override class func canonicalRequest(for request: URLRequest) -> URLRequest { + request + } + + public override func startLoading() { + defer { client?.urlProtocolDidFinishLoading(self) } + if let url = request.url, + let mockData = mockData[url.path(percentEncoded: true)], + let data = mockData, + let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil) { + client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) + client?.urlProtocol(self, didLoad: data) + } else { + client?.urlProtocol(self, didFailWithError: NetworkError.urlError) + } + } + + private func loadMockData(fileName: String) -> Data? { + guard let url = Bundle.module.url(forResource: fileName, withExtension: "json") + else { + return nil + } + return try? Data(contentsOf: url) + } +} From 9ebef57ff7e66bb7a6b03973e382230fccfea94d Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Mon, 5 Feb 2024 17:39:15 +0900 Subject: [PATCH 06/14] =?UTF-8?q?=F0=9F=8C=B1=20ProductPrice=20Entity=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Entity/Sources/Entity/ProductPrice.swift | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Entity/Sources/Entity/ProductPrice.swift diff --git a/Entity/Sources/Entity/ProductPrice.swift b/Entity/Sources/Entity/ProductPrice.swift new file mode 100644 index 0000000..f850e03 --- /dev/null +++ b/Entity/Sources/Entity/ProductPrice.swift @@ -0,0 +1,21 @@ +// +// ProductPrice.swift +// +// +// Created by 김응철 on 2/5/24. +// + +import Foundation + +public struct ProductPrice { + let date: String + let price: Int + + public init( + date: String, + price: Int + ) { + self.date = date + self.price = price + } +} From 937eb841c4663f800aa938a888a3a730e791d50f Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Mon, 5 Feb 2024 17:52:20 +0900 Subject: [PATCH 07/14] =?UTF-8?q?=E2=9C=A8=20ProductInfoService=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- APIService/Package.swift | 1 + .../ProductInfoAPI/ProductInfoService.swift | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 APIService/Sources/ProductInfoAPI/ProductInfoService.swift diff --git a/APIService/Package.swift b/APIService/Package.swift index db73612..d80e2f5 100644 --- a/APIService/Package.swift +++ b/APIService/Package.swift @@ -45,6 +45,7 @@ let package = Package( .target( name: "ProductInfoAPI", dependencies: [ + "HomeAPI", .product(name: "Network", package: "Core"), .product(name: "Entity", package: "Entity"), ] diff --git a/APIService/Sources/ProductInfoAPI/ProductInfoService.swift b/APIService/Sources/ProductInfoAPI/ProductInfoService.swift new file mode 100644 index 0000000..874895f --- /dev/null +++ b/APIService/Sources/ProductInfoAPI/ProductInfoService.swift @@ -0,0 +1,50 @@ +// +// ProductInfoService.swift +// +// +// Created by 김응철 on 2/5/24. +// + +import Entity +import Foundation +import HomeAPI +import Network + +// MARK: - ProductInfoServiceRepresentable + +public protocol ProductInfoServiceRepresentable { + func fetchProduct(productID: Int) async throws -> Product + func fetchProductPrice(productID: Int) async throws -> [ProductPrice] +} + +// MARK: - ProductInfoService + +public struct ProductInfoService { + private let network: Networking + + public init(network: Networking) { + self.network = network + } +} + +// MARK: ProductInfoServiceRepresentable + +extension ProductInfoService: ProductInfoServiceRepresentable { + public func fetchProduct(productID _: Int) async throws -> Product { + let product: ProductResponse = try await network.request(with: ProductInfoEndPoint.fetchProduct(0)) + return product + } + + public func fetchProductPrice(productID _: Int) async throws -> [ProductPrice] { + let productPrice: [ProductPriceResponse] = try await network.request( + with: ProductInfoEndPoint.fetchPrices(0) + ) + return productPrice.map(ProductPrice.init) + } +} + +private extension ProductPrice { + init(dto: ProductPriceResponse) { + self.init(date: dto.date, price: dto.price) + } +} From 92dc9155ef73cb49779a910054f0c7f57141b1c2 Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Mon, 5 Feb 2024 18:36:31 +0900 Subject: [PATCH 08/14] =?UTF-8?q?=F0=9F=8E=A8=20SwiftFormat=20=ED=98=95?= =?UTF-8?q?=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EC=BD=94=EB=93=9C=20=EC=BB=A8?= =?UTF-8?q?=EB=B2=A4=EC=85=98=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- APIService/Package.swift | 2 -- .../ProductInfoAPI/ProductInfoEndPoint.swift | 4 ++-- .../ProductInfoAPI/ProductInfoService.swift | 4 ++-- .../ProductInfoURLProtocol.swift | 20 +++++++++---------- Entity/Sources/Entity/ProductPrice.swift | 2 +- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/APIService/Package.swift b/APIService/Package.swift index d80e2f5..7633eb3 100644 --- a/APIService/Package.swift +++ b/APIService/Package.swift @@ -46,8 +46,6 @@ let package = Package( name: "ProductInfoAPI", dependencies: [ "HomeAPI", - .product(name: "Network", package: "Core"), - .product(name: "Entity", package: "Entity"), ] ), .target( diff --git a/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift b/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift index d9a946e..14a943c 100644 --- a/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift +++ b/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift @@ -24,9 +24,9 @@ extension ProductInfoEndPoint: EndPoint { public var path: String { switch self { - case .fetchProduct(let productID): + case let .fetchProduct(productID): "/v2/products/\(productID)" - case .fetchPrices(let productID): + case let .fetchPrices(productID): "/v2/products/\(productID)/price-history" } } diff --git a/APIService/Sources/ProductInfoAPI/ProductInfoService.swift b/APIService/Sources/ProductInfoAPI/ProductInfoService.swift index 874895f..4136004 100644 --- a/APIService/Sources/ProductInfoAPI/ProductInfoService.swift +++ b/APIService/Sources/ProductInfoAPI/ProductInfoService.swift @@ -31,8 +31,8 @@ public struct ProductInfoService { extension ProductInfoService: ProductInfoServiceRepresentable { public func fetchProduct(productID _: Int) async throws -> Product { - let product: ProductResponse = try await network.request(with: ProductInfoEndPoint.fetchProduct(0)) - return product + let productResponse: ProductResponse = try await network.request(with: ProductInfoEndPoint.fetchProduct(0)) + return Product(dto: productResponse) } public func fetchProductPrice(productID _: Int) async throws -> [ProductPrice] { diff --git a/APIService/Sources/ProductInfoAPISupport/ProductInfoURLProtocol.swift b/APIService/Sources/ProductInfoAPISupport/ProductInfoURLProtocol.swift index ec1a6b9..7ece465 100644 --- a/APIService/Sources/ProductInfoAPISupport/ProductInfoURLProtocol.swift +++ b/APIService/Sources/ProductInfoAPISupport/ProductInfoURLProtocol.swift @@ -6,24 +6,24 @@ // import Foundation -import ProductInfoAPI import Network +import ProductInfoAPI public final class ProductInfoURLProtocol: URLProtocol { private lazy var mockData: [String: Data?] = [ - ProductInfoEndPoint.fetchProduct(0).path : loadMockData(fileName: "HomeProductResponse"), - ProductInfoEndPoint.fetchPrices(0).path : loadMockData(fileName: "ProductPriceResponse") + ProductInfoEndPoint.fetchProduct(0).path: loadMockData(fileName: "HomeProductResponse"), + ProductInfoEndPoint.fetchPrices(0).path: loadMockData(fileName: "ProductPriceResponse"), ] - - public override class func canInit(with request: URLRequest) -> Bool { + + override public class func canInit(with _: URLRequest) -> Bool { true } - - public override class func canonicalRequest(for request: URLRequest) -> URLRequest { + + override public class func canonicalRequest(for request: URLRequest) -> URLRequest { request } - - public override func startLoading() { + + override public func startLoading() { defer { client?.urlProtocolDidFinishLoading(self) } if let url = request.url, let mockData = mockData[url.path(percentEncoded: true)], @@ -35,7 +35,7 @@ public final class ProductInfoURLProtocol: URLProtocol { client?.urlProtocol(self, didFailWithError: NetworkError.urlError) } } - + private func loadMockData(fileName: String) -> Data? { guard let url = Bundle.module.url(forResource: fileName, withExtension: "json") else { diff --git a/Entity/Sources/Entity/ProductPrice.swift b/Entity/Sources/Entity/ProductPrice.swift index f850e03..977354e 100644 --- a/Entity/Sources/Entity/ProductPrice.swift +++ b/Entity/Sources/Entity/ProductPrice.swift @@ -10,7 +10,7 @@ import Foundation public struct ProductPrice { let date: String let price: Int - + public init( date: String, price: Int From 3320a6542a0e5c49244c15320d5a96491801c45d Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Mon, 5 Feb 2024 18:37:59 +0900 Subject: [PATCH 09/14] =?UTF-8?q?=F0=9F=8C=B1=20Product.init(dto:)=20priva?= =?UTF-8?q?te=20=ED=95=B4=EC=A0=9C=20=EB=B0=8F=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- APIService/Sources/HomeAPI/HomeService.swift | 13 ------------- .../HomeAPI/Responses/ProductResponse.swift | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/APIService/Sources/HomeAPI/HomeService.swift b/APIService/Sources/HomeAPI/HomeService.swift index bf0b025..48d2d26 100644 --- a/APIService/Sources/HomeAPI/HomeService.swift +++ b/APIService/Sources/HomeAPI/HomeService.swift @@ -39,16 +39,3 @@ extension HomeService: HomeServiceRepresentable { return countResponse.count } } - -private extension Product { - init(dto: ProductResponse) { - self.init( - id: dto.id, - imageURL: dto.imageURL, - price: dto.price, - name: dto.name, - promotion: dto.promotion, - convenienceStore: dto.convenienceStore - ) - } -} diff --git a/APIService/Sources/HomeAPI/Responses/ProductResponse.swift b/APIService/Sources/HomeAPI/Responses/ProductResponse.swift index 1000295..18dbeb9 100644 --- a/APIService/Sources/HomeAPI/Responses/ProductResponse.swift +++ b/APIService/Sources/HomeAPI/Responses/ProductResponse.swift @@ -8,7 +8,9 @@ import Entity import Foundation -struct ProductResponse: Decodable { +// MARK: - ProductResponse + +public struct ProductResponse: Decodable { let id: Int let imageURL: URL let price: Int @@ -25,3 +27,16 @@ struct ProductResponse: Decodable { case convenienceStore } } + +public extension Product { + init(dto: ProductResponse) { + self.init( + id: dto.id, + imageURL: dto.imageURL, + price: dto.price, + name: dto.name, + promotion: dto.promotion, + convenienceStore: dto.convenienceStore + ) + } +} From 67339424f34ac0482443cb5d0df7cd26220e50e4 Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Mon, 5 Feb 2024 23:06:20 +0900 Subject: [PATCH 10/14] =?UTF-8?q?=F0=9F=8C=B1=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProductDetailResponse DTO 추가 - 같은 모듈내에서 참조하는 것을 방지 --- APIService/Package.swift | 3 ++- APIService/Sources/HomeAPI/HomeService.swift | 13 +++++++++ .../HomeAPI/Responses/ProductResponse.swift | 13 --------- .../ProductInfoAPI/ProductInfoService.swift | 16 +++++++++-- .../Responses/ProductDetailResponse.swift | 27 +++++++++++++++++++ 5 files changed, 56 insertions(+), 16 deletions(-) create mode 100644 APIService/Sources/ProductInfoAPI/Responses/ProductDetailResponse.swift diff --git a/APIService/Package.swift b/APIService/Package.swift index 7633eb3..db73612 100644 --- a/APIService/Package.swift +++ b/APIService/Package.swift @@ -45,7 +45,8 @@ let package = Package( .target( name: "ProductInfoAPI", dependencies: [ - "HomeAPI", + .product(name: "Network", package: "Core"), + .product(name: "Entity", package: "Entity"), ] ), .target( diff --git a/APIService/Sources/HomeAPI/HomeService.swift b/APIService/Sources/HomeAPI/HomeService.swift index 48d2d26..bf0b025 100644 --- a/APIService/Sources/HomeAPI/HomeService.swift +++ b/APIService/Sources/HomeAPI/HomeService.swift @@ -39,3 +39,16 @@ extension HomeService: HomeServiceRepresentable { return countResponse.count } } + +private extension Product { + init(dto: ProductResponse) { + self.init( + id: dto.id, + imageURL: dto.imageURL, + price: dto.price, + name: dto.name, + promotion: dto.promotion, + convenienceStore: dto.convenienceStore + ) + } +} diff --git a/APIService/Sources/HomeAPI/Responses/ProductResponse.swift b/APIService/Sources/HomeAPI/Responses/ProductResponse.swift index 18dbeb9..1163ec8 100644 --- a/APIService/Sources/HomeAPI/Responses/ProductResponse.swift +++ b/APIService/Sources/HomeAPI/Responses/ProductResponse.swift @@ -27,16 +27,3 @@ public struct ProductResponse: Decodable { case convenienceStore } } - -public extension Product { - init(dto: ProductResponse) { - self.init( - id: dto.id, - imageURL: dto.imageURL, - price: dto.price, - name: dto.name, - promotion: dto.promotion, - convenienceStore: dto.convenienceStore - ) - } -} diff --git a/APIService/Sources/ProductInfoAPI/ProductInfoService.swift b/APIService/Sources/ProductInfoAPI/ProductInfoService.swift index 4136004..d1da493 100644 --- a/APIService/Sources/ProductInfoAPI/ProductInfoService.swift +++ b/APIService/Sources/ProductInfoAPI/ProductInfoService.swift @@ -7,7 +7,6 @@ import Entity import Foundation -import HomeAPI import Network // MARK: - ProductInfoServiceRepresentable @@ -31,7 +30,7 @@ public struct ProductInfoService { extension ProductInfoService: ProductInfoServiceRepresentable { public func fetchProduct(productID _: Int) async throws -> Product { - let productResponse: ProductResponse = try await network.request(with: ProductInfoEndPoint.fetchProduct(0)) + let productResponse: ProductDetailResponse = try await network.request(with: ProductInfoEndPoint.fetchProduct(0)) return Product(dto: productResponse) } @@ -48,3 +47,16 @@ private extension ProductPrice { self.init(date: dto.date, price: dto.price) } } + +private extension Product { + init(dto: ProductDetailResponse) { + self.init( + id: dto.id, + imageURL: dto.imageURL, + price: dto.price, + name: dto.name, + promotion: dto.promotion, + convenienceStore: dto.convenienceStore + ) + } +} diff --git a/APIService/Sources/ProductInfoAPI/Responses/ProductDetailResponse.swift b/APIService/Sources/ProductInfoAPI/Responses/ProductDetailResponse.swift new file mode 100644 index 0000000..9f33c48 --- /dev/null +++ b/APIService/Sources/ProductInfoAPI/Responses/ProductDetailResponse.swift @@ -0,0 +1,27 @@ +// +// ProductDetailResponse.swift +// +// +// Created by 김응철 on 2/5/24. +// + +import Entity +import Foundation + +public struct ProductDetailResponse: Decodable { + let id: Int + let imageURL: URL + let price: Int + let name: String + let promotion: Promotion + let convenienceStore: ConvenienceStore + + enum CodingKeys: String, CodingKey { + case id + case imageURL = "image_url" + case price + case name + case promotion + case convenienceStore + } +} From c98799b78a7cf1ff2665850769d749c6f57079e5 Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Mon, 5 Feb 2024 23:21:03 +0900 Subject: [PATCH 11/14] =?UTF-8?q?=F0=9F=8C=B1=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=20=EB=B3=80=ED=99=98=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - init(decoder:) 제거 - ProductPrice init(dto:) 날짜 변환 코드 추가 --- .../ProductInfoAPI/ProductInfoService.swift | 9 ++++++++- .../Responses/ProductPriceResponse.swift | 15 +-------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/APIService/Sources/ProductInfoAPI/ProductInfoService.swift b/APIService/Sources/ProductInfoAPI/ProductInfoService.swift index d1da493..59cd50d 100644 --- a/APIService/Sources/ProductInfoAPI/ProductInfoService.swift +++ b/APIService/Sources/ProductInfoAPI/ProductInfoService.swift @@ -44,7 +44,14 @@ extension ProductInfoService: ProductInfoServiceRepresentable { private extension ProductPrice { init(dto: ProductPriceResponse) { - self.init(date: dto.date, price: dto.price) + let formatter = DateFormatter() + formatter.dateFormat = "yyyy.MM" + let dateString = formatter.string(from: dto.date) + + self.init( + date: dateString, + price: dto.price + ) } } diff --git a/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift b/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift index 8e041ec..1bad4e8 100644 --- a/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift +++ b/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift @@ -8,19 +8,6 @@ import Foundation public struct ProductPriceResponse: Decodable { - let date: String + let date: Date let price: Int - - enum CodingKeys: CodingKey { - case date - case price - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - // TODO: 서버에서 내려주는 날짜 형식으로 바꾸기 - let dateString = try container.decode(Date.self, forKey: .date).formatted() - date = dateString - price = try container.decode(Int.self, forKey: .price) - } } From 05995d0cb4b2a71de35b6c32661f61dbec9e510d Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Tue, 6 Feb 2024 10:09:49 +0900 Subject: [PATCH 12/14] =?UTF-8?q?=F0=9F=8C=B1=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProductPrice, yearMonth으로 변수 네이밍 변경 - 불필요한 접근 제어 제거 - 불필요한 코드 간략화 - ProductDetail, Entity 추가 및 적용 - ResponseDTO 날짜 값 iso8601으로 변경 --- .../HomeAPI/Responses/ProductResponse.swift | 2 +- .../ProductInfoAPI/ProductInfoEndPoint.swift | 4 +- .../ProductInfoAPI/ProductInfoService.swift | 12 ++--- .../Mocks/ProductPriceResponse.json | 6 +-- Entity/Sources/Entity/Product.swift | 2 +- Entity/Sources/Entity/ProductDetail.swift | 47 +++++++++++++++++++ Entity/Sources/Entity/ProductPrice.swift | 8 ++-- 7 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 Entity/Sources/Entity/ProductDetail.swift diff --git a/APIService/Sources/HomeAPI/Responses/ProductResponse.swift b/APIService/Sources/HomeAPI/Responses/ProductResponse.swift index 1163ec8..7dab396 100644 --- a/APIService/Sources/HomeAPI/Responses/ProductResponse.swift +++ b/APIService/Sources/HomeAPI/Responses/ProductResponse.swift @@ -10,7 +10,7 @@ import Foundation // MARK: - ProductResponse -public struct ProductResponse: Decodable { +struct ProductResponse: Decodable { let id: Int let imageURL: URL let price: Int diff --git a/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift b/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift index 14a943c..f4e9089 100644 --- a/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift +++ b/APIService/Sources/ProductInfoAPI/ProductInfoEndPoint.swift @@ -18,7 +18,7 @@ public enum ProductInfoEndPoint { // MARK: EndPoint extension ProductInfoEndPoint: EndPoint { - public var method: Network.HTTPMethod { + public var method: HTTPMethod { .get } @@ -31,7 +31,7 @@ extension ProductInfoEndPoint: EndPoint { } } - public var parameters: Network.HTTPParameter { + public var parameters: HTTPParameter { .plain } diff --git a/APIService/Sources/ProductInfoAPI/ProductInfoService.swift b/APIService/Sources/ProductInfoAPI/ProductInfoService.swift index 59cd50d..9cceb22 100644 --- a/APIService/Sources/ProductInfoAPI/ProductInfoService.swift +++ b/APIService/Sources/ProductInfoAPI/ProductInfoService.swift @@ -12,7 +12,7 @@ import Network // MARK: - ProductInfoServiceRepresentable public protocol ProductInfoServiceRepresentable { - func fetchProduct(productID: Int) async throws -> Product + func fetchProduct(productID: Int) async throws -> ProductDetail func fetchProductPrice(productID: Int) async throws -> [ProductPrice] } @@ -29,9 +29,9 @@ public struct ProductInfoService { // MARK: ProductInfoServiceRepresentable extension ProductInfoService: ProductInfoServiceRepresentable { - public func fetchProduct(productID _: Int) async throws -> Product { + public func fetchProduct(productID _: Int) async throws -> ProductDetail { let productResponse: ProductDetailResponse = try await network.request(with: ProductInfoEndPoint.fetchProduct(0)) - return Product(dto: productResponse) + return ProductDetail(dto: productResponse) } public func fetchProductPrice(productID _: Int) async throws -> [ProductPrice] { @@ -46,16 +46,16 @@ private extension ProductPrice { init(dto: ProductPriceResponse) { let formatter = DateFormatter() formatter.dateFormat = "yyyy.MM" - let dateString = formatter.string(from: dto.date) + let yearMonth = formatter.string(from: dto.date) self.init( - date: dateString, + yearMonth: yearMonth, price: dto.price ) } } -private extension Product { +private extension ProductDetail { init(dto: ProductDetailResponse) { self.init( id: dto.id, diff --git a/APIService/Sources/ProductInfoAPISupport/Mocks/ProductPriceResponse.json b/APIService/Sources/ProductInfoAPISupport/Mocks/ProductPriceResponse.json index de1c90f..aa735e7 100644 --- a/APIService/Sources/ProductInfoAPISupport/Mocks/ProductPriceResponse.json +++ b/APIService/Sources/ProductInfoAPISupport/Mocks/ProductPriceResponse.json @@ -1,14 +1,14 @@ [ { - "date": "2023.12" + "date": "2023-12-06T00:42:21Z" "price": 1100 }, { - "date": "2024.01" + "date": "2024-01-06T00:42:21Z" "price": 1200 }, { - "date": "2024.02" + "date": "2024-02-06T00:42:21Z" "price": 1250 } ] diff --git a/Entity/Sources/Entity/Product.swift b/Entity/Sources/Entity/Product.swift index 82f4471..1a209e6 100644 --- a/Entity/Sources/Entity/Product.swift +++ b/Entity/Sources/Entity/Product.swift @@ -7,7 +7,7 @@ import Foundation -/// 편의점 행사 제품에 대한 Entity Model입니다. +/// 편의점 행사 제품 리스트에 대한 Entity Model입니다. public struct Product: Identifiable { /// 제품 고유 Identifier public let id: Int diff --git a/Entity/Sources/Entity/ProductDetail.swift b/Entity/Sources/Entity/ProductDetail.swift new file mode 100644 index 0000000..6c653c5 --- /dev/null +++ b/Entity/Sources/Entity/ProductDetail.swift @@ -0,0 +1,47 @@ +// +// ProductDetail.swift +// +// +// Created by 김응철 on 2/6/24. +// + +import Foundation + +/// 편의점 행사 제품에 대한 Entity Model입니다. +public struct ProductDetail: Identifiable { + /// 제품 고유 Identifier + public let id: Int + + /// 이미지 URL + public let imageURL: URL + + /// 제품 가격 + public let price: Int + + /// 제품명 + public let name: String + + /// 행사 정보 + public let promotion: Promotion + + /// 편의점 + public let convenienceStore: ConvenienceStore + + // TODO: 카테고리 상수 추가하기 + + public init( + id: Int, + imageURL: URL, + price: Int, + name: String, + promotion: Promotion, + convenienceStore: ConvenienceStore + ) { + self.id = id + self.imageURL = imageURL + self.price = price + self.name = name + self.promotion = promotion + self.convenienceStore = convenienceStore + } +} diff --git a/Entity/Sources/Entity/ProductPrice.swift b/Entity/Sources/Entity/ProductPrice.swift index 977354e..c1fb5bc 100644 --- a/Entity/Sources/Entity/ProductPrice.swift +++ b/Entity/Sources/Entity/ProductPrice.swift @@ -8,14 +8,14 @@ import Foundation public struct ProductPrice { - let date: String - let price: Int + public let yearMonth: String + public let price: Int public init( - date: String, + yearMonth: String, price: Int ) { - self.date = date + self.yearMonth = yearMonth self.price = price } } From b530288f99ee85504f113af3eafc72741f5a0e3e Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Tue, 6 Feb 2024 18:10:23 +0900 Subject: [PATCH 13/14] =?UTF-8?q?=F0=9F=8C=B1=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이니셜라이저에 불필요한 코드 제거 --- .../Sources/ProductInfoAPI/ProductInfoService.swift | 9 +-------- Entity/Sources/Entity/ProductPrice.swift | 6 +++--- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/APIService/Sources/ProductInfoAPI/ProductInfoService.swift b/APIService/Sources/ProductInfoAPI/ProductInfoService.swift index 9cceb22..4f145a5 100644 --- a/APIService/Sources/ProductInfoAPI/ProductInfoService.swift +++ b/APIService/Sources/ProductInfoAPI/ProductInfoService.swift @@ -44,14 +44,7 @@ extension ProductInfoService: ProductInfoServiceRepresentable { private extension ProductPrice { init(dto: ProductPriceResponse) { - let formatter = DateFormatter() - formatter.dateFormat = "yyyy.MM" - let yearMonth = formatter.string(from: dto.date) - - self.init( - yearMonth: yearMonth, - price: dto.price - ) + self.init(date: dto.date, price: dto.price) } } diff --git a/Entity/Sources/Entity/ProductPrice.swift b/Entity/Sources/Entity/ProductPrice.swift index c1fb5bc..670d7c7 100644 --- a/Entity/Sources/Entity/ProductPrice.swift +++ b/Entity/Sources/Entity/ProductPrice.swift @@ -8,14 +8,14 @@ import Foundation public struct ProductPrice { - public let yearMonth: String + public let date: Date public let price: Int public init( - yearMonth: String, + date: Date, price: Int ) { - self.yearMonth = yearMonth + self.date = date self.price = price } } From 4f7c119327a69b6454291fce0f41605e6feab152 Mon Sep 17 00:00:00 2001 From: EungCheol Kim Date: Tue, 6 Feb 2024 21:43:34 +0900 Subject: [PATCH 14/14] =?UTF-8?q?=F0=9F=8C=B1=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 불필요한 접근제어 삭제 --- .../ProductInfoAPI/Responses/ProductDetailResponse.swift | 2 +- .../Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/APIService/Sources/ProductInfoAPI/Responses/ProductDetailResponse.swift b/APIService/Sources/ProductInfoAPI/Responses/ProductDetailResponse.swift index 9f33c48..5f9a8fc 100644 --- a/APIService/Sources/ProductInfoAPI/Responses/ProductDetailResponse.swift +++ b/APIService/Sources/ProductInfoAPI/Responses/ProductDetailResponse.swift @@ -8,7 +8,7 @@ import Entity import Foundation -public struct ProductDetailResponse: Decodable { +struct ProductDetailResponse: Decodable { let id: Int let imageURL: URL let price: Int diff --git a/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift b/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift index 1bad4e8..633ef9a 100644 --- a/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift +++ b/APIService/Sources/ProductInfoAPI/Responses/ProductPriceResponse.swift @@ -7,7 +7,7 @@ import Foundation -public struct ProductPriceResponse: Decodable { +struct ProductPriceResponse: Decodable { let date: Date let price: Int }