diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/DIContainer.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/DIContainer.swift deleted file mode 100644 index 29435662..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/DIContainer.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// DIContainer.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Foundation - -class DIContainer: ObservableObject { - var services: ServiceType - - init(services: ServiceType) { - self.services = services - } -} - diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Domain/UseCase/MyPageUseCase.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Domain/UseCase/MyPageUseCase.swift deleted file mode 100644 index 99b46e2e..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Domain/UseCase/MyPageUseCase.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// MyPageUseCase.swift -// MyPageFeature -// -// Created by 류희재 on 9/3/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Foundation -import Combine -import Core -import Domain - -protocol MyPageUseCaseType { - func getUserDate() -> AnyPublisher - func logout() - func revokeUser() -} - -final class MyPageUseCase: MyPageUseCaseType { - private var container: DIContainer - private var cancelBag = CancelBag() - - init(container: DIContainer) { - self.container = container - } - - func getUserDate() -> AnyPublisher { - container.services.userService.getUserData() - .map { $0.data! } - .eraseToAnyPublisher() - } - - func logout() { - container.services.authService.logoutUser() - .sink { _ in - } receiveValue: { _ in - UserManager.shared.clearLogout() - }.store(in: cancelBag) - } - - func revokeUser() { - container.services.authService.revokeUser() - .sink { _ in - } receiveValue: { _ in - UserManager.shared.revokeData() - }.store(in: cancelBag) - } -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/MyFeature/MyPageButtonType.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/MyFeature/MyPageButtonType.swift deleted file mode 100644 index 702448d2..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/MyFeature/MyPageButtonType.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// MyPageButtonType.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Foundation - -import DSKit -import UIKit - -enum MyPageButtonType_Refactor { - case travel - case market - case term - case info - - var titleText: String { - switch self { - case .travel: - return StringLiteral.MyPageButton.travel - case .market: - return StringLiteral.MyPageButton.market - case .term: - return StringLiteral.MyPageButton.term - case .info: - return StringLiteral.MyPageButton.info - } - } - - var imageName: String? { - switch self { - case .travel: - return "map" - case .market: - return "market" - case .term, .info: - return nil - } - } - - var clickAction: Void { - switch self { - case .travel: - let url = URL(string: StringLiteral.MyPageURL.term)! - UIApplication.shared.open(url) - case .market: - let url = URL(string: StringLiteral.MyPageURL.info)! - UIApplication.shared.open(url) - default: - break - } - } -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/MyFeature/MyPageButton_Refactor.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/MyFeature/MyPageButton_Refactor.swift deleted file mode 100644 index f07c9cd0..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/MyFeature/MyPageButton_Refactor.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// MyPageButton_Refactor.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - - -import SwiftUI - -import DSKit - -struct MyPageButton_Refactor: View { - - var buttonType: MyPageButtonType_Refactor - - var body: some View { - ZStack { - HStack() { - if let image = buttonType.imageName { - Image(image) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 24, height: 24) - .padding(.trailing, 15) - } - Text(buttonType.titleText) - .font(.text5_medium_16) - Spacer() - Image(uiImage: buttonType == .travel ? DSKitAsset.chevrongray.image : DSKitAsset.chevronRight.image) - } - .frame(maxWidth: .infinity) - .frame(height: 58) - .foregroundColor(buttonType == .travel ? DSKitAsset.gray5.swiftUIColor : DSKitAsset.whiteText.swiftUIColor) - } - .background(DSKitAsset.blackground.swiftUIColor) - .onTapGesture { - buttonType.clickAction - } - } -} - - diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/MyFeature/MyPageViewModel_Refactor.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/MyFeature/MyPageViewModel_Refactor.swift deleted file mode 100644 index 91a8216d..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/MyFeature/MyPageViewModel_Refactor.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// MyPageViewModel.swift -// HMH_iOS -// -// Created by Seonwoo Kim on 4/12/24. -// - -import Combine - -import Core -import DSKit - -class MyPageViewModel_Refactor: ObservableObject { - - private var useCase: MyPageUseCaseType - private var cancelBag = CancelBag() - - init(useCase: MyPageUseCaseType) { - self.useCase = useCase - } - - @Published private(set) var state = State( - alertType: .logout, - name: "", - point: 0 - ) - - //MARK: Action - - enum Action { - case onAppearEvent - case logoutButtonDidTap - case withdrawButtonDidTap - case confirmButtonDidTap - } - - //MARK: State - - struct State { - var alertType: CustomAlertType - var name: String - var point: Int - } - - func send(action: Action) { - switch action { - case .onAppearEvent: - useCase.getUserDate() - .sink { _ in - } receiveValue: { [weak self] data in - self?.state.name = data.name - self?.state.point = data.point - }.store(in: cancelBag) - - case .logoutButtonDidTap: - state.alertType = .logout - - case .withdrawButtonDidTap: - state.alertType = .withdraw - - case .confirmButtonDidTap: - state.alertType == .logout ? useCase.logout() : useCase.revokeUser() - } - } -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/MyFeature/MyPageView_Refactor.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/MyFeature/MyPageView_Refactor.swift deleted file mode 100644 index d13817d5..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/MyFeature/MyPageView_Refactor.swift +++ /dev/null @@ -1,126 +0,0 @@ -// -// MyPageView.swift -// HMH_iOS -// -// Created by Seonwoo Kim on 3/12/24. -// - -import SwiftUI - -import DSKit -import Core - -public struct MyPageView_Refactor: View { - @State private var isPresented: Bool = false - @StateObject var viewModel: MyPageViewModel_Refactor - - public var body: some View { - VStack { - Spacer() - .frame(height: 64) - ProfileView() - Spacer() - .frame(height: 36) - MyInfoView() - Spacer() - .frame(height: 34) - HMHInfoView() - Spacer() - AccountControlView() - } - .onAppear { viewModel.send(action: .onAppearEvent)} - .padding(20) - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(DSKitAsset.blackground.swiftUIColor) - .customAlert( - isPresented: $isPresented, - customAlert: { - CustomAlertView( - alertType: viewModel.state.alertType, - confirmBtn: CustomAlertButtonView( - buttonType: .Confirm, - alertType: viewModel.state.alertType, - isPresented: $isPresented, - action: { - UserManager.shared.appStateString = "login" - viewModel.send(action: .confirmButtonDidTap) - } - ), - cancelBtn: CustomAlertButtonView( - buttonType: .Cancel, - alertType: viewModel.state.alertType, - isPresented: $isPresented, - action: { isPresented = false } - ), currentPoint: 0, usagePoint: 0 - ) - } - ) - } -} - -extension MyPageView_Refactor { - private func ProfileView() -> some View { - VStack { - Image(uiImage: DSKitAsset.profile.image) - .frame(width: 54, height: 54) - .padding(10) - Text(viewModel.state.name) - .font(.title4_semibold_20) - Spacer() - .frame(height: 16) - HStack { - Text(StringLiteral.MyPageAccountControl.point) - .font(.text6_medium_14) - Text("\(viewModel.state.point)") - .font(.text6_medium_14) - } - .frame(maxWidth: .infinity) - .frame(height: 40) - .background(DSKitAsset.gray7.swiftUIColor) - .cornerRadius(8) - } - .foregroundColor(DSKitAsset.whiteText.swiftUIColor) - .frame(width: 133, height: 150) - } - - private func MyInfoView() -> some View { - VStack(spacing: 0) { - MyPageButton_Refactor(buttonType: .travel) - MyPageButton_Refactor(buttonType: .market) - } - .background(DSKitAsset.gray7.swiftUIColor) - } - - private func HMHInfoView() -> some View { - VStack(alignment: .leading, spacing: 0) { - Text("정보") - .font(.text4_semibold_16) - .foregroundColor(DSKitAsset.gray2.swiftUIColor) - .padding(.vertical, 14) - MyPageButton_Refactor(buttonType: .info) - MyPageButton_Refactor(buttonType: .term) - } - } - - private func AccountControlView() -> some View { - HStack { - Text(StringLiteral.MyPageAccountControl.logout) - .font(.text6_medium_14) - .onTapGesture { - isPresented = true - viewModel.send(action: .logoutButtonDidTap) - } - Rectangle() - .frame(width: 1, height: 16) - Text(StringLiteral.MyPageAccountControl.revoke) - .font(.text6_medium_14) - .onTapGesture { - isPresented = true - viewModel.send(action: .withdrawButtonDidTap) - } - } - .foregroundColor(DSKitAsset.gray3.swiftUIColor) - .frame(height: 77) - } -} - diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/API/AuthAPI.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/API/AuthAPI.swift deleted file mode 100644 index ceeb4821..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/API/AuthAPI.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// AuthAPI.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Foundation - -import Moya -import Domain - -enum AuthAPI { - case revoke - case logout -} - -extension AuthAPI: BaseAPI { - public static var apiType: APIType = .auth - - var path: String { - switch self { - case .revoke: - return "user" - case .logout: - return "user/logout" - } - } - - var method: Moya.Method { - switch self { - case .revoke: - return .delete - case .logout: - return .post - } - } - - var task: Moya.Task { - switch self { - case .revoke: - return .requestPlain - case .logout: - return .requestPlain - } - } - - var validationType: ValidationType { - switch self { - case .revoke: - return .successCodes - case .logout: - return .successCodes - } - } -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/API/UserAPI.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/API/UserAPI.swift deleted file mode 100644 index 6800e7fe..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/API/UserAPI.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// UserAPI.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Foundation - -import Moya -import Networks - -enum UserAPI { - case getUserData -} - -extension UserAPI: BaseAPI { - public static var apiType: APIType = .user - - var headers: [String : String]? { - switch self { - case .getUserData: - return ["Content-Type": "application/json", - "OS": "iOS", - "auth": ""] - } - } - - var path: String { - switch self { - case .getUserData: - return "user" - } - } - - var method: Moya.Method { - switch self { - case .getUserData: - return .get - } - } - - var task: Moya.Task { - switch self { - case .getUserData: - return .requestPlain - } - } -} - diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/APIError.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/APIError.swift deleted file mode 100644 index 3df4c65c..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/APIError.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// APIError.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Foundation - -public enum APIError: Error, Equatable { - case network(statusCode: Int, response: ErrorResponse) - case unknown - case tokenReissuanceFailed - - init(error: Error, statusCode: Int? = 0, response: ErrorResponse) { - guard let statusCode else { self = .unknown ; return } - - self = .network(statusCode: statusCode, response: response) - } -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/BaseAPI.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/BaseAPI.swift deleted file mode 100644 index dad5f9aa..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/BaseAPI.swift +++ /dev/null @@ -1,68 +0,0 @@ -// -// BaseAPI.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Alamofire -import Moya -import Foundation -import Core - -public enum APIType { - case auth - case user -} - -public protocol BaseAPI: TargetType { - static var apiType: APIType { get set } -} - -extension BaseAPI { - public var baseURL: URL { - var base = "Config.Network.baseURL" - let operationBaseURL = "Config.Network.operationBaseURL" - - switch Self.apiType { - case .auth: - base += "/auth" - case .user: - base += "/user" - } - - guard let url = URL(string: base) else { - fatalError("baseURL could not be configured") - } - - return url - } - - public var headers: [String: String]? { - return HeaderType.jsonWithToken.value - } - - public var validationType: ValidationType { - return .customCodes(Array(200..<600).filter { $0 != 401 }) - } -} - -public enum HeaderType { - case json - case jsonWithToken - case multipartWithToken - - public var value: [String: String] { - switch self { - case .json: - return ["Content-Type": "application/json"] - case .jsonWithToken: - return ["Content-Type": "application/json", - "Authorization": UserManager.shared.accessToken] - case .multipartWithToken: - return ["Content-Type": "multipart/form-data", - "Authorization": UserManager.shared.accessToken] - } - } -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/BaseEntity.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/BaseEntity.swift deleted file mode 100644 index 5d168acf..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/BaseEntity.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// BaseEntity.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Foundation - -public struct BaseEntity: Decodable { - public let success: Bool - public let message: String - public let data: T? - - enum CodingKeys: String, CodingKey { - case success, message, data - } - - public init(from decoder: Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - success = try values.decode(Bool.self, forKey: .success) - message = try values.decode(String.self, forKey: .message) - data = try? values.decodeIfPresent(T.self, forKey: .data) - } -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/BaseService.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/BaseService.swift deleted file mode 100644 index f1815cd7..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/BaseService.swift +++ /dev/null @@ -1,281 +0,0 @@ -// -// BaseService.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - - -import Combine -import Foundation - -import Core - -import Alamofire -import Moya - -open class BaseService { - - typealias API = Target - - // MARK: - Properties - - var cancelBag = CancelBag() - - lazy var provider = self.defaultProvider - - private lazy var defaultProvider: MoyaProvider = { - let configuration = URLSessionConfiguration.default - configuration.timeoutIntervalForRequest = 10 - configuration.timeoutIntervalForResource = 10 - configuration.requestCachePolicy = .reloadIgnoringLocalCacheData -// let interceptor = AlamoInterceptor() -// let sessionDelegate = BaseSessionDelegate() - let session = Session(configuration: configuration)/*, delegate: sessionDelegate, interceptor: interceptor)*/ - let provider = MoyaProvider( - endpointClosure: endpointClosure, - session: session, - plugins: [NetworkLoggerPlugin()] - ) - return provider - }() - - private lazy var testingProvider: MoyaProvider = { - let testingProvider = MoyaProvider(endpointClosure: endpointClosure, stubClosure: MoyaProvider.immediatelyStub) - return testingProvider - }() - - private lazy var testingProviderWithError: MoyaProvider = { - let testingProvider = MoyaProvider(endpointClosure: endpointClosureWithError, stubClosure: MoyaProvider.immediatelyStub) - return testingProvider - }() - - private let endpointClosure = { (target: API) -> Endpoint in - let url = target.baseURL.appendingPathComponent(target.path).absoluteString - var endpoint: Endpoint = Endpoint(url: url, - sampleResponseClosure: {.networkResponse(200, target.sampleData)}, - method: target.method, - task: target.task, - httpHeaderFields: target.headers) - return endpoint - } - - private let endpointClosureWithError = { (target: API) -> Endpoint in - let url = target.baseURL.appendingPathComponent(target.path).absoluteString - var endpoint: Endpoint = Endpoint(url: url, - sampleResponseClosure: {.networkResponse(400, target.sampleData)}, - method: target.method, - task: target.task, - httpHeaderFields: target.headers) - return endpoint - } - - // MARK: - Initializers - - public init() {} -} - -// MARK: - Providers - -public extension BaseService { - var `default`: BaseService { - self.provider = self.defaultProvider - return self - } - - var test: BaseService { - self.provider = self.testingProvider - return self - } - - var testWithError: BaseService { - self.provider = self.testingProviderWithError - return self - } -} - -// MARK: - MakeRequests - -extension BaseService { - func requestObjectInCombine(_ target: API) -> AnyPublisher { - return Future { promise in - self.provider.request(target) { response in - switch response { - case .success(let value): - do { - let decoder = JSONDecoder() - let body = try decoder.decode(T.self, from: value.data) - promise(.success(body)) - } catch let error { - promise(.failure(error)) - } - case .failure(let error): - if case MoyaError.underlying(let error, _) = error, - case AFError.requestRetryFailed(let retryError, _) = error, - let retryError = retryError as? APIError, - retryError == APIError.tokenReissuanceFailed { - promise(.failure(retryError)) - } else { - promise(.failure(error)) - } - } - } - }.eraseToAnyPublisher() - } - - func requestObjectWithNetworkErrorInCombine(_ target: API) -> AnyPublisher { - return Future { promise in - self.provider.request(target) { response in - switch response { - case .success(let value): - do { - guard let response = value.response else { throw NSError(domain: "이 경우는 생각 업씀", code: -1000) } - - switch response.statusCode { - case 200...399: - let decoder = JSONDecoder() - let body = try decoder.decode(T.self, from: value.data) - promise(.success(body)) - case 400...599: - // NOTE: (@승호) 여기에서 서버와 에러 처리 핸들링 해서 Error도 json Decode 해야 함 - // 임시로 Error 처리 - let decoder = JSONDecoder() - let body = try decoder.decode(ErrorResponse.self, from: value.data) - let apiError = APIError(error: NSError(domain: "임시에러", code: -1001), statusCode: response.statusCode, response: body) -// SentrySDK.capture(error: apiError) - throw apiError - default: break - } - } catch let error { -// SentrySDK.capture(message: "디코딩 에러") - promise(.failure(error)) - } - case .failure(let error): - if case MoyaError.underlying(let error, _) = error, - case AFError.requestRetryFailed(let retryError, _) = error, - let retryError = retryError as? APIError, - retryError == APIError.tokenReissuanceFailed { - promise(.failure(retryError)) - } else { - promise(.failure(error)) - } - } - } - }.eraseToAnyPublisher() - } - - func opRequestObjectInCombine(_ target: API) -> AnyPublisher { - return Future { promise in - self.provider.request(target) { response in - switch response { - case .success(let value): - do { - let decoder = JSONDecoder() - let body = try decoder.decode(T.self, from: value.data) - promise(.success(body)) - } catch let error { - promise(.failure(error)) - } - case .failure(let error): - // NOTE: 에러 메세지 받아서 보여주기 위함 - if case MoyaError.underlying(let error, _) = error, - case AFError.requestRetryFailed(let retryError, _) = error, - let retryError = retryError as? APIError, - retryError == APIError.tokenReissuanceFailed { - promise(.failure(retryError)) - } else { - do { - let decoder = JSONDecoder() - let errorData = try decoder.decode(BaseEntity.self, from: error.response?.data ?? Data()) - throw OPAPIError.attendanceError(errorData) - } catch let error { - promise(.failure(error)) - } - } - } - } - }.eraseToAnyPublisher() - } - - - func requestObjectInCombineNoResult(_ target: API) -> AnyPublisher { - return Future { promise in - self.provider.request(target) { response in - switch response { - case .success(let value): - promise(.success(value.statusCode)) - case .failure(let error): - promise(.failure(error)) - } - } - }.eraseToAnyPublisher() - } - - func requestObject(_ target: API, completion: @escaping (Result) -> Void) { - provider.request(target) { response in - switch response { - case .success(let value): - do { - let decoder = JSONDecoder() - let body = try decoder.decode(T.self, from: value.data) - completion(.success(body)) - } catch let error { - completion(.failure(error)) - } - case .failure(let error): - switch error { - case .underlying(let error, _): - if error.asAFError?.isSessionTaskError ?? false { - - } - default: break - } - completion(.failure(error)) - } - } - } - - func requestArray(_ target: API, completion: @escaping (Result<[T], Error>) -> Void) { - provider.request(target) { response in - switch response { - case .success(let value): - do { - let decoder = JSONDecoder() - let body = try decoder.decode([T].self, from: value.data) - completion(.success(body)) - } catch let error { - completion(.failure(error)) - } - case .failure(let error): - switch error { - case .underlying(let error, _): - if error.asAFError?.isSessionTaskError ?? false { - - } - default: break - } - completion(.failure(error)) - } - } - } - - func requestObjectWithNoResult(_ target: API, completion: @escaping (Result) -> Void) { - provider.request(target) { response in - switch response { - case .success(let value): - completion(.success(value.statusCode)) - - case .failure(let error): - switch error { - case .underlying(let error, _): - if error.asAFError?.isSessionTaskError ?? false { - - } - default: break - } - completion(.failure(error)) - } - } - } -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/CancelBag.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/CancelBag.swift deleted file mode 100644 index 8aacf3a2..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/CancelBag.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// CancelBag.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Combine - -open class CancelBag { - public var subscriptions = Set() - - public func cancel() { - subscriptions.forEach { $0.cancel() } - subscriptions.removeAll() - } - - public init() { } -} - -extension AnyCancellable { - public func store(in cancelBag: CancelBag) { - cancelBag.subscriptions.insert(self) - } -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/ErrorResponse.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/ErrorResponse.swift deleted file mode 100644 index f0ff5cd2..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/ErrorResponse.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// ErrorResponse.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Foundation - -public struct ErrorResponse: Decodable, Equatable { - public let statusCode: String - public let responseMessage: String -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/OPAPIError.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/OPAPIError.swift deleted file mode 100644 index af186801..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Foundation/OPAPIError.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// OPAPIError.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Foundation - -public enum OPAPIError: LocalizedError { - case attendanceError(BaseEntity) - - public var errorDescription: String? { - switch self { - case let .attendanceError(error): - return String(error.message.split(separator: ": ").last ?? "") - } - } -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Service/AuthService.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Service/AuthService.swift deleted file mode 100644 index 927d7297..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Service/AuthService.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// AuthService.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Foundation -import Combine - -import Domain - -typealias DefaultAuthService = BaseService - -protocol AuthServiceType { - func revokeUser() -> AnyPublisher - func logoutUser() -> AnyPublisher -} - -extension DefaultAuthService: AuthServiceType { - func revokeUser() -> AnyPublisher { - return requestObjectWithNetworkErrorInCombine(.revoke) - } - - func logoutUser() -> AnyPublisher { - return requestObjectWithNetworkErrorInCombine(.logout) - } -} - -class StubAuthService: AuthServiceType { - func revokeUser() -> AnyPublisher { - Empty().eraseToAnyPublisher() - } - - func logoutUser() -> AnyPublisher { - Empty().eraseToAnyPublisher() - } -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Service/UserService.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Service/UserService.swift deleted file mode 100644 index 35adacc2..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Service/UserService.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// UserService.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Foundation - -import Combine -import Domain -import Networks - -typealias DefaultUserService = BaseService - -import Foundation - -public struct BaseResponse: Decodable { - public var status: Int - public var message: String? - public var data: T? -} - -protocol UserServiceType { - func getUserData() -> AnyPublisher, Error> -} - -extension DefaultUserService: UserServiceType { - func getUserData() -> AnyPublisher, Error> { - return requestObjectWithNetworkErrorInCombine(.getUserData) - } -} - -class StubUserService: UserServiceType { - func getUserData() -> AnyPublisher, Error> { - Empty().eraseToAnyPublisher() - } -} diff --git a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Services.swift b/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Services.swift deleted file mode 100644 index 7653093f..00000000 --- a/HMH_Tuist_iOS/Projects/Features/MyPageFeature/Sources/Refactor_MyFeature_Combine/Service/Services.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// Services.swift -// MyPageFeatureInterface -// -// Created by 류희재 on 8/13/24. -// Copyright © 2024 HMH-iOS. All rights reserved. -// - -import Foundation - -protocol ServiceType { - var authService: AuthServiceType { get set } - var userService: UserServiceType { get set } -} - -class Services: ServiceType { - var authService: AuthServiceType - var userService: UserServiceType - - init() { - self.authService = DefaultAuthService() - self.userService = DefaultUserService() - } -} - -class StubService: ServiceType { - var authService: AuthServiceType = StubAuthService() - var userService: UserServiceType = StubUserService() -} - diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Example.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Example.swift deleted file mode 100644 index e69de29b..00000000 diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Base/.gitkeep b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Base/.gitkeep similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Base/.gitkeep rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Base/.gitkeep diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Base/AuthInterceptor.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Base/AuthInterceptor.swift similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Base/AuthInterceptor.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Base/AuthInterceptor.swift diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Base/BaseModel.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Base/BaseModel.swift similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Base/BaseModel.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Base/BaseModel.swift diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Base/BaseTargetType.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Base/BaseTargetType.swift similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Base/BaseTargetType.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Base/BaseTargetType.swift diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Base/NetworkProvider.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Base/NetworkProvider.swift similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Base/NetworkProvider.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Base/NetworkProvider.swift diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/.gitkeep b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Foundation/.gitkeep similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/.gitkeep rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Foundation/.gitkeep diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/Config.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Foundation/Config.swift similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/Config.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Foundation/Config.swift diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/MoyaLoggerPlugin.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Foundation/MoyaLoggerPlugin.swift similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/MoyaLoggerPlugin.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Foundation/MoyaLoggerPlugin.swift diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/NetworkHelper.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Foundation/NetworkHelper.swift similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/NetworkHelper.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Foundation/NetworkHelper.swift diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/NetworkResult.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Foundation/NetworkResult.swift similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/NetworkResult.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Foundation/NetworkResult.swift diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/ResponseData.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Foundation/ResponseData.swift similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/ResponseData.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Foundation/ResponseData.swift diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/.gitkeep b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/.gitkeep similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/.gitkeep rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/.gitkeep diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/AuthRouter.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/AuthRouter.swift similarity index 87% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/AuthRouter.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/AuthRouter.swift index 531e38a9..11778c72 100644 --- a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/AuthRouter.swift +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/AuthRouter.swift @@ -19,18 +19,20 @@ enum AuthRouter { } extension AuthRouter: BaseTargetType { - var headers: Parameters? { + + + var headers: [String : String]? { switch self { case .socialLogin: - return APIConstants.hasSocialTokenHeader + return APIHeaders.hasSocialTokenHeader case .signUp: - return APIConstants.signUpHeader + return APIHeaders.signUpHeader case .tokenRefresh: - return APIConstants.hasRefreshTokenHeader + return APIHeaders.hasRefreshTokenHeader case .revoke: - return APIConstants.hasTokenHeader + return APIHeaders.hasTokenHeader case .logout: - return APIConstants.hasTokenHeader + return APIHeaders.hasTokenHeader } } diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/ChallengeRouter.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/ChallengeRouter.swift similarity index 87% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/ChallengeRouter.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/ChallengeRouter.swift index b68d4f67..0c53536c 100644 --- a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/ChallengeRouter.swift +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/ChallengeRouter.swift @@ -24,19 +24,19 @@ extension ChallengeRouter: BaseTargetType { var headers: [String : String]? { switch self { case .createChallenge: - return APIConstants.hasTokenHeader + return APIHeaders.hasTokenHeader case .dailyChallengeFail : - return APIConstants.hasTokenHeader + return APIHeaders.hasTokenHeader case .getChallenge: - return APIConstants.hasTokenHeader + return APIHeaders.hasTokenHeader case .getdailyChallenge: - return APIConstants.hasTokenHeader + return APIHeaders.hasTokenHeader case .addApp: - return APIConstants.hasTokenHeader + return APIHeaders.hasTokenHeader case .deleteApp: - return APIConstants.hasTokenHeader + return APIHeaders.hasTokenHeader case .postDailyChallenge: - return APIConstants.hasTokenHeader + return APIHeaders.hasTokenHeader } } diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/MyPageRouter.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/MyPageRouter.swift similarity index 93% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/MyPageRouter.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/MyPageRouter.swift index 9ff31f5d..0f27dc37 100644 --- a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/MyPageRouter.swift +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/MyPageRouter.swift @@ -18,7 +18,7 @@ extension MyPageRouter: BaseTargetType { var headers: [String : String]? { switch self { case .getUserData: - return APIConstants.hasTokenHeader + return APIHeaders.hasTokenHeader } } diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/PointRouter.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/PointRouter.swift similarity index 86% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/PointRouter.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/PointRouter.swift index c5d03022..f36c337a 100644 --- a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Router/PointRouter.swift +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Router/PointRouter.swift @@ -23,17 +23,17 @@ extension PointRouter: BaseTargetType { var headers: [String : String]? { switch self { case .getUsagePoint: - return APIConstants.hasAccessTokenHeader + return APIHeaders.hasAccessTokenHeader case .patchEarnPoint : - return APIConstants.hasTokenHeader + return APIHeaders.hasTokenHeader case .getEarnPoint: - return APIConstants.hasAccessTokenHeader + return APIHeaders.hasAccessTokenHeader case .getPointList: - return APIConstants.hasAccessTokenHeader + return APIHeaders.hasAccessTokenHeader case .patchPointUse: - return APIConstants.hasAccessTokenHeader + return APIHeaders.hasAccessTokenHeader case .getCurrentPoint: - return APIConstants.hasAccessTokenHeader + return APIHeaders.hasAccessTokenHeader } } diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Service/.gitkeep b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Service/.gitkeep similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Service/.gitkeep rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Service/.gitkeep diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Service/Providers.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Service/Providers.swift similarity index 100% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Service/Providers.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Legacy/Service/Providers.swift diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Base/BaseAPI.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Base/BaseAPI.swift new file mode 100644 index 00000000..b7c493b5 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Base/BaseAPI.swift @@ -0,0 +1,23 @@ +// +// BaseAPI.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation + +protocol BaseAPI: URLRequestTargetType { } + +extension BaseAPI { + public var url: String { + return Config.baseURL + } + + public var headers: [String: String]? { + return APIHeaders.noTokenHeader + } +} + + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Base/BaseService.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Base/BaseService.swift new file mode 100644 index 00000000..9425ae7a --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Base/BaseService.swift @@ -0,0 +1,101 @@ +// +// BaseService.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +final class BaseService { + + typealias API = Target + + private let requestHandler = RequestHandler.shared + + private lazy var session: URLSession = { + let configuration = URLSessionConfiguration.default + configuration.timeoutIntervalForRequest = 10 + configuration.timeoutIntervalForResource = 10 + return URLSession(configuration: configuration) + }() + + func requestWithResult(_ target: API, _ responseType: T.Type) -> AnyPublisher { + return fetchResponse(with: target) + .flatMap { response in + self.validate(response: response) + .map { _ in response.data! } + .mapError { ErrorHandler.handleError(target, error: $0) } + } + .flatMap { self.decode(data: $0, target: target) } + .eraseToAnyPublisher() + } + + func requestWithNoResult(_ target: API) -> AnyPublisher { + return fetchResponse(with: target) + .flatMap { response -> AnyPublisher in + self.validate(response: response) // validate 연결 + .map { _ in response.data! } // 성공 시 data 반환 + .eraseToAnyPublisher() + } + .mapError { ErrorHandler.handleError(target, error: $0) } + .flatMap { data -> AnyPublisher in + self.decode(data: data, target: target) + } + .map { _ in () } + .eraseToAnyPublisher() + } +} + + +extension BaseService { + /// 네트워크 응답 처리 메소드 + private func fetchResponse(with target: API) -> AnyPublisher { + return requestHandler.executeRequest(for: target, isWithInterceptor: target.isWithInterceptor) + .handleEvents(receiveSubscription: { _ in + NetworkLogHandler.requestLogging(target) + }, receiveOutput: { response in + NetworkLogHandler.responseSuccess(target, result: response) + }) + .mapError { ErrorHandler.handleError(target, error: $0) } + .eraseToAnyPublisher() + } + + /// 응답 유효성 검사 메서드 + private func validate(response: NetworkResponse) -> AnyPublisher { + guard response.response.isValidateStatus() else { + if response.response.unAuthorized() { + RequestHandler.shared.tokenRequest() + } + let error = ErrorHandler.handleInvalidResponse(response: response) + return Fail(error: error).eraseToAnyPublisher() + } + + return Just(()).setFailureType(to: HMHNetworkError.self).eraseToAnyPublisher() + } + + + /// 디코딩 메소드 + private func decode(data: Data, target: API) -> AnyPublisher { + return Just(data) + .decode(type: GenericResponse.self, decoder: JSONDecoder()) + .mapError { _ in ErrorHandler.handleError(target, error: .decodingFailed(.failed)) } + .map { $0.data! } + .eraseToAnyPublisher() + } + +} + +// HTTP 상태코드 유효성 검사 +extension HTTPURLResponse { + func isValidateStatus() -> Bool { + return (200...299).contains(self.statusCode) + } + + func unAuthorized() -> Bool { + return self.statusCode == 401 + } +} + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Base/NetworkLogHandler.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Base/NetworkLogHandler.swift new file mode 100644 index 00000000..e38b0402 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Base/NetworkLogHandler.swift @@ -0,0 +1,62 @@ +// +// NetworkLogHandler.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation + +struct NetworkLogHandler { + + // 네트워크 요청 로깅 함수 + static func requestLogging(_ endpoint: URLRequestTargetType) { + let url = endpoint.url + (endpoint.path ?? "") + let method = endpoint.method.rawValue + let headers = endpoint.headers ?? [:] + let parameters = endpoint.task + + print(""" + ================== 📤 Request ===================> + 📝 URL: \(url) + 📝 HTTP Method: \(method) + 📝 Header: \(headers) + 📝 Parameters: \(parameters) + ================================ + """) + } + + // 성공적인 응답 로깅 함수 + static func responseSuccess(_ endpoint: any URLRequestTargetType, result response: NetworkResponse) { + let url = endpoint.url + (endpoint.path ?? "") + let headers = endpoint.headers ?? [:] + let responseData = String(data: response.data ?? Data(), encoding: .utf8) ?? "No data" + + print(""" + ======================== 📥 Response <======================== + ========================= ✅ Success ========================= + ✌🏻 URL: \(url) + ✌🏻 Header: \(headers) + ✌🏻 Success Data: \(responseData) + ============================================================== + """) + } + + // 에러 응답 로깅 함수 + static func responseError(_ endpoint: any URLRequestTargetType, result error: HMHNetworkError) { + let url = endpoint.url + (endpoint.path ?? "") + let headers = endpoint.headers ?? [:] + + print(""" + ======================== 📥 Response <======================== + ========================= ❌ Error ========================== + ❗️ Error Type: \(error.description) + ❗️ URL: \(url) + ❗️ Header: \(headers) + ❗️ Error Data: \(error.localizedDescription) + ============================================================== + """) + } +} + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Interceptor/TokenInterceptor.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Interceptor/TokenInterceptor.swift new file mode 100644 index 00000000..6977a262 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Interceptor/TokenInterceptor.swift @@ -0,0 +1,41 @@ +// +// TokenInterceptor.swift +// Networks +// +// Created by 류희재 on 10/28/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +struct TokenInterceptor { + + private var retryLimit = 2 + + static let shared = TokenInterceptor() + + private let reissueService: ReissueAPIService + + private init() { + self.reissueService = ReissueAPIService() + } + + + func adapt(_ request: URLRequest) -> AnyPublisher { + // 여기에 필요한 로직을 추가하여 request를 수정할 수 있습니다. + return Just(request) + .setFailureType(to: HMHNetworkError.self) // 성공 시 반환될 값의 타입 설정 + .eraseToAnyPublisher() // AnyPublisher로 반환 + } + + + func retry(for session: URLSession) -> AnyPublisher { + // 여기에 retry 로직을 추가합니다. + return Future { promise in + // retry 로직을 구현하여 promise를 성공 또는 실패로 완료합니다. + // 예: promise(.success(())) 또는 promise(.failure(error)) + } + .eraseToAnyPublisher() // AnyPublisher로 반환 + } +} diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Builder/RequestHandler.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Builder/RequestHandler.swift new file mode 100644 index 00000000..a9b06b87 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Builder/RequestHandler.swift @@ -0,0 +1,65 @@ +// +// RequestHandler.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +class RequestHandler { + + static let shared = RequestHandler() + + private init() {} + + private lazy var session: URLSession = { + let configuration = URLSessionConfiguration.default + configuration.timeoutIntervalForRequest = 10 + configuration.timeoutIntervalForResource = 10 + // TODO: Interceptor 추가 + return URLSession(configuration: configuration) + }() + + func executeRequest(for target: T, isWithInterceptor: Bool) -> AnyPublisher { + return target.asURLRequest() + .map { $0 } + .mapError { ErrorHandler.handleError(target, error: .invalidRequest($0)) } + .flatMap { urlRequest in + if isWithInterceptor { + return TokenInterceptor.shared.adapt(urlRequest) + } else { + return Just(urlRequest) + .setFailureType(to: HMHNetworkError.self) + .eraseToAnyPublisher() + } + } + .map { $0 } + .flatMap { urlRequest in + self.session.dataTaskPublisher(for: urlRequest) + .tryMap { data, response -> NetworkResponse in + guard let httpResponse = response as? HTTPURLResponse else { + throw HMHNetworkError.ResponseError.unhandled + } + return NetworkResponse(data: data, response: httpResponse, error: nil) + } + .mapError { error -> HMHNetworkError in + if let requestErr = error as? HMHNetworkError.ResponseError { + return .invalidResponse(requestErr) + } else { + return .unknown(error) + } + } + .eraseToAnyPublisher() + } + .eraseToAnyPublisher() + } + + func tokenRequest() { + TokenInterceptor.shared.retry(for: session) + } +} + + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Builder/URLRequestTargetType.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Builder/URLRequestTargetType.swift new file mode 100644 index 00000000..7b8ac9d5 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Builder/URLRequestTargetType.swift @@ -0,0 +1,35 @@ +// +// URLRequestTargetType.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +protocol URLRequestTargetType { + var url: String { get } + var path: String? { get } + var method: HTTPMethod { get } + var headers : [String : String]? { get } + var task: Task { get } + var isWithInterceptor: Bool { get } + + func asURLRequest() -> AnyPublisher +} + +extension URLRequestTargetType { + func asURLRequest() -> AnyPublisher { + guard let url = URL(string: self.url) else { + return Fail(error: .invalidURL(self.url)).eraseToAnyPublisher() + } + + var baseURL = url + if let path = self.path { baseURL.appendPathComponent(path) } + + return task.buildRequest(baseURL: baseURL, method: self.method, headers: self.headers) + } +} + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/APIConstants.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Config/APIHeaders.swift similarity index 88% rename from HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/APIConstants.swift rename to HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Config/APIHeaders.swift index 332aa9ef..64abee6d 100644 --- a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Foundation/APIConstants.swift +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Config/APIHeaders.swift @@ -1,5 +1,5 @@ // -// APIConstants.swift +// APIHeaders.swift // HMH_iOS // // Created by 지희의 MAC on 1/11/24. @@ -10,7 +10,7 @@ import Moya import Core -public struct APIConstants { +public struct APIHeaders { static let contentType = "Content-Type" static let applicationJSON = "application/json" static let auth = "Authorization" @@ -31,7 +31,11 @@ public struct APIConstants { static let iOS = "iOS" } -public extension APIConstants { +public extension APIHeaders { + static var noTokenHeader: Dictionary { + [contentType: applicationJSON] + } + static var hasSocialTokenHeader: [String: String] { return [contentType: applicationJSON, auth: appleAccessToken] diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Config/HTTPMethod.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Config/HTTPMethod.swift new file mode 100644 index 00000000..191f5b24 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Config/HTTPMethod.swift @@ -0,0 +1,20 @@ +// +// HTTPMethods.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation + +public typealias Parameters = [String: Any] + +@frozen public enum HTTPMethod: String { + case get = "GET" + case head = "HEAD" + case post = "POST" + case put = "PUT" + case patch = "PATCH" + case delete = "DELETE" +} diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Config/Paths.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Config/Paths.swift new file mode 100644 index 00000000..d18d2f86 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Config/Paths.swift @@ -0,0 +1,43 @@ +// +// Paths.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +public enum Paths { + + //MARK: - Auth + + static let signUp = "user/signup" + static let socialLogin = "user/login" + static let tokenRefresh = "user/reissue" + + //MARK: - User + + static let logout = "user/logout" + static let deleteAccount = "user" + static let getUserData = "/users" + static let getCurrentPoint = "user/point" + + //MARK: - Point + + static let getUsagePoint = "point/use" + static let patchEarnPoint = "point/earn" + static let getEarnPoint = "point/earn" + static let getPointList = "point/list" + static let patchPointUse = "point/use" + + //MARK: - Point + + static let createChallenge = "challenge" + static let dailyChallengeFail = "dailychallenge/failure" + static let getChallenge = "challenge" + static let getdailyChallenge = "challenge/home" + static let addApp = "challenge/app" + static let deleteApp = "challenge/app" + static let postDailyChallenge = "challenge/daily/success" + +} + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Config/Task.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Config/Task.swift new file mode 100644 index 00000000..555da10e --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Config/Task.swift @@ -0,0 +1,41 @@ +// +// Task.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +enum Task { + case requestPlain + case requestParameters(Parameters) + case requestJSONEncodable(Encodable) +} + +extension Task { + func buildRequest(baseURL: URL, method: HTTPMethod, headers: [String: String]?) -> AnyPublisher { + var request = URLRequest(url: baseURL) + request.httpMethod = method.rawValue + request.allHTTPHeaderFields = headers + + switch self { + case .requestPlain: + return Just(request) + .setFailureType(to: HMHNetworkError.RequestError.self) + .eraseToAnyPublisher() + + case .requestParameters(let parameters): + return URLEncoding().encode(request, with: parameters) + .mapError { .parameterEncodingFailed($0) } + .eraseToAnyPublisher() + + case .requestJSONEncodable(let encodable): + return JSONEncoding().encode(request, with: encodable) + .mapError { .parameterEncodingFailed($0) } + .eraseToAnyPublisher() + } + } +} diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Encoders/JSONEncoding.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Encoders/JSONEncoding.swift new file mode 100644 index 00000000..17afb3c5 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Encoders/JSONEncoding.swift @@ -0,0 +1,28 @@ +// +// JSONEncoding.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +public struct JSONEncoding: ParameterEncodable { + func encode(_ request: URLRequest, with parameters: Encodable?) -> AnyPublisher { + var request = request + return checkValidURLData(parameters, request.url) + .tryMap { parameters, _ -> URLRequest in + do { + let data = try JSONEncoder().encode(parameters) + request.httpBody = data + return request + } catch { + throw HMHNetworkError.invalidRequest(.parameterEncodingFailed(.jsonEncodingFailed)) + } + } + .mapError { $0 as! HMHNetworkError.ParameterEncoding } //TODO: 예외 상황이 없는거 같아서.. + .eraseToAnyPublisher() + } +} diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Encoders/ParameterEncodable.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Encoders/ParameterEncodable.swift new file mode 100644 index 00000000..3e876b4a --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Encoders/ParameterEncodable.swift @@ -0,0 +1,38 @@ +// +// ParameterEncoding.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +protocol ParameterEncodable {} + +extension ParameterEncodable { + func checkValidURLData( + _ parameters: Parameters?, + _ url: URL? + ) -> AnyPublisher<(Parameters, URL), HMHNetworkError.ParameterEncoding> { + guard let parameters else { return Fail(error: .emptyParameters).eraseToAnyPublisher() } + guard let url else { return Fail(error: .missingURL).eraseToAnyPublisher() } + + return Just((parameters, url)) + .setFailureType(to: HMHNetworkError.ParameterEncoding.self) + .eraseToAnyPublisher() + } + + func checkValidURLData( + _ parameters: Encodable?, + _ url: URL? + ) -> AnyPublisher<(Encodable, URL), HMHNetworkError.ParameterEncoding> { + guard let parameters else { return Fail(error: .emptyParameters).eraseToAnyPublisher() } + guard let url else { return Fail(error: .missingURL).eraseToAnyPublisher() } + + return Just((parameters, url)) + .setFailureType(to: HMHNetworkError.ParameterEncoding.self) + .eraseToAnyPublisher() + } +} diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Encoders/URLEncoding.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Encoders/URLEncoding.swift new file mode 100644 index 00000000..48487a27 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Request/Encoders/URLEncoding.swift @@ -0,0 +1,29 @@ +// +// URLEncoding.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +public struct URLEncoding: ParameterEncodable { + func encode(_ request: URLRequest, with parameters: Parameters?) -> AnyPublisher { + var request = request + + return checkValidURLData(parameters, request.url) + .map { parameters, url -> URLRequest in + if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) { + urlComponents.queryItems = parameters.compactMap { key, value in + URLQueryItem(name: key, value: "\(value)") + } + request.url = urlComponents.url + } + return request + } + .mapError { $0 } + .eraseToAnyPublisher() + } +} diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Error/ErrorHandler.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Error/ErrorHandler.swift new file mode 100644 index 00000000..8a50242d --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Error/ErrorHandler.swift @@ -0,0 +1,32 @@ +// +// ErrorHandler.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +struct ErrorHandler { + static func handleError(_ target: T, error: HMHNetworkError) -> HMHNetworkError { NetworkLogHandler.responseError(target, result: error) + return error + } + + // 유효하지 않은 응답인 경우 에러 처리 + static func handleInvalidResponse(response: NetworkResponse) -> HMHNetworkError { + if let data = response.data { + do { + // 에러 응답 모델로 디코딩 시도 + let errorResponse = try JSONDecoder().decode(ErrorResponse.self, from: data) + return .invalidResponse(.invalidStatusCode(code: response.response.statusCode, data: errorResponse.data)) + } catch { + return .invalidResponse(.invalidStatusCode(code: response.response.statusCode)) + } + } else { + return .invalidResponse(.invalidStatusCode(code: response.response.statusCode)) + } + } +} + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Error/HMHNetworkError.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Error/HMHNetworkError.swift new file mode 100644 index 00000000..f39357ee --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Error/HMHNetworkError.swift @@ -0,0 +1,126 @@ +// +// HMHNetworkError.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation + +@frozen public enum HMHNetworkError: Error { + case invalidRequest(RequestError) + case invalidResponse(ResponseError) + case decodingFailed(DecodeError) + case unknown(Error) + + var description: String { + switch self { + case .invalidRequest(let requestError): + return "요청 시 발생된" + requestError.description + case .invalidResponse(let responseError): + return "응답 시 발생된" + responseError.description + case .decodingFailed(let decodeError): + return decodeError.description + case .unknown(let error): + return "알 수 없는 오류 \(error)가 발생하였습니다!" + } + } +} + +extension HMHNetworkError { + public enum RequestError: Error { + case parameterEncodingFailed(ParameterEncoding) // 인코딩시 생기는 에러 + case invalidURL(String) // url이 유효하지 않을때 + case unknownErr // 그 외 예기치 못한 에러 + + var description: String { + switch self { + case .parameterEncodingFailed(let parameterEncoding): + return "인코딩 시 발생한" + parameterEncoding.description + case .invalidURL(let string): + return "\(string)은 유효한 url이 아닙니다" + case .unknownErr: + return "요청 시 발생한 알 수 없는 에러입니다." + } + } + } + + public enum ParameterEncoding: Error { + case emptyParameters // 파라미터가 비어있을 때 + case missingURL // url이 없을때 + case invalidJSON // json 형식에 맞지 않을때 + case jsonEncodingFailed // json으로 인코딩 할 시 + + var description: String { + switch self { + case .emptyParameters: + return "파라미터가 비어있는 에러입니다." + case .missingURL: + return "url이 없습니다" + case .invalidJSON: + return "json 형식에 맞지 않습니다." + case .jsonEncodingFailed: + return "json 인코딩 시 발생한 에러입니다." + } + } + } +} + +extension HMHNetworkError { + public enum ResponseError: Error { + case cancelled + case unhandled + case invalidStatusCode(code: Int, data: String? = nil) + + var description: String { + switch self { + case .cancelled: + return "취소되었습니다." + case .unhandled: + return "응답이 올바르지 않습니다" + case .invalidStatusCode(let code, let errMessage): + switch code { + case 401: + return "autheticationError: 인증오류입니다" + case 403: + return errMessage ?? "forbiddeError: 금지된 에러입니다" + case 404: + return errMessage ?? "notFoundError: 찾을 수 없습니다" + case 408: + return "timeoutError: 시간을 초과했습니다" + case 409: + return errMessage ?? "409 -> 해당 statuscode와 관련된 오류입니다" + case 500: + return "internalServerError: 서버 내부 오류입니다" + default: + return "\(code) -> 해당 statuscode와 관련된 오류입니다" + } + } + } + + var statusCode: Int? { + if case let .invalidStatusCode(code, _) = self { + return code + } + return nil + } + } +} + +extension HMHNetworkError { + public enum DecodeError: Error { + case failed + case dataIsNil + + var description: String { + switch self { + case .failed: + return "디코딩에 실패했습니다" + case .dataIsNil: + return "데이터가 존재하지 않습니다." + } + } + } +} + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Models/Decodable/ErrorResponse.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Models/Decodable/ErrorResponse.swift new file mode 100644 index 00000000..acf12575 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Models/Decodable/ErrorResponse.swift @@ -0,0 +1,29 @@ +// +// ErrorResponse.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation + +struct ErrorResponse: Decodable { + var statusCode: Int + var message: String + var data: String? + + enum CodingKeys: CodingKey { + case statusCode + case message + case data + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.statusCode = (try? container.decode(Int.self, forKey: .statusCode)) ?? 500 + self.message = (try? container.decode(String.self, forKey: .message)) ?? "" + self.data = try container.decodeIfPresent(String.self, forKey: .data) + } +} + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Models/Decodable/GenericResponse.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Models/Decodable/GenericResponse.swift new file mode 100644 index 00000000..a4c469d1 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Models/Decodable/GenericResponse.swift @@ -0,0 +1,28 @@ +// +// GenericResponse.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation + +struct GenericResponse: Decodable { + var statusCode: Int + var message: String + var data: T? + + enum CodingKeys: CodingKey { + case statusCode + case message + case data + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.statusCode = (try? container.decode(Int.self, forKey: .statusCode)) ?? 500 + self.message = (try? container.decode(String.self, forKey: .message)) ?? "" + self.data = try container.decodeIfPresent(T.self, forKey: .data) + } +} diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Models/Decodable/VoidResult.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Models/Decodable/VoidResult.swift new file mode 100644 index 00000000..d451f540 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Models/Decodable/VoidResult.swift @@ -0,0 +1,11 @@ +// +// VoidResult.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation + +struct VoidResult: Decodable {} diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Models/NetworkResponse.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Models/NetworkResponse.swift new file mode 100644 index 00000000..7d6437c7 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Foundation/Response/Models/NetworkResponse.swift @@ -0,0 +1,21 @@ +// +// NetworkResponse.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation + +public struct NetworkResponse { + public let data: Data? + public let response: HTTPURLResponse + public let error: Error? + + public init(data: Data?, response: HTTPURLResponse, error: Error?) { + self.data = data + self.response = response + self.error = error + } +} diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Auth/AuthAPI.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Auth/AuthAPI.swift new file mode 100644 index 00000000..17573868 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Auth/AuthAPI.swift @@ -0,0 +1,68 @@ +// +// AuthAPI.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation + +import Domain + +enum AuthAPI { + case signUp(data: SignUpRequestDTO) + case socialLogin(data: SocialLoginRequestDTO) + case tokeRefresh +} + +extension AuthAPI: BaseAPI { + var isWithInterceptor: Bool { + return false + } + + var path: String? { + switch self { + case .signUp: + return Paths.signUp + case .socialLogin: + return Paths.socialLogin + case .tokeRefresh: + return Paths.tokenRefresh + } + } + + var method: HTTPMethod { + switch self { + case .signUp: + return .post + case .socialLogin: + return .post + case .tokeRefresh: + return .post + } + } + + var task: Task { + switch self { + case .signUp(let data): + return .requestJSONEncodable(data) + case .socialLogin(let data): + return .requestJSONEncodable(data) + case .tokeRefresh: + return .requestPlain + } + } + + var headers: [String : String]? { + switch self { + case .signUp: + return APIHeaders.signUpHeader + case .socialLogin: + return APIHeaders.hasSocialTokenHeader + case .tokeRefresh: + return APIHeaders.hasRefreshTokenHeader + } + } +} + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Auth/AuthService.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Auth/AuthService.swift new file mode 100644 index 00000000..ccfafa87 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Auth/AuthService.swift @@ -0,0 +1,28 @@ +// +// AuthService.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +typealias AuthService = BaseService + +protocol AuthServiceType { + +} + +extension AuthService: AuthServiceType { + + +} + +struct StubAuthService: AuthServiceType { + +} + + + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Auth/ReissueAPIService.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Auth/ReissueAPIService.swift new file mode 100644 index 00000000..21a8e5bb --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Auth/ReissueAPIService.swift @@ -0,0 +1,25 @@ +// +// ReissueService.swift +// Networks +// +// Created by 류희재 on 10/28/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +typealias ReissueAPIService = BaseService + +protocol ReissueAPIServiceType { + +} + +extension ReissueAPIService: ReissueAPIServiceType { + + +} + +struct StubReissueAPIService: ReissueAPIServiceType { + +} diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Challenge/ChallengeAPI.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Challenge/ChallengeAPI.swift new file mode 100644 index 00000000..eedd208c --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Challenge/ChallengeAPI.swift @@ -0,0 +1,104 @@ +// +// ChallengeAPI.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation + +import Domain + +enum ChallengeAPI { + case createChallenge(data: CreateChallengeRequestDTO) + case dailyChallengeFail + case getChallenge + case getdailyChallenge + case addApp(data: AddAppRequestDTO) + case deleteApp(data: DeleteAppRequestDTO) + case postDailyChallenge(data: MidnightRequestDTO) +} + +extension ChallengeAPI: BaseAPI { + var isWithInterceptor: Bool { + return true + } + + var path: String? { + switch self { + case .createChallenge: + return Paths.createChallenge + case .dailyChallengeFail: + return Paths.dailyChallengeFail + case .getChallenge: + return Paths.getChallenge + case .getdailyChallenge: + return Paths.getChallenge + case .addApp: + return Paths.addApp + case .deleteApp: + return Paths.deleteApp + case .postDailyChallenge: + return Paths.postDailyChallenge + } + } + + var method: HTTPMethod { + switch self { + case .createChallenge: + return .post + case .dailyChallengeFail: + return .patch + case .getChallenge: + return .get + case .getdailyChallenge: + return .get + case .addApp: + return .post + case .deleteApp: + return .delete + case .postDailyChallenge: + return .post + } + } + + var task: Task { + switch self { + case .createChallenge(let data): + return .requestJSONEncodable(data) + case .dailyChallengeFail: + return .requestPlain + case .getChallenge: + return .requestPlain + case .getdailyChallenge: + return .requestPlain + case .addApp(let data): + return .requestJSONEncodable(data) + case .deleteApp(let data): + return .requestJSONEncodable(data) + case .postDailyChallenge(let data): + return .requestJSONEncodable(data) + } + } + + var headers: [String : String]? { + switch self { + case .createChallenge: + return APIHeaders.hasTokenHeader + case .dailyChallengeFail : + return APIHeaders.hasTokenHeader + case .getChallenge: + return APIHeaders.hasTokenHeader + case .getdailyChallenge: + return APIHeaders.hasTokenHeader + case .addApp: + return APIHeaders.hasTokenHeader + case .deleteApp: + return APIHeaders.hasTokenHeader + case .postDailyChallenge: + return APIHeaders.hasTokenHeader + } + } +} + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Challenge/ChallengeService.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Challenge/ChallengeService.swift new file mode 100644 index 00000000..dfc3d266 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Challenge/ChallengeService.swift @@ -0,0 +1,27 @@ +// +// ChallengeService.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +typealias ChallengeService = BaseService + +protocol ChallengeServiceType { + +} + +extension ChallengeService: ChallengeServiceType { + + +} + +struct StubChallengeService: ChallengeServiceType { + +} + + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Point/PointAPI.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Point/PointAPI.swift new file mode 100644 index 00000000..736b9689 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Point/PointAPI.swift @@ -0,0 +1,79 @@ +// +// PointAPI.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation + +import Domain + +enum PointAPI { + case getUsagePoint + case patchEarnPoint(data: PointRequestDTO) + case getEarnPoint + case getPointList + case patchPointUse(data: PointRequestDTO) +} + +extension PointAPI: BaseAPI { + var isWithInterceptor: Bool { + return false + } + + var path: String? { + switch self { + case .getUsagePoint: + return Paths.getUsagePoint + case .patchEarnPoint(data: let data): + return Paths.patchEarnPoint + case .getEarnPoint: + return Paths.getEarnPoint + case .getPointList: + return Paths.getPointList + case .patchPointUse: + return Paths.patchPointUse + } + } + + var method: HTTPMethod { + switch self { + case .getUsagePoint: + return .get + case .patchEarnPoint: + return .patch + case .getEarnPoint: + return .get + case .getPointList: + return .get + case .patchPointUse: + return .patch + } + } + + var task: Task { + switch self { + case .getUsagePoint: + return .requestPlain + case .patchEarnPoint(data: let data): + return .requestJSONEncodable(data) + case .getEarnPoint: + return .requestPlain + case .getPointList: + return .requestPlain + case .patchPointUse(data: let data): + return .requestJSONEncodable(data) + } + } + + var headers: [String : String]? { + switch self { + case .getEarnPoint: + return APIHeaders.hasTokenHeader + default: + return APIHeaders.hasAccessTokenHeader + } + } +} diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Point/PointService.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Point/PointService.swift new file mode 100644 index 00000000..e1ef8953 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/Point/PointService.swift @@ -0,0 +1,39 @@ +// +// PointService.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +typealias PointService = BaseService + +protocol PointServiceType { + func getUsagePoint() + func patchEarnPoint(challengeDate: String) + func getEarnPoint() + func getPointList() + func patchPointUse(challengeDate: String) +} + +extension PointService: PointServiceType { + func getUsagePoint() {} + func patchEarnPoint(challengeDate: String) {} + func getEarnPoint() {} + func getPointList() {} + func patchPointUse(challengeDate: String) {} + +} + +struct StubPointServicee: PointServiceType { + func getUsagePoint() {} + func patchEarnPoint(challengeDate: String) {} + func getEarnPoint() {} + func getPointList() {} + func patchPointUse(challengeDate: String) {} +} + + diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/User/UserAPI.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/User/UserAPI.swift new file mode 100644 index 00000000..ad07fae0 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/User/UserAPI.swift @@ -0,0 +1,76 @@ +// +// UserAPI.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation + +import Domain + +enum UserAPI { + case logout + case deleteAccount + case getUserData + case getCurrentPoint +} + +extension UserAPI: BaseAPI { + var isWithInterceptor: Bool { + return false + } + + var path: String? { + switch self { + case .logout: + return Paths.logout + case .deleteAccount: + return Paths.deleteAccount + case .getUserData: + return Paths.getUserData + case .getCurrentPoint: + return Paths.getCurrentPoint + } + } + + var method: HTTPMethod { + switch self { + case .logout: + return .post + case .deleteAccount: + return .delete + case .getUserData: + return .get + case .getCurrentPoint: + return .get + } + } + + var task: Task { + switch self { + case .logout: + return .requestPlain + case .deleteAccount: + return .requestPlain + case .getUserData: + return .requestPlain + case .getCurrentPoint: + return .requestPlain + } + } + + var headers: [String : String]? { + switch self { + case .logout: + return APIHeaders.hasTokenHeader + case .deleteAccount: + return APIHeaders.hasTokenHeader + case .getUserData: + return APIHeaders.hasTokenHeader + case .getCurrentPoint: + return APIHeaders.hasAccessTokenHeader + } + } +} diff --git a/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/User/UserService.swift b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/User/UserService.swift new file mode 100644 index 00000000..1537d4a7 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Modules/Networks/Sources/Refactor/Service/User/UserService.swift @@ -0,0 +1,26 @@ +// +// UserService.swift +// Networks +// +// Created by 류희재 on 10/14/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +typealias UserService = BaseService + +protocol UserServiceType { + +} + +extension UserService: UserServiceType { + + +} + +struct StubUserService: UserServiceType { + +} + diff --git a/HMH_iOS/HMH_iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/HMH_iOS/HMH_iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0f2a0462..47818748 100644 --- a/HMH_iOS/HMH_iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/HMH_iOS/HMH_iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,7 +1,5 @@ { - - "originHash" : "121817b8ddbac77685ab40e17f7a00d795dff609a9d68288c102f550e0f9779d", - + "originHash" : "511b5126f44782a39b3141b0c74c3caf5444c7925e20b25f53462ca6803f0925", "pins" : [ { "identity" : "alamofire", @@ -84,24 +82,6 @@ "version" : "6.7.0" } }, - { - "identity" : "realm-core", - "kind" : "remoteSourceControl", - "location" : "https://github.com/realm/realm-core.git", - "state" : { - "revision" : "9cf7ef4ad8e2f4c7a519c9a395ca3d253bb87aa8", - "version" : "14.6.2" - } - }, - { - "identity" : "realm-swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/realm/realm-swift", - "state" : { - "branch" : "master", - "revision" : "21ac01d5ae30dcc156e0e43e47c4afe39cc0367f" - } - }, { "identity" : "rxswift", "kind" : "remoteSourceControl",