SwiftConcurrency 3편 Upload / Download

김재형·2024년 6월 5일
0
post-thumbnail
post-custom-banner

들어가기에 앞서 ...

3편을 보기전 전편을 보지 않으셨다면
1편 부터 봐주시실 바랍니다! https://velog.io/@little_tail/Swift-Concurrency1%ED%8E%B8async-await
자 그러면 시작해 보겠습니다.

Async / Await 사용해보자.

저번 1편때 코드를 다시 가져와 보겠습니다.

.complitionHandler 의 문제점 - 1편

func someRequest<Dto: DTOType>(router: Router, completion: @escaping (Result<Dto, Error>) -> Void) {

    let urlRequest = router.makeURLRequest()
    
    let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in
        if let error = error {
            completion(.failure(error)) // 헨들러
            
        } else if (response as? HTTPURLResponse)?.statusCode != 200 {
            completion(.failure(NetworkError.failForStatusCodeChange)) // 헨들러
            
        } else {
            guard let data = data else {
                completion(.failure(NetworkError.dataIsNil)) // 헨들러
                return
            }
            do {
                let decodedData = try JSONDecoder().decode(Dto.self, from: data)
                completion(.success(decodedData)) // 헨들러
                
            } catch {
                completion(.failure(NetworkError.decodingError)) // 헨들러
                
            }
        }
    }
    task.resume()
}

해당 하는 코드처럼 개발자가 모든 에러나 성공 사례들을 컴플리션 핸들러로 컨트롤 하여야 했습니다.

  • 너무 복잡해! -> 흐름 복잡
  • 핸들러 잘못짜도 에러가 나지 않아!! -> 문제 파악 어려움
  • 이게 무슨 코드야...? -> 코드 가독성 저하
    그래서 해당 코드를 1편과 2편을 통해 아래와 같이 구성하였습니다.
func someRequest<Dto: DTOType>(router: Router) async throws -> Dto {
   let urlRequest = router.makeURLRequest() // Thread Block
   
   let (data, response) = try await URLSession.shared.data(for: urlRequest)
   
   guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
       throw NetworkError.failForStatusCodeChange
   }
   
   do {
       let decodedData = try JSONDecoder().decode(Dto.self, from: data) // Thread Block
       return decodedData
   } catch {
       throw NetworkError.decodingError
   }
}

async - await 를 통해 코드를 더욱 간결하게 만들었습니다.

  • 코드의 흐름이 마치 동기 코드같아요!
  • 해당 코드 에선 동일한 concurrency context 에서 이루어져 쓰레딩 이슈를 해결할수 있습니다.
  • CompletionHandler 없이 코드를 종료 시켜줍니다 -> Throw -> 어디서 무제가 생기는지도 알수있죠.

DownLoad

URLSession 에선 다운로드와 업로드 메서드를 제공합니다.

DownLoad 예시

func download(from url: URL) async throws -> (URL, URLResponse)
func download(for request: URLRequest) async throws -> (URL, URLResponse)
func download(resumeFrom resumeData: Data) async throws -> (URL, URLResponse)


//구현 예시
func downloadFile(from url: URL) async throws -> (URL, URLResponse) {
    let (fileURL, response) = try await URLSession.shared.download(from: url)
    
    guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
        throw URLError(.badServerResponse)
    }
    
    return (fileURL, response)
}

func downloadFile(for request: URLRequest) async throws -> (URL, URLResponse) {
    let (fileURL, response) = try await URLSession.shared.download(for: request)
    
    guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
        throw URLError(.badServerResponse)
    }
    
    return (fileURL, response)
}

Task {
    do {
        let url = URL(string: "https://example.com/exampleFile")!
        let (fileURL, response) = try await downloadFile(from: url)
        // 다운로드된 파일~
        print("File downloaded to: \(fileURL)")
    } catch {
        print("파일 다운로드 실패: \(error)")
    }
}

Upload

GET은 업로드를 지원하지 않으므로 요청전 올바른 HTTP 방법을 설정해야합니다.

업로드 예시

func upload(for request: URLRequest, from data: Data) async throws -> (Data, URLResponse)
func upload(for request: URLRequest, fromFile url: URL) async throws -> (Data, URLResponse)

import Foundation

func uploadFile(to url: URL, from fileURL: URL) async throws -> (Data, URLResponse) {
    var request = URLRequest(url: url)
    request.httpMethod = "PUT"
    
    let (data, response) = try await URLSession.shared.upload(for: request, fromFile: fileURL)
    
    guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
        throw URLError(.badServerResponse)
    }
    
    return (data, response)
}

Task {
    do {
        let url = URL(string: "https://example.com/upload")!
        let fileURL = URL(fileURLWithPath: "/path/to/your/file.zip")
        let (data, response) = try await uploadFile(to: url, from: fileURL)
        // 업로드 결과 다루기
    } catch {
        print("파일 업로드 실패: \(error)")
    }
}

마무리 하며..

생각 외로 분량이 적었습니다..
더많은 양이 나올 부분이라고 생각했었는데 이정도 더군요.
다음편은 구조화 동시성 에 대해서 배워보겠습니다.
모두 고생하셨습니다.

profile
IOS 개발자 새싹이
post-custom-banner

0개의 댓글