Skip to content

Commit

Permalink
[Merge] #60 - pull request #62 from Team-Wable/network/#60
Browse files Browse the repository at this point in the history
Network [#60] CombineMoya 를 활용한 네트워크 통신
  • Loading branch information
JinUng41 authored Nov 16, 2024
2 parents f6e88c7 + 6740456 commit ebe5b34
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 53 deletions.
10 changes: 10 additions & 0 deletions Wable-iOS/Global/Extention/Publisher+UIControl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,13 @@ struct UIControlPublisher<Control: UIControl>: Publisher {
subscriber.receive(subscription: subscription)
}
}

extension Publisher {
/// 네트워크 통신 후 뷰 모델에서 에러를 단순히 출력할 때 사용합니다.
func mapWableNetworkError() -> Publishers.MapError<Self, Failure> where Failure == BaseAPI.WableNetworkError {
self.mapError { error in
Swift.print("\(error)")
return error
}
}
}
71 changes: 71 additions & 0 deletions Wable-iOS/Network/Foundation/BaseAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,74 @@ class BaseAPI {
}
}
}

extension BaseAPI {
enum WableNetworkError: Error {
/// 요청 오류
case requestError(String)
/// 디코딩 오류
case decodedError(String)
/// 경로 오류
case pathError
/// 서버 내부 오류
case serverError
/// 네트워크 실패
case networkFail
/// 토큰 인증 오류
case authorizationFail(String, Int)
/// 알 수 없는 에러
case unknownError(String)
}

// TODO: 프로토콜로 추상화 가능

func parseResponse<T: Codable>(statusCode: Int, data: Data) throws -> T? {
let baseResponse = try decodeResponse(with: BaseResponse<T>.self, from: data)
return try handleStatusCode(statusCode, with: baseResponse)
}

private func decodeResponse<T:Codable>(with baseResonse: BaseResponse<T>.Type, from data: Data) throws -> BaseResponse<T> {
do {
let decodedData = try JSONDecoder().decode(baseResonse, from: data)
return decodedData
} catch {
throw WableNetworkError.decodedError("\(error)")
}
}

private func handleStatusCode<T: Codable>(_ statusCode: Int, with baseResponse: BaseResponse<T>) throws -> T? {
switch statusCode {
case 200..<300:
return baseResponse.data
case 401:
throw WableNetworkError.authorizationFail(baseResponse.message, baseResponse.status)
case 400..<500:
throw WableNetworkError.requestError(baseResponse.message)
case 500...:
throw WableNetworkError.serverError
default:
throw WableNetworkError.networkFail
}
}
}

extension BaseAPI.WableNetworkError: CustomStringConvertible {
var description: String {
switch self {
case .requestError(let message):
return "‼️ 요청 에러 발생: \(message) ‼️"
case .decodedError(let message):
return "‼️ 디코딩 에러 발생: \(message) ‼️"
case .pathError:
return "‼️ 경로 에러 발생 ‼️"
case .serverError:
return "‼️ 서버 내부 에러 발생 ‼️"
case .networkFail:
return "‼️ 네트워크 실패! ‼️"
case .authorizationFail(_, _):
return "‼️ 권한이 없네요. ‼️"
case .unknownError(let message):
return "‼️ 알 수 없는 에러: \(message) ‼️"
}
}
}
2 changes: 2 additions & 0 deletions Wable-iOS/Network/Foundation/URLSession/Empty.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ struct EmptyBody: Encodable {
struct EmptyResponse: Decodable {

}

struct EmptyDTO: Codable {}
20 changes: 11 additions & 9 deletions Wable-iOS/Network/Info/InfoAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,24 @@
//

import Foundation
import Combine

import CombineMoya
import Moya

final class InfoAPI: BaseAPI {
static let shared = InfoAPI()
private var infoProvider = MoyaProvider<InfoRouter>(plugins: [MoyaLoggingPlugin()])
private override init() {}
private var infoProvider = MoyaProvider<InfoRouter>(plugins: [MoyaLoggingPlugin()])
private override init() {}
}

extension InfoAPI {
func getMatchInfo(completion: @escaping (NetworkResult<Any>) -> Void) {
infoProvider.request(.getMatchInfo) { result in
self.disposeNetwork(result,
dataModel: [TodayMatchesDTO].self,
completion: completion)

}
func getMatchInfo() -> AnyPublisher<[TodayMatchesDTO]?, WableNetworkError> {
infoProvider.requestPublisher(.getMatchInfo)
.tryMap { [weak self] response -> [TodayMatchesDTO]? in
return try self?.parseResponse(statusCode: response.statusCode, data: response.data)
}
.mapError { $0 as? WableNetworkError ?? .unknownError($0.localizedDescription) }
.eraseToAnyPublisher()
}
}
53 changes: 9 additions & 44 deletions Wable-iOS/Presentation/Info/ViewModels/InfoMatchViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,17 @@ final class InfoMatchViewModel: ViewModelType {
}

func transform(from input: Input, cancelBag: CancelBag) -> Output {
let matchInfoSubject = CurrentValueSubject<[TodayMatchesDTO], Never>([])

input.viewWillAppear
.sink { _ in
InfoAPI.shared.getMatchInfo { [weak self] result in
guard let result = self?.validateResult(result) as? [TodayMatchesDTO] else { return }
let formattedResult = self?.processGameSchedules(result)
matchInfoSubject.send(formattedResult ?? [])
}
let matchInfo = input.viewWillAppear
.flatMap { _ -> AnyPublisher<[TodayMatchesDTO], Never> in
return InfoAPI.shared.getMatchInfo()
.compactMap { $0 }
.mapWableNetworkError()
.replaceError(with: [])
.eraseToAnyPublisher()
}
.store(in: cancelBag)
.eraseToAnyPublisher()

return Output(matchInfo: matchInfoSubject.eraseToAnyPublisher())
return Output(matchInfo: matchInfo)
}
}

Expand All @@ -43,36 +41,3 @@ extension InfoMatchViewModel {
return dateString == dateFormatter.string(from: Date())
}
}

// MARK: - Private Method

private extension InfoMatchViewModel {
func validateResult(_ result: NetworkResult<Any>) -> Any?{
switch result{
case .success(let data):
return data
case .requestErr(let message):
print(message)
case .pathErr:
print("path 혹은 method 오류입니다.🤯")
case .serverErr:
print("서버 내 오류입니다.🎯")
case .networkFail:
print("네트워크가 불안정합니다.💡")
case .decodedErr:
print("디코딩 오류가 발생했습니다.🕹️")
case .authorizationFail(_):
print("인증 오류가 발생했습니다. 다시 로그인해주세요🔐")
}
return nil
}

func processGameSchedules(_ schedules: [TodayMatchesDTO]) -> [TodayMatchesDTO] {
var formattedSchedules = schedules
for i in 0..<formattedSchedules.count {
formattedSchedules[i].formatDate()
}

return formattedSchedules
}
}

0 comments on commit ebe5b34

Please sign in to comment.