본문 바로가기

iOS/DataParsing

[iOS] Codable 프로토콜 사용 유무 차이.. 완죤 대박 #4

728x90

이전 글에서 Codable에 대해 공부를 했었는데요. 이번글에서는 Codable을 쓰면 좋은 점을 URLRequest +URLSession vs Alamofire 을 비교하면서 공부를 해볼까 합니다. 지난 글의 연장선이라고 할 수 있습니다.

 

저번 글과 마찬가지로 https://www.youtube.com/watch?v=w7xJrAYQoHE 이 유튜브에서 나오는 REST API url을 사용할 것입니다.

// 위에서 언급했던 url 에서 정의된 JSON형식의 데이터 
{
    "results":{
       "sunrise":"2022-09-01T02:45:56+00:00",
       "sunset":"2022-09-01T16:15:33+00:00",
       "solar_noon":"2022-09-01T09:30:45+00:00",
       "day_length":48577,
       "civil_twilight_begin":"2022-09-01T03:23:30+00:00",
       "civil_twilight_end":"2022-09-01T15:37:59+00:00",
       "nautical_twilight_begin":"2022-09-01T04:08:49+00:00",
       "nautical_twilight_end":"2022-09-01T14:52:41+00:00",
       "astronomical_twilight_begin":"2022-09-01T04:54:38+00:00",
       "astronomical_twilight_end":"2022-09-01T14:06:51+00:00"
    },
    "status":"OK"
 }

데이터의 형식은 {"result" : {딕셔너리}, "status" : "OK"} 형식으로 root Object as Object타입입니다.

Codable에 맞게 위 데이터를 전부 받아오기 위한 설계를 목표로 가정할 것입니다.

1. 데이터를 JSON형식으로 파싱할 struct 정의하기

Codable 프로토콜 규칙을 준수하여 구조체를 정의하면

struct APIResponse: Codable{
    let results : WeatherResponse
    let status  : String
}

struct WeatherResponse: Codable{
    var sunrise: String
    let sunset: String
    let solar_noon: String
    let day_length: Int
    let civil_twilight_begin: String
    let civil_twilight_end: String
    let nautical_twilight_begin: String
    let nautical_twilight_end: String
    let astronomical_twilight_begin: String
    let astronomical_twilight_end: String
}

이때 원하는 키에 해당하는 프로토콜만 선언해도 됩니다.

2-1. URLRequset + URLSession 으로 Codable 인스턴스 얻기

1. URLResponse로 HTTP 헤더 정의

2. URLSession으로 데이터 파싱

3. dataTask으로 성공적으로 요청한 이후에 받은 데이터를 JSONDecoder().decode(_:from:) 를 통해 Codable 인스턴스 얻기!

func getDataWithURLSession(){
        // 1. set URLRequest Line-Header-body
        guard let url = URL(string: urlStr) else {
            return
        }
        guard var request = try? URLRequest(url: url) else {
            print("Can't get URLRequest instance")
            return
        }
        request.httpMethod = "GET"
        request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
        
        // 2. url 객체로 구현된 Request를 통한 dataTask HTTP통신
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            
            guard let _data = data, error == nil else {
                return
            }
            // JSONDecoder를 통해 우리가 정한 커스텀 자료 구조로 디코딩
            guard let res = try? JSONDecoder().decode(APIResponse.self, from: _data) else {
                print("Can't deocde")
                return
            }
            print(res)
        }
        task.resume()

    }

이후의 res인스턴스는 APIResponse 인스턴스  사용하듯 사용하면 됩니다.(개꿀..)

2-2. Alamofire responseDecodable(of:)로 Codable 인스턴스 얻기

이방법은 위 방법보다 더 간단합니다.

func getDataWithAFResponseDecodable(){
        //RESIful API에 데이터 요청
        let call = AF.request(urlStr, method: .get, encoding: JSONEncoding.default)
        
        //Decode
        call.responseDecodable(of: APIResponse.self){ response in
            guard response.error == nil else{
                print("에러")
                return
            }
            switch response.result{
            case .success(_):
                guard let _data = response.value else {
                    print("Failed binding response.value")
                    return
                }
                print(_data.results)
                break
            case .failure(_):
                break
            }
        }
    }

3. Codable을 사용하지 않은 경우 URLRequest + URLSession으로 JSON데이터 파싱하기

1. 일반 구조체 생성

2. 바이트 데이터를 JSONSerialization으로 JSON타입으로 변환

3. 변환된 Object의 키를 통해 value를 프로퍼티에 저장

4. 1에서 생성한 구조체에 대입

 

func getDataWithURLSessionDefault() {
        // 1. set URLRequest Line-Header-body
        guard let url = URL(string: urlStr) else {
            return
        }
        
        guard var request = try? URLRequest(url: url) else {
            print("Can't get URLRequest instance")
            return
        }
        request.httpMethod = "GET"
        request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
        
        // 2. url 객체로 구현된 Request를 통한 dataTask HTTP통신
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            guard let _data = data, error == nil else {
                return
            }
            
            guard let json = try? JSONSerialization.jsonObject(with: _data, options: []) as? NSDictionary else{
                print("can't parse as NSDictionary")
                return
            }
            
            let status = json["status"] as? String ?? ""
            guard let results = json["results"] as? NSDictionary else {
                print("results key value is nil")
                return
            }
            let sunrise = results["sunrise"] as? String ?? ""
            let sunset  = results["sunset"] as? String ?? ""
            let solar_noon = results["solar_noon"] as? String ?? ""
            let day_length = results["day_length"] as? Int ?? -1
            let civil_twilight_begin = results["civil_twilight_begin"] as? String ?? ""
            let civil_twilight_end   = results["civil_twilight_end"] as? String ?? ""
            let nautical_twilight_begin = results["nautical_twilight_begin"] as? String ?? ""
            let nautical_twilight_end   = results["nautical_twilight_end"] as? String ?? ""
            let astronomical_twilight_begin = results["astronomical_twilight_begin"] as? String ?? ""
            let astronomical_twilight_end = results["astronomical_twilight_end"] as? String ?? ""
            
            let _results = WeatherResponseDefault(sunrise: sunrise, sunset: sunset, solar_noon: solar_noon,
            									day_length: day_length, civil_twilight_begin: civil_twilight_begin,
                                                civil_twilight_end: civil_twilight_end, nautical_twilight_begin: nautical_twilight_begin,
                                                nautical_twilight_end: nautical_twilight_end, astronomical_twilight_begin: astronomical_twilight_begin,
                                                astronomical_twilight_end: astronomical_twilight_end)
            
            let weatherInfo = APIResponseDefault(results: _results, status: status)
            
        }
        task.resume()
    }

읔.. 앞으로 저는 Codable만 사용할 것 같습니다.

728x90