From 5e43b23ed45db728f3e6b4814801937a9ccf4e98 Mon Sep 17 00:00:00 2001 From: jeonseonghun Date: Thu, 9 May 2024 23:52:53 +0900 Subject: [PATCH] =?UTF-8?q?[Feat]:=20Infra=20=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AVIRO.xcodeproj/project.pbxproj | 6 +- .../DataTransferErrorLogger.swift | 17 ++ .../DataTransfer/DataTransferService.swift | 163 ++++++++++++++++++ 3 files changed, 185 insertions(+), 1 deletion(-) diff --git a/AVIRO.xcodeproj/project.pbxproj b/AVIRO.xcodeproj/project.pbxproj index 17e04d84..0f36bbf9 100644 --- a/AVIRO.xcodeproj/project.pbxproj +++ b/AVIRO.xcodeproj/project.pbxproj @@ -201,6 +201,7 @@ C5BA9DF22BECC8F9004FA06E /* NetworkSessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5BA9DF12BECC8F9004FA06E /* NetworkSessionManager.swift */; }; C5BA9DF42BECC902004FA06E /* NetworkErrorLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5BA9DF32BECC902004FA06E /* NetworkErrorLogger.swift */; }; C5BA9DF62BECCD58004FA06E /* DataTransferError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5BA9DF52BECCD58004FA06E /* DataTransferError.swift */; }; + C5BA9DFB2BED136A004FA06E /* DataTransferErrorLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5BA9DFA2BED136A004FA06E /* DataTransferErrorLogger.swift */; }; C5C20BAC2AD504D500855BBB /* AmplitudeUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5C20BAB2AD504D500855BBB /* AmplitudeUtility.swift */; }; C5C290742A85E4EC00ED5DCE /* PlaceSegmentedControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5C290732A85E4EC00ED5DCE /* PlaceSegmentedControlView.swift */; }; C5C290772A85E4FF00ED5DCE /* PlaceHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5C290762A85E4FF00ED5DCE /* PlaceHomeView.swift */; }; @@ -493,6 +494,7 @@ C5BA9DF12BECC8F9004FA06E /* NetworkSessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSessionManager.swift; sourceTree = ""; }; C5BA9DF32BECC902004FA06E /* NetworkErrorLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkErrorLogger.swift; sourceTree = ""; }; C5BA9DF52BECCD58004FA06E /* DataTransferError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTransferError.swift; sourceTree = ""; }; + C5BA9DFA2BED136A004FA06E /* DataTransferErrorLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTransferErrorLogger.swift; sourceTree = ""; }; C5C20BAB2AD504D500855BBB /* AmplitudeUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmplitudeUtility.swift; sourceTree = ""; }; C5C290732A85E4EC00ED5DCE /* PlaceSegmentedControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceSegmentedControlView.swift; sourceTree = ""; }; C5C290762A85E4FF00ED5DCE /* PlaceHomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceHomeView.swift; sourceTree = ""; }; @@ -1703,7 +1705,6 @@ children = ( C5BA9DD32BE8B39B004FA06E /* RequestTable.swift */, C5BA9DD12BE8AF80004FA06E /* BodyEncoder.swift */, - C5BA9DD52BE8B884004FA06E /* ResponseDecoder.swift */, ); path = Response_Request; sourceTree = ""; @@ -1724,7 +1725,9 @@ C5BA9DF72BECCDB5004FA06E /* DataTransfer */ = { isa = PBXGroup; children = ( + C5BA9DD52BE8B884004FA06E /* ResponseDecoder.swift */, C5BA9DF52BECCD58004FA06E /* DataTransferError.swift */, + C5BA9DFA2BED136A004FA06E /* DataTransferErrorLogger.swift */, C5BA9DC82BE8AB8F004FA06E /* DataTransferService.swift */, ); path = DataTransfer; @@ -2381,6 +2384,7 @@ C58FBF902AC2B83A00AB6EFC /* AVIROAPIManagerProtocol.swift in Sources */, C5DCBC9F2B91D9FE003502BA /* MyCommentListViewModel.swift in Sources */, C50F6AF52A68FD6300E942F5 /* EnrollPlacePresenter.swift in Sources */, + C5BA9DFB2BED136A004FA06E /* DataTransferErrorLogger.swift in Sources */, C50DB7572A9B8DF80078B501 /* EditLocationAddressTextView.swift in Sources */, C595ACD12A84A4EB00D35123 /* PlaceSummaryView.swift in Sources */, C520663A2A808DC40038ECCD /* PlaceListHeaderView.swift in Sources */, diff --git a/AVIRO/Infrastructure/Network/DataTransfer/DataTransferErrorLogger.swift b/AVIRO/Infrastructure/Network/DataTransfer/DataTransferErrorLogger.swift index b4fed283..4415e525 100644 --- a/AVIRO/Infrastructure/Network/DataTransfer/DataTransferErrorLogger.swift +++ b/AVIRO/Infrastructure/Network/DataTransfer/DataTransferErrorLogger.swift @@ -6,3 +6,20 @@ // import Foundation + +protocol DataTransferErrorLoggerProtocol { + func log(error: Error) +} + +final class DataTransferErrorLogger: DataTransferErrorLoggerProtocol { + func log(error: Error) { + printIfDebug("------------") + printIfDebug("\(error)") + } +} + +private func printIfDebug(_ string: String) { + #if DEBUG + print(string) + #endif +} diff --git a/AVIRO/Infrastructure/Network/DataTransfer/DataTransferService.swift b/AVIRO/Infrastructure/Network/DataTransfer/DataTransferService.swift index 545f21ae..e6990c73 100644 --- a/AVIRO/Infrastructure/Network/DataTransfer/DataTransferService.swift +++ b/AVIRO/Infrastructure/Network/DataTransfer/DataTransferService.swift @@ -6,3 +6,166 @@ // import Foundation + +protocol DataTransferDispatchQueue { + func asyncExecute(work: @escaping () -> Void) +} + +extension DispatchQueue: DataTransferDispatchQueue { + func asyncExecute(work: @escaping () -> Void) { + async(group: nil, execute: work) + } +} + +protocol DataTransferServiceProtocol { + typealias CompletionHandler = (Result) -> Void + + @discardableResult + func request( + with endpoint: E, + on queue: DataTransferDispatchQueue, + completion: @escaping CompletionHandler + ) -> NetworkCancellable? where E.Response == T + + @discardableResult + func request( + with endpoint: E, + completion: @escaping CompletionHandler + ) -> NetworkCancellable? where E.Response == T + + @discardableResult + func request( + with endpoint: E, + on queue: DataTransferDispatchQueue, + completion: @escaping CompletionHandler + ) -> NetworkCancellable? where E.Response == Void + + @discardableResult + func request( + with endpoint: E, + completion: @escaping CompletionHandler + ) -> NetworkCancellable? where E.Response == Void +} + +protocol DataTransferErrorResolverProtocol { + func resolve(error: NetworkError) -> Error +} + +final class DataTransferService { + private let networkService: NetworkService + private let errorResolver: DataTransferErrorResolverProtocol + private let errorLogger: DataTransferErrorLoggerProtocol + + init( + networkService: NetworkService, + errorResolver: DataTransferErrorResolverProtocol, + errorLogger: DataTransferErrorLoggerProtocol + ) { + self.networkService = networkService + self.errorResolver = errorResolver + self.errorLogger = errorLogger + } +} + +extension DataTransferService: DataTransferServiceProtocol { + func request( + with endpoint: E, + on queue: any DataTransferDispatchQueue, + completion: @escaping CompletionHandler + ) -> NetworkCancellable? where T == E.Response { + networkService.request(endPoint: endpoint) { result in + switch result { + case .success(let data): + let result: Result = self.decode( + data: data, + decoder: endpoint.responseDecoder + ) + + queue.asyncExecute { + completion(result) + } + + case .failure(let error): + self.errorLogger.log(error: error) + + let error = self.resolve(networkError: error) + + queue.asyncExecute { + completion(.failure(error)) + } + } + } + } + + func request( + with endpoint: E, + completion: @escaping CompletionHandler + ) -> NetworkCancellable? where T == E.Response { + request( + with: endpoint, + on: DispatchQueue.main, + completion: completion + ) + } + + func request( + with endpoint: E, + on queue: any DataTransferDispatchQueue, + completion: @escaping CompletionHandler + ) -> NetworkCancellable? where E.Response == Void { + networkService.request(endPoint: endpoint) { result in + switch result { + case .success: + queue.asyncExecute { + completion(.success(())) + } + case .failure(let error): + self.errorLogger.log(error: error) + let error = self.resolve(networkError: error) + + queue.asyncExecute { + completion(.failure(error)) + } + } + } + } + + func request( + with endpoint: E, + completion: @escaping CompletionHandler + ) -> NetworkCancellable? where E.Response == Void { + request(with: endpoint, on: DispatchQueue.main, completion: completion) + } + + private func decode( + data: Data?, + decoder: ResponseDecoder + ) -> Result { + do { + guard let data = data else { return .failure(.noResponse) } + + let result: T = try decoder.decode(data) + + return .success(result) + } catch { + self.errorLogger.log(error: error) + + return .failure(.parsing(error)) + } + } + + private func resolve( + networkError error: NetworkError + ) -> DataTransferError { + let resolvedError = self.errorResolver.resolve(error: error) + + return resolvedError is NetworkError ? + .networkFailure(error) : .resolveNetworkFailure(resolvedError) + } +} + +final class DataTransferErrorResolver: DataTransferErrorResolverProtocol { + func resolve(error: NetworkError) -> any Error { + return error + } +}