From 1240c2dbc79ec8f447db9e01415c64365f231da0 Mon Sep 17 00:00:00 2001 From: SeungHyun Hong Date: Sat, 3 Feb 2024 10:39:14 +0900 Subject: [PATCH] =?UTF-8?q?:twisted=5Frightwards=5Farrows:=20=EC=83=81?= =?UTF-8?q?=ED=92=88=20=EA=B0=9C=EC=88=98=20API=20=EC=97=B0=EA=B2=B0=20(#3?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :sparkles: Implement product count api request model and endpoint * :construction: Create mock JSON and response model - Connected URLProtocol with mock JSON and linked the network flow in `HomeService`, setting up a foundation for network testing. * :bug: Fix bug where mock JSON file could not be read * :sparkles: Connect HomeProductCount API to view --- APIService/Package.swift | 2 +- APIService/Sources/HomeAPI/HomeEndPoint.swift | 37 ++++++++++++++++--- APIService/Sources/HomeAPI/HomeService.swift | 12 ++++-- .../Requests/ProductCountRequest.swift | 17 +++++++++ .../HomeAPI/Requests/ProductRequest.swift | 8 ++++ .../Responses/ProductCountResponse.swift | 12 ++++++ .../HomeAPI/Responses/ProductResponse.swift | 16 ++++---- .../HomeAPISupport/HomeURLProtocol.swift | 15 ++++++-- .../Mocks/HomeProductCountResponse.json | 3 ++ .../{ => Mocks}/HomeProductResponse.json | 0 Core/Sources/Network/NetworkProvider.swift | 1 - .../HomeScene/HomeProductSorterView.swift | 16 ++++---- .../Sources/Scenes/HomeScene/HomeView.swift | 2 +- .../Scenes/HomeScene/HomeViewModel.swift | 15 +++++++- 14 files changed, 124 insertions(+), 32 deletions(-) create mode 100644 APIService/Sources/HomeAPI/Requests/ProductCountRequest.swift create mode 100644 APIService/Sources/HomeAPI/Responses/ProductCountResponse.swift create mode 100644 APIService/Sources/HomeAPISupport/Mocks/HomeProductCountResponse.json rename APIService/Sources/HomeAPISupport/{ => Mocks}/HomeProductResponse.json (100%) diff --git a/APIService/Package.swift b/APIService/Package.swift index 6d68147..cbe9406 100644 --- a/APIService/Package.swift +++ b/APIService/Package.swift @@ -32,7 +32,7 @@ let package = Package( dependencies: [ "HomeAPI", ], - resources: [.process("HomeProductResponse.json")] + resources: [.process("Mocks")] ), ] ) diff --git a/APIService/Sources/HomeAPI/HomeEndPoint.swift b/APIService/Sources/HomeAPI/HomeEndPoint.swift index 280a72a..e22e0b7 100644 --- a/APIService/Sources/HomeAPI/HomeEndPoint.swift +++ b/APIService/Sources/HomeAPI/HomeEndPoint.swift @@ -8,12 +8,39 @@ import Foundation import Network -struct HomeEndPoint: EndPoint { - let method: HTTPMethod = .get +// MARK: - HomeEndPoint - let path: String = "v2/products" +public enum HomeEndPoint { + case fetchProducts(ProductRequest) + case fetchCount(ProductCountRequest) +} + +// MARK: EndPoint + +extension HomeEndPoint: EndPoint { + public var method: HTTPMethod { + .get + } + + public var path: String { + switch self { + case .fetchProducts: + "/v2/products" + case .fetchCount: + "/v2/products/count" + } + } - let parameters: HTTPParameter = .plain + public var parameters: HTTPParameter { + switch self { + case let .fetchProducts(requestModel): + .query(requestModel) + case let .fetchCount(requestModel): + .query(requestModel) + } + } - let headers: [String: String] = ["Content-Type": "application/json"] + public var headers: [String: String] { + ["Content-Type": "application/json"] + } } diff --git a/APIService/Sources/HomeAPI/HomeService.swift b/APIService/Sources/HomeAPI/HomeService.swift index 19a3e48..bf0b025 100644 --- a/APIService/Sources/HomeAPI/HomeService.swift +++ b/APIService/Sources/HomeAPI/HomeService.swift @@ -12,7 +12,8 @@ import Network // MARK: - HomeServiceRepresentable public protocol HomeServiceRepresentable { - func fetchProductList() async throws -> [Product] + func fetchProductList(request: ProductRequest) async throws -> [Product] + func fetchProductCount(request: ProductCountRequest) async throws -> Int } // MARK: - HomeService @@ -28,10 +29,15 @@ public struct HomeService { // MARK: HomeServiceRepresentable extension HomeService: HomeServiceRepresentable { - public func fetchProductList() async throws -> [Product] { - let products: [ProductResponse] = try await network.request(with: HomeEndPoint()) + public func fetchProductList(request: ProductRequest) async throws -> [Product] { + let products: [ProductResponse] = try await network.request(with: HomeEndPoint.fetchProducts(request)) return products.map(Product.init) } + + public func fetchProductCount(request: ProductCountRequest) async throws -> Int { + let countResponse: ProductCountResponse = try await network.request(with: HomeEndPoint.fetchCount(request)) + return countResponse.count + } } private extension Product { diff --git a/APIService/Sources/HomeAPI/Requests/ProductCountRequest.swift b/APIService/Sources/HomeAPI/Requests/ProductCountRequest.swift new file mode 100644 index 0000000..ac3af6f --- /dev/null +++ b/APIService/Sources/HomeAPI/Requests/ProductCountRequest.swift @@ -0,0 +1,17 @@ +// +// ProductCountRequest.swift +// +// +// Created by 홍승현 on 2/2/24. +// + +import Entity +import Foundation + +public struct ProductCountRequest: Encodable { + public let convenienceStore: ConvenienceStore + + public init(convenienceStore: ConvenienceStore) { + self.convenienceStore = convenienceStore + } +} diff --git a/APIService/Sources/HomeAPI/Requests/ProductRequest.swift b/APIService/Sources/HomeAPI/Requests/ProductRequest.swift index 7653ad9..a1c26cd 100644 --- a/APIService/Sources/HomeAPI/Requests/ProductRequest.swift +++ b/APIService/Sources/HomeAPI/Requests/ProductRequest.swift @@ -14,4 +14,12 @@ public struct ProductRequest: Encodable { public let order: Order public let pageSize: Int public let offset: Int + + public init(store: ConvenienceStore, promotion: Promotion, order: Order, pageSize: Int, offset: Int) { + self.store = store + self.promotion = promotion + self.order = order + self.pageSize = pageSize + self.offset = offset + } } diff --git a/APIService/Sources/HomeAPI/Responses/ProductCountResponse.swift b/APIService/Sources/HomeAPI/Responses/ProductCountResponse.swift new file mode 100644 index 0000000..07bdf66 --- /dev/null +++ b/APIService/Sources/HomeAPI/Responses/ProductCountResponse.swift @@ -0,0 +1,12 @@ +// +// ProductCountResponse.swift +// +// +// Created by 홍승현 on 2/2/24. +// + +import Foundation + +struct ProductCountResponse: Decodable { + let count: Int +} diff --git a/APIService/Sources/HomeAPI/Responses/ProductResponse.swift b/APIService/Sources/HomeAPI/Responses/ProductResponse.swift index d7b63cf..1000295 100644 --- a/APIService/Sources/HomeAPI/Responses/ProductResponse.swift +++ b/APIService/Sources/HomeAPI/Responses/ProductResponse.swift @@ -8,15 +8,15 @@ import Entity import Foundation -public struct ProductResponse: Decodable { - public let id: Int - public let imageURL: URL - public let price: Int - public let name: String - public let promotion: Promotion - public let convenienceStore: ConvenienceStore +struct ProductResponse: Decodable { + let id: Int + let imageURL: URL + let price: Int + let name: String + let promotion: Promotion + let convenienceStore: ConvenienceStore - public enum CodingKeys: String, CodingKey { + enum CodingKeys: String, CodingKey { case id case imageURL = "image_url" case price diff --git a/APIService/Sources/HomeAPISupport/HomeURLProtocol.swift b/APIService/Sources/HomeAPISupport/HomeURLProtocol.swift index 558055d..46005c5 100644 --- a/APIService/Sources/HomeAPISupport/HomeURLProtocol.swift +++ b/APIService/Sources/HomeAPISupport/HomeURLProtocol.swift @@ -6,10 +6,18 @@ // import Foundation +import HomeAPI import Network public final class HomeURLProtocol: URLProtocol { - private let fileName = "HomeProductResponse" + private lazy var mockData: [String: Data?] = [ + HomeEndPoint.fetchProducts( + .init(store: .gs25, promotion: .allItems, order: .normal, pageSize: 0, offset: 0) + ).path: loadMockData(fileName: "HomeProductResponse"), + HomeEndPoint.fetchCount( + .init(convenienceStore: .gs25) + ).path: loadMockData(fileName: "HomeProductCountResponse"), + ] override public class func canInit(with _: URLRequest) -> Bool { true @@ -21,8 +29,9 @@ public final class HomeURLProtocol: URLProtocol { override public func startLoading() { defer { client?.urlProtocolDidFinishLoading(self) } - if let data = loadMockData(fileName: fileName), - let url = request.url, + 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) diff --git a/APIService/Sources/HomeAPISupport/Mocks/HomeProductCountResponse.json b/APIService/Sources/HomeAPISupport/Mocks/HomeProductCountResponse.json new file mode 100644 index 0000000..93406bd --- /dev/null +++ b/APIService/Sources/HomeAPISupport/Mocks/HomeProductCountResponse.json @@ -0,0 +1,3 @@ +{ + "count": 24 +} diff --git a/APIService/Sources/HomeAPISupport/HomeProductResponse.json b/APIService/Sources/HomeAPISupport/Mocks/HomeProductResponse.json similarity index 100% rename from APIService/Sources/HomeAPISupport/HomeProductResponse.json rename to APIService/Sources/HomeAPISupport/Mocks/HomeProductResponse.json diff --git a/Core/Sources/Network/NetworkProvider.swift b/Core/Sources/Network/NetworkProvider.swift index 1b2dcac..c56db78 100644 --- a/Core/Sources/Network/NetworkProvider.swift +++ b/Core/Sources/Network/NetworkProvider.swift @@ -5,7 +5,6 @@ // Created by 홍승현 on 1/25/24. // -import Combine import Foundation // MARK: - NetworkProvider diff --git a/PyeonHaeng-iOS/Sources/Scenes/HomeScene/HomeProductSorterView.swift b/PyeonHaeng-iOS/Sources/Scenes/HomeScene/HomeProductSorterView.swift index fc1d575..2d1c4d4 100644 --- a/PyeonHaeng-iOS/Sources/Scenes/HomeScene/HomeProductSorterView.swift +++ b/PyeonHaeng-iOS/Sources/Scenes/HomeScene/HomeProductSorterView.swift @@ -8,11 +8,8 @@ import SwiftUI struct HomeProductSorterView: View { - private let count: Int - - init(count: Int) { - self.count = count - } + @EnvironmentObject private var viewModel: HomeViewModel + @State private var count: Int = 0 var body: some View { HStack { @@ -24,6 +21,11 @@ struct HomeProductSorterView: View { } } .padding(.all, 8) + .onAppear { + Task { + count = try await viewModel.fetchProductCounts() + } + } } var productCountString: AttributedString { @@ -36,7 +38,3 @@ struct HomeProductSorterView: View { return string } } - -#Preview { - HomeProductSorterView(count: 50) -} diff --git a/PyeonHaeng-iOS/Sources/Scenes/HomeScene/HomeView.swift b/PyeonHaeng-iOS/Sources/Scenes/HomeScene/HomeView.swift index 6398be7..e568082 100644 --- a/PyeonHaeng-iOS/Sources/Scenes/HomeScene/HomeView.swift +++ b/PyeonHaeng-iOS/Sources/Scenes/HomeScene/HomeView.swift @@ -15,7 +15,7 @@ struct HomeView: View { NavigationStack { VStack { HomeProductDetailSelectionView() - HomeProductSorterView(count: 12) + HomeProductSorterView() HomeProductListView() } .padding(.horizontal, Metrics.horizontal) diff --git a/PyeonHaeng-iOS/Sources/Scenes/HomeScene/HomeViewModel.swift b/PyeonHaeng-iOS/Sources/Scenes/HomeScene/HomeViewModel.swift index 18c4ad7..68d8fa1 100644 --- a/PyeonHaeng-iOS/Sources/Scenes/HomeScene/HomeViewModel.swift +++ b/PyeonHaeng-iOS/Sources/Scenes/HomeScene/HomeViewModel.swift @@ -21,6 +21,19 @@ final class HomeViewModel: ObservableObject { } func fetchProducts() async throws { - try await products.append(contentsOf: service.fetchProductList()) + // TODO: View와 연결할 때 수정해야합니다. + let request: ProductRequest = .init( + store: .gs25, + promotion: .buyOneGetOneFree, + order: .normal, + pageSize: 20, + offset: 0 + ) + try await products.append(contentsOf: service.fetchProductList(request: request)) + } + + func fetchProductCounts() async throws -> Int { + // TODO: 편의점 선택 뷰를 구성할 때 수정해야합니다. + try await service.fetchProductCount(request: .init(convenienceStore: .gs25)) } }