"Download files directly to the filesystem."
파일시스템에 파일을 직접 다운로드합니다.
이미지, 문서처럼 이미 파일로 저장된 네트워크 리소스의 경우 로컬 파일시스템에 직접 이러한 아이템을 가져오기 위해 다운로드 작업을 사용할 수 있습니다.
Tip
애플리케이션이 백그라운드에서 일시정지이거나 종료되어 있는 동안에도 작업하기 위한 다운로드 작업을 설정할 수 있습니다. 자세한 내용은 Downloading Files in the Background를 보시기 바랍니다.
Downloading Files in the Background
https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_in_the_background
https://velog.io/@panther222128/Downloading-Files-in-the-Background
파일을 다운로드하려면 URLSession
으로부터 URLSessionDownloadTask
를 생성할 수 있습니다. 다운로드 동안 진행 업데이트 혹은 다른 딜리게이션 콜백을 신경쓰지 않는 경우 컴플리션 핸들러를 사용할 수 있습니다. 작업은 다운로드가 끝나면 컴플리션 핸들러를 호출하며, 다운로드 성공 및 실패를 가리지 않습니다.
컴플리션 핸들러는 네트워크에 연결할 수 없는 경우처럼 로컬 문제를 나타내는 클라이언트 측 에러를 받을 수 있습니다. 클라이언트 측 에러가 없으면 서버로부터 성공적인 응답을 나타내는지 확인해야 하는 URLResponse
를 받을 수 있습니다.
다운로드가 성공적이면 컴플리션 핸들러는 파일 위치(로컬 파일 시스템에 있는)를 나타내는 URL을 받을 것입니다. 이 저장소는 일시적입니다. 만약 파일을 보존하길 원한다면, 컴플리션 핸들러로부터 반환하기 전에 이 위치로부터 복사하거나 이동시켜야 합니다.
Listing 1은 컴플리션 핸들러를 사용해서 다운로드 작업을 생성하는 간단한 예시를 보여줍니다. 에러가 나타나지 않으면 컴플리션 핸들러는 다운로드된 파일을 앱의 문서 디렉토리로 이동시킵니다. resume()
호출을 통해 작업을 시작합니다.
Listing 1 Creating a download task with a completion handler
let downloadTask = URLSession.shared.downloadTask(with: url) {
urlOrNil, responseOrNil, errorOrNil in
// check for and handle errors:
// * errorOrNil should be nil
// * responseOrNil should be an HTTPURLResponse with statusCode in 200..<299
guard let fileURL = urlOrNil else { return }
do {
let documentsURL = try
FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
let savedURL = documentsURL.appendingPathComponent(fileURL.lastPathComponent)
try FileManager.default.moveItem(at: fileURL, to: savedURL)
} catch {
print ("file error: \(error)")
}
}
downloadTask.resume()
Tip
Listing 1은 간단하게 URL 파라미터를 받는downloadTask(with:)
를 사용해서 다운로드 작업을 생성합니다. 만약 서버에 보내는 요청을 커스터마이징하려면,downloadTask(with:)
를 사용해서 작업을 생성하고 커스터마이징된URLRequest
에 전달하시기 바랍니다.
다운로드가 진행될 때 진행사항 업데이트르 받길 원한다면 딜리게이트를 사용해야 합니다. 컴플리션 핸들러에서 결과를 받는 것 대신 URLSessionTaskDelegate
와 URLSessionDownloadDelegate
프로토콜로부터 메소드의 구현에 콜백을 받게됩니다.
고유한 URLSession
인스턴스를 생성하고 딜리게이트 속성을 설정하시기 바랍니다. Listing 2는 스스로를 딜리게이트로 설정하고 있는 urlSession
속성을 게으르게 인스턴스화한 것을 보여줍니다.
Listing 2 Creating a URL session with a delegate
private lazy var urlSession = URLSession(configuration: .default,
delegate: self,
delegateQueue: nil)
다운로드를 시작하려면 URLSessionDownloadTask
를 생성하기 위해 이 URLSession
을 사용해여 하며, 다음으로 Listing 3에서 보이는 것처럼 resume()
호출로 작업을 시작합니다.
Listing 3 Creating and starting a download task that uses a delegate
private func startDownload(url: URL) {
let downloadTask = urlSession.downloadTask(with: url)
downloadTask.resume()
self.downloadTask = downloadTask
}
다운로드를 시작하면 URLSessionDownloadDelegate
메소드 urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)
에서 주기적인 진행사항 업데이트를 받습니다. 진행사항 UI를 업데이트하기 위해 이 콜백으로 제공된 바이트 카운트를 사용할 수 있습니다.
Listing 4는 이 콜백 메소드의 구현을 보여줍니다. 이 구현은 다운로드의 진행사항 조각을 계산하고, 퍼센티지로 진행사항을 보여주는 레이블을 업데이트하기 위해 계산을 사용합니다. 콜백은 알려지지 않은 Grand Central Dispatch 큐에서 수행되기 때문에 메인 큐에서 명시적으로 UI를 업데이트해야 합니다.
Listing 4 Using a delegate method to update download progress in a UI
func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didWriteData bytesWritten: Int64,
totalBytesWritten: Int64,
totalBytesExpectedToWrite: Int64) {
if downloadTask == self.downloadTask {
let calculatedProgress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
DispatchQueue.main.async {
self.progressLabel.text = self.percentFormatter.string(from:
NSNumber(value: calculatedProgress))
}
}
Tip
만약 다운로드 동안 필요한 UI 업데이트가UIProgressView
에만 해당한다면, 진행사항 계산 수행 대신 작업의progress
속성을 사용하시기 바랍니다. 이 속성은UIProgressView
의observedProgress
속성에 할당할 수 있는Progress
의 인스턴스이며, 진행사항 뷰의 자동 업데이트를 가져오기 위해 작업을 생성할 때 해당합니다.
컴플리션 핸들러 대신 딜리게이트를 사용하는 경우 urlSession(_:downloadTask:didFinishDownloadingTo:)
구현을 통해 다운로드의 컴플리션을 처리해야 합니다. 서버 응답이 성공을 나타내는지 확실히 하기 위해 downloadTask
의 응답 속성을 확인하시기 바랍니다. 만약 그렇다면 위치 파라미터는 파일이 저장된 로컬 URL을 제공합니다. 이 위치는 콜백의 끝 시점까지만 유효합니다. 이는 파일을 즉시 읽거나, 콜백 메소드로부터 반환 전에 앱의 문서 디렉토리와 같은 다른 위치로 이동해야 함을 의미합니다. Listing 5는 다운로드된 파일의 보존 방법을 보여줍니다.
Listing 5 Saving the downloaded file in the delegate callback
func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL) {
// check for and handle errors:
// * downloadTask.response should be an HTTPURLResponse with statusCode in 200..<299
do {
let documentsURL = try
FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
let savedURL = documentsURL.appendingPathComponent(
location.lastPathComponent)
try FileManager.default.moveItem(at: location, to: savedURL)
} catch {
// handle filesystem error
}
}
클라이언트 측 에러가 발생하면 딜리게이트는 urlSession(_:task:didCompleteWithError:)
딜리게이트 메소드에 대한 콜백에서 에러를 받습니다. 반면에 다운로드가 성공적으로 완료되면, 이 메소드는 urlSession(_:downloadTask:didFinishDownloadingTo:)
이후에 호출되고 에러는 nil
입니다.
사용자가 다시 시작하지 않고도 다운로드를 재개할 수 있도록 합니다.
https://developer.apple.com/documentation/foundation/url_loading_system/pausing_and_resuming_downloads
https://velog.io/@panther222128/Pausing-and-Resuming-Downloads
앱이 비활성화 상태인 동안 파일을 다운로드하는 작업을 생성합니다.
https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_in_the_background
https://velog.io/@panther222128/Downloading-Files-in-the-Background