개발/Swift

[Swift] URLSession 통해 async fetch & json 파싱

덤벨로퍼 2022. 10. 4. 09:01

HTTP 메소드, JSON 파싱

http 통신을 위해 URLSession을 사용해보고 얻어온 데이터를 원하는 정보만 디코딩해서 사용하려 한다.

class Network {
    
    func fetchTrends() {
        guard let url = URL(string: "<https://api.giphy.com/v1/gifs/search?api_key=\\(apikey)>") else { return }
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        URLSession.shared.dataTask(with: request) { data, response, error in
            if let error = error {
                print("Error fetch Trend", error.localizedDescription)
            }
            print(data)
        }
    }
}

우선 데이터 fetch를 위한 코드이다. dataTask 클로져 내에 data를 사용할 것이다.

또 URLSession은 async await 을 지원해준다. 따라서 다음과 같이도 사용할수 있다.

static func fetchTrends() async throws {
        guard let url = URL(string: "<https://api.giphy.com/v1/gifs/trending?api_key=\\(APIKEY)&offset=\\(offset)>") else {
            throw NetworkError.invalidURL }
        
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
         print("request \\(request)")
         
         
         let (data, response) = try await URLSession.shared.data(from: url)
         
        
         
    }

let (data, response) = try await URLSession.shared.data(from: url) 해당 코드는 비동기로 처리가 된다. 그러므로 아래에 있는 코드는

response를 받을떄까지 기다렸다가 처리된다.

데이터는 아래와 같다. 나는 data-images-original & preview 에 있는

height & width & url 이 필요한 상황이다.

따라서 모델을 다음과 같이 정의했다.

class GifData: Decodable {
    var data: [Gif]
}

class Gif : Decodable {
    var images: Images?
    
}

class Images: Decodable {
    var original: Image?
    var preview: Image?
    
    enum CodingKeys: String, CodingKey {
        case original       
				case preview = "preview_gif"
    }
    
    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        original = try? values.decode(Image.self, forKey: .original)
        preview = try? values.decode(Image.self, forKey: .preview)

    }
}

class Image: Decodable {
    var url: String?
    var height: Int?
    var width: Int?
    
    enum CodingKeys: String, CodingKey {
        case url, mp4, height, width
    }
    
    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        url = try? values.decode(String.self, forKey: .url)
        width = try? Int(values.decode(String.self, forKey: .width))
        height = try? Int(values.decode(String.self, forKey: .height))
                         
    }
}

Decodable 한 모델을 통해 받아온 데이터를 모델링 할수 있다.

let decoder = JSONDecoder()
let gif = try decoder.decode(GifData.self, from: data)

return gif.data

전체 코드

static func fetchTrends(offset: Int) async throws -> [Gif] {
        guard let url = URL(string: "<https://api.giphy.com/v1/gifs/trending?api_key=\\(APIKEY)&offset=\\(offset)>") else {
            throw NetworkError.invalidURL }
        
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
         
         let (data, response) = try await URLSession.shared.data(from: url)
         
         guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
             throw NetworkError.invalidServerResponse
         }
         
         let decoder = JSONDecoder()
         let gif = try decoder.decode(GifData.self, from: data)
         return gif.data