URL Session(01)

Introduce

URLSession Loding System은 HTTPs와 같은 표준 프로토콜이나 사용자가 커스텀한 프로토콜을 사용해 URL로 식별된 리소스에 접근할 수 있는 기능을 제공한다.

로딩 작업은 비동기적으로 수행되므로, 앱은 응답성을 유지하며 데이터가 도착하거나 오류가 발생할 때 이를 처리할 수 있다.

Memory-structure image

Memory-structure image


URLSession 인스턴스를 사용하여 하나 이상의 URLSessionTask 인스턴스를 생성할 수 있으며, 생성된 URLSessionTask 인스턴스는 데이터를 Request, Response, 파일을 다운로드할 수 있다.

URLSession을 구성하기 위해서는 URLSessionConfiguration 객체를 사용한다.

이를 통해 캐시와 쿠키를 사용하는 방법이나, 셀룰러 네트워크에서 연결을 허용할지 여부와 같은 동작을 제어할 수 있다.

URLSession 종류

URLSession은 기본 요청을 처리하기 위한 싱글톤(shared) 세션을 제공한다.

해당 세션은 URLSessionConfiguration 객체를 사용하지 않는다.

사용자가 직접 생성한 세션만큼 커스텀할 수는 없지만, 요구사항이 매우 간단할 경우 사용하기 적절하다.

Memory-structure image


다른 종류의 세션을 사용하려면 다음 URLSessionConfiguration 객체를 사용해 URLSession을 생성해야 한다.

.default

.default 세션은 싱글톤 세션과 유사하게 동작하지만, 사용자가 설정을 구성할 수 있다.

또한 데이터를 받을 수 있도록 델리게이트를 할당할 수도 있다.

.ephemeral

.ephemeral 세션은 싱글톤 세션과 유사하지만, 캐시, 쿠키와 같은 중요 데이터를 디스크에 기록하지 않는다.

.background

.background 세션은 앱이 실행 중이 아닐 때에도 데이터를 업로드하거나 다운로드할 수 있도록 지원한다.

비동기성와 URLSession

URLSession API는 비동기적이며, 메서드 호출 방식에 따라 앱에 데이터를 반환하는 다양한 방법을 지원한다.

async/await

async/await 키워드를 사용한 비동기 작업은 가독성이 completion handler 방식과 비교했을때 높으며, try-catch 구문을 사용해 비교적 간단한 에러 처리를 할 수 있다.

하지만 코드를 실행하는 스레드가 지정되기 때문에 효율적인 컨텍스트 스위칭 및 QoS를 통한 성능 최적화를 기대하기는 어렵다.

var urlComponents = URLComponents(string: "url")
urlComponents?.queryItems = self.urlQueryItems
        
guard let url = urlComponents?.url else { return }
        
Task {
    let data = await fetchData(url: url)
    // ...          
}

private func fetchData(url: URL) async -> CurrentWeatherResponseDTO? {
    let urlSession = URLSession(configuration: .default)
    
    do  {
        let (data, response) = try await urlSession.data(from: url)
        if let response = response as? HTTPURLResponse,
           (200..<300).contains(response.statusCode) {
            guard let decodedData = try? JSONDecoder().decode(CurrentWeatherResponseDTO.self, from: data) else { return nil }
            return decodedData
        } else {
            return nil
        }
    } catch {
        return nil
    }
}

completion handler

completion handler 방식은 직접 사용자가 해당 작업을 수행할 스레드를 지정할 수 있으며, QoS를 잘 적용하면 성능 최적화를 기대할 수 있다.

하지만 async/await 키워드 방식에 비해 가독성이 떨어지며, 복잡한 비동기 작업 구현시 콜백 재옥 현상이 발생할 수 있다.

var urlComponents = URLComponents(string: "url")
urlComponents?.queryItems = self.urlQueryItems

guard let url = urlComponents?.url else { return }

fetchData(url: url) { [weak self] (data: CurrentWeatherResponseDTO?) in
    guard let self, let data else { return }

    DispatchQueue.main.async {
        //...
    }
}


private func fetchData<T: Decodable>(url: URL, completion: @escaping (T?) -> Void) {
    let urlSession = URLSession(configuration: .default)
    urlSession.dataTask(with: URLRequest(url: url)) { data, response, error in
        guard let data = data, error == nil { return completion(nil) }
    }

    if let response = response as? HTTPURLResponse,
       (200..<300).contains(response.statusCode) {
        guard let decodedData = try? JSONDecoder().decode(T.self, from: data) else { return complition(nil) }
        completion(decodedData)
    } else {
        return complition(nil)
    }.resume()
}

© 2024. All rights reserved.

Powered by Hydejack v9.2.1