//Moya 처럼 사용 가능하게 만들기위해
typealias HTTPHeaders = [String : String]
protocol TargetType {
var baseURL: String { get }
var headers : HTTPHeaders { get }
var method: HTTPMethod { get }
var path: String { get }
var parameters: RequestParams { get }
}
enum HTTPMethod : String{
case GET = "GET"
case POST = "POST"
}
//요청은 Json으로 만들기 때문에 Encodeable
enum RequestParams {
case query(_ parameter: Encodable?)
case body(_ parameter: Encodable?)
}
Moya에서 사용하던것과 똑같게 만들기위해 필요한 파라미터들을 protocol로 만들어 줍니다 다양한 method가 필요하다면
추가해주시면됩니다.
그럼 프로토콜로 만들어준것을 urlrequest로 만들기위해 asURLRequest() 함수를 만들어 줍니다.
extension TargetType {
// URLRequestConvertible 구현
func asURLRequest() throws -> URLRequest {
guard let url = URL(string: baseURL)?.appendingPathComponent(path) else {
throw AsURLRequestError.URLError
}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = method.rawValue
for header in headers {
urlRequest.addValue(header.value, forHTTPHeaderField: header.key)
}
switch parameters {
case .query(let request):
let params = request?.toDictionary ?? [:]
let queryParams = params.map { URLQueryItem(name: $0.key, value: "\($0.value)") }
var components = URLComponents(string: url.appendingPathComponent(path).absoluteString)
components?.queryItems = queryParams
urlRequest.url = components?.url
case .body(let request):
let params = request?.toDictionary ?? [:]
urlRequest.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
}
return urlRequest
}
}
enum AsURLRequestError : Error{
case URLError
}
지난번 포스팅에서 말한것처럼 throw를 이용해 에러를 핸들링을 해준다 지금은 단순히 URL을 파싱하지 못하였을때 생기는 오류만을 가정하여 Error을 만들었는데 필요에따라 추가적으로 Error를 만들어 사용해주면된다.
사용방법은 간단하다
Moya를 사용하듯이 enum의 API 의 리스트들을 정의해주고
TargetType 을 채택하여 기본값을 셋팅해준다.
enum AppStoreApi {
case SERARCH_RECOMEND_APP(term : String)
}
extension AppStoreApi : TargetType{
var baseURL: String {
return "https://itunes.apple.com"
}
var headers : HTTPHeaders {
return [:]
}
var method: HTTPMethod {
return .GET
}
var path: String {
switch self {
case .SERARCH_RECOMEND_APP :
return "/search"
}
}
var parameters: RequestParams {
switch self{
case .SERARCH_RECOMEND_APP(let term) :
let param : SearchRequestModel = SearchRequestModel(term : term)
return .query(param)
}
}
}
실제로 데이터를 통신을위해 사용하는 코드
class TempAPI : NetworkServiceProtocol{
static let shared = TempAPI()
let jsonDecoder = JSONDecoder()
func fetchRepositories<T: Decodable>(type : T.Type , _ api: AppStoreApi) throws -> Observable<T> {
return URLSession.shared.rx
.data(request : try api.asURLRequest())
.map{ [weak self] item in
guard let self = self else { throw SelfError.NonSelf }
let product = try self.jsonDecoder.decode(T.self, from: item)
return product
}
}
}
enum SelfError : Error{
case NonSelf
}
URLSession을 통해 데이터를 요청하고 함수를 제네릭하게 만들어 리턴시킬 데이터형태를 선언을 받는다.
URLSession.shared.rx 를 통해 비동기를 Rx로 리턴을 해준다.
그리고 마찬가지로 throws 를 통해 생기는 에러들을 컨트롤 해준다.
viewDidLoad
.flatMap { [weak self] _ -> Observable<RecomendSearchResponse> in
guard let self = self else { return .never() }
return try self.networkAPI.fetchRepositories(type: RecomendSearchResponse.self, .SERARCH_RECOMEND_APP(term: "game"))
.catch{ error in
return .never()
}
}
.map{ $0.results }
실제 사용은 이런식으로 진행해주면된다. type에 리턴받을 데이터의 형태를 적어주고 그 뒤에 내가 사용할 API와 변수를 넘겨준다.
그리고 catch문을 통해 error을 핸들링을 진행해주면된다.
지금 예제로 만들어진 함수는 retry , 여러 네트워크 오류에대한 error handling등이 이뤄지지 않고 최소한의 예외처리만을 진행하여 만들어진 예제다 retry , error handling 을 Alamofire와 , Moya를 사용한다면 상당히 쉽게 처리할수있다 하지만 라이브러리를 사용하기전 기본적인 원리 , 구조를 이해하고 진행해야하기에 직접만들어서 통신까지 진행해보았고 자료를 공유한다.
'Swift 공부' 카테고리의 다른 글
RxSwift Disposable 이란? (0) | 2022.04.13 |
---|---|
Property Wrapper (0) | 2022.04.12 |
RxDataSources Multiple Object Multiple Section (1) | 2022.04.08 |
Swift Throw Error Handling (0) | 2022.04.07 |
Async / await의 도입 스위프트 5.5 (0) | 2022.04.05 |