본문 바로가기
iOS/Swift

Use async/await with URLSession

by 바등쪼 2023. 2. 11.

 

서버 통신을 통해 사진을 불러오는 기능을 async/await URLSession을 사용해 구현해보자!

 

기존의 방식 (completion Handler)


  • 문제점
    • 스레드 문제가 발생 할 수 있다. (Data Race)
    • 위의 코드에서 총 3번의 completionHandler를 호출부에서 오직 한 곳만 main queue에 dispatch 되고 있다. → 버그 발생 가능
    • 에러 처리 또한 제대로 되어 있지 않다.

 

Fetch Photo with async/await


func fetchPhoto(url: URL) async throws -> UIImage
{
    let (data, response) = try await URLSession.shared.data(from: url)

    guard let httpResponse = response as? HTTPURLResponse,
          httpResponse.statusCode == 200 else {
        throw WoofError.invalidServerResponse
    }

    guard let image = UIImage(data: data) else {
        throw WoofError.unsupportedImage
    }

    return image
}
  • 장점
    • Control flow가 위에서 아래로 linear 하다.
    • 같은 concurrency context 안에서 모든 작업이 진행된다. → 쓰레드 문제 발생 X

 

 

URLSession.data


let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
      httpResponse.statusCode == 200 /* OK */ else {
    throw MyNetworkingError.invalidServerResponse
}
  • URLSession을 이용해 데이터 불러오기

 

URLSession.upload


func upload(for request: URLRequest, from data: Data) async throws -> (Data, URLResponse)
func upload(for request: URLRequest, fromFile url: URL) async throws -> (Data, URLResponse)
var request = URLRequest(url: url)
request.httpMethod = "POST"

let (data, response) = try await URLSession.shared.upload(for: request, fromFile: fileURL)
guard let httpResponse = response as? HTTPURLResponse,
      httpResponse.statusCode == 201 /* Created */ else {
    throw MyNetworkingError.invalidServerResponse
}

 

URLSession.download


func download(from url: URL) async throws -> (URL, URLResponse)
func download(for request: URLReqeust) async throws -> (URL, URLResponse)
func download(resumeFrom resumeData: Data) async throws -> (URL, URLResponse)
let (location, response) = try await URLSession.shared.download(from: url)
guard let httpResponse = response as? HTTPURLResponse,
      httpResponse.statusCode == 200 /* OK */ else {
    throw MyNetworkingError.invalidServerResponse
}

try FileManager.default.moveItem(at: location, to: newLocation)
  • download 메서드는 response body를 메모리가 아닌 파일로 저장한다.
  • 이러한 방식은 파일을 자동으로 삭제하지 않기 때문에 직접 파일을 삭제하는 것을 잊으면 안 된다.

 

Cancellation


let task = Task {
    let (data1, response1) = try await URLSession.shared.data(from: url1)

    let (data2, response2) = try await URLSession.shared.data(from: url2)

}

task.cancel()
  • Concurrency Task 를 이용해 cancel 할 수 있다.
  • Task라는 같은 단어를 사용하지만 concurrency Task와 URLSessionTask는 서로 관계가 없다.

 

URLSession.byte


  • Response Body를 점진적으로 수신하기 위한 메서드
  • Response Header가 수신되면 Response Body이 바이트의 비동기 시퀀스로 전송된다.
  • Real Time endpoint API가 있다고 가정하면 다음과 같이 demo 앱을 구현 할 수 있다.
let (bytes, response) = try await URLSession.shared.bytes(from: Self.eventStreamURL)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
    throw WoofError.invalidServerResponse
}
for try await line in bytes.lines {
    let photoMetadata = try JSONDecoder().decode(PhotoMetadata.self, from: Data(line.utf8))
    await updateFavoriteCount(with: photoMetadata)
}

 

 

task specific delegate demo


class AuthenticationDelegate: NSObject, URLSessionTaskDelegate {
    private let signInController: SignInController
    
    init(signInController: SignInController) {
        self.signInController = signInController
    }
    
    func urlSession(_ session: URLSession,
                    task: URLSessionTask,
                    didReceive challenge: URLAuthenticationChallenge) async
    -> (URLSession.AuthChallengeDisposition, URLCredential?) {
        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic {
            do {
                let (username, password) = try await signInController.promptForCredential()
                return (.useCredential,
                        URLCredential(user: username, password: password, persistence: .forSession))
            } catch {
                return (.cancelAuthenticationChallenge, nil)
            }
        } else {
            return (.performDefaultHandling, nil)
        }
    }
}

 

 


Uploaded by

N2T

'iOS > Swift' 카테고리의 다른 글

Understanding Swift Performance  (2) 2023.03.13
Explore structured concurrency in Swift  (0) 2023.02.11
Meet AsyncSequence  (0) 2023.02.11
Meet async/await in Swift  (0) 2023.02.11
Opaque Type  (0) 2023.02.11

댓글