프로젝트 개선 및 리팩토링 사항
통신: Moya
struct BaseResponseModel<Decode: Decodable>: Decodable {
let status: Int
let data: Decode?
let success: Bool
let message: String?
}
- 데이터 통신 분기처리를 위한 모델 생성
final class APIService {
static let shared = APIService()
private init() {}
private let provider = MoyaProvider<APITarget>()
func requestCityActivity(cityID: Int, completion: @escaping (Result<CityActivityResponseModel, Error>) -> Void) {
provider.request(.cityActivity(cityID: cityID)) { result in
switch result {
case let .success(success):
let responseData = success.data
do {
let decoded = try JSONDecoder().decode(CityActivityResponseModel.self, from: responseData)
completion(.success(decoded))
} catch {
completion(.failure(error))
}
case let .failure(error):
completion(.failure(error))
}
}
}
}
- Activity Data 통신을 위한 APIService 메소드 정의
typealias CityActivityResponseModel = BaseResponseModel<[_CityActivityResponseModel]>
- typealias를 이용한 제네릭 모델 별칭 지정, 코드 간소화 및 가독성 개선
@escaping (Result<CityActivityResponseModel, Error>)
- Generic 부분 추가 설명 , 하나 이상의 타입 인자를 사용할 수 있으며, 꺽쇠 안에서 타입 인자 이름을 콤마로 분리하여 작성한다. (ex. <T, M>)
- Result 의 원래 인자는 <Success, Failure>
public protocol TargetType {
/// The target's base `URL`.
var baseURL: URL { get }
/// The path to be appended to `baseURL` to form the full `URL`.
var path: String { get }
/// The HTTP method used in the request.
var method: Moya.Method { get }
/// Provides stub data for use in testing.
var sampleData: Data { get }
/// The type of HTTP task to be performed.
var task: Task { get }
/// The type of validation to perform on the request. Default is `.none`.
var validationType: ValidationType { get }
/// The headers to be used in the request.
var headers: [String: String]? { get }
}
public extension TargetType {
/// The type of validation to perform on the request. Default is `.none`.
var validationType: ValidationType {
return .none
}
}
-
Moya의 TargetType protocol 을 채택하고 준수하여 열거형으로 APITarget구현
// import Moya enum APITarget: TargetType { case medianHotelRead(cityID: Int, subCategory: Int) case cityActivity(cityID: Int) case tripCreate(cityID: Int, body: TripCreateRequestModel) case medianFoodRead(cityID : Int) var baseURL: URL { return URL(string: "http://13.125.42.117:3000")! } var path: String { switch self { case let .medianHotelRead(cityID, subCategory): return "/median/\(cityID)/\(subCategory)/hoteliOS/" case let .cityActivity(cityID): return "/citys/\(cityID)/Activity" case let .tripCreate(cityID, _): return "/trips/\(cityID)" case let .medianFoodRead(cityID): return "/median/\(cityID)/food" } } var method: Method { switch self { case .medianHotelRead: return .get case .cityActivity: return .get case .tripCreate: return .post case .medianFoodRead: return .get } // case. sendA: // return .post } var sampleData: Data { return .init() } var task: Task { switch self { case .medianHotelRead: return .requestPlain case .cityActivity: return .requestPlain case let .tripCreate(cityID, body): let encoded = try! JSONEncoder().encode(body) return .requestCompositeData(bodyData: encoded, urlParameters: ["CityId": cityID]) case let .medianFoodRead(cityID): return .requestPlain } } var headers: [String : String]? { return ["Content-Type": "application/json"] } }
enum APITarget: TargetType {
case medianHotelRead(cityID: Int, subCategory: Int)
case cityActivity(cityID: Int)
case tripCreate(cityID: Int, body: TripCreateRequestModel)
case medianFoodRead(cityID : Int)
var path: String {
switch self {
case let .cityActivity(cityID):
return "/citys/\(cityID)/Activity"
}
}
func requestCityActivity(cityID: Int, completion: @escaping (Result<CityActivityResponseModel, Error>) -> Void) {
provider.request(.cityActivity(cityID: cityID))
- provider.request부분에서 .cityActivity부분이 APItarget을 사용 하는부분
- provider.request( APITarget.cityActivity(cityID: cityID)) 축약형으로 표현
override func viewDidAppear(_ animated: Bool) {
let cityID = 1
APIService.shared.requestCityActivity(cityID: cityID) { [weak self] result in
switch result {
case let .success(success):
guard let data = success.data else { return }
self?.responseModel = data
case let .failure(error):
print(error.localizedDescription)
}
}
}
- viewDidAppear ViewLifeCycle에서 APIService의 분기별로 처리해 놓은 (싱클턴패턴) 정의 해놨던 request 함수호출
- To do 이미지캐싱