Swift
File Download
iOS Development
Swift Programming
Networking in Swift

How to download file in swift?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Downloading a file in Swift is usually a URLSession task. The important details are not only starting the request, but also validating the response, moving the temporary download to a permanent location, and choosing whether you need progress reporting.

The simplest modern download flow

For modern Swift, URLSession.shared.download(from:) is the cleanest starting point.

swift
1import Foundation
2
3func downloadExample() async {
4    let remoteURL = URL(string: "https://example.com/report.pdf")!
5    let destination = FileManager.default.temporaryDirectory
6        .appendingPathComponent("report.pdf")
7
8    do {
9        let (temporaryURL, response) = try await URLSession.shared.download(from: remoteURL)
10
11        guard let http = response as? HTTPURLResponse,
12              (200...299).contains(http.statusCode) else {
13            throw URLError(.badServerResponse)
14        }
15
16        if FileManager.default.fileExists(atPath: destination.path) {
17            try FileManager.default.removeItem(at: destination)
18        }
19
20        try FileManager.default.moveItem(at: temporaryURL, to: destination)
21        print("Saved to: \(destination.path)")
22    } catch {
23        print("Download failed: \(error)")
24    }
25}

This works well when you just need to fetch a file and save it.

Why the temporary URL matters

A successful download task does not usually save the file where you want it permanently. It gives you a temporary file location. If you do not move or copy that file during completion handling, it may disappear.

That is why the file move step is not optional. It is part of the correct download flow.

Choose the destination directory deliberately

On Apple platforms, file placement matters. A common rule is:

  • use Documents for user-visible files
  • use Caches for recreatable downloaded data
  • use Temporary for short-lived files

For example, to save to Documents:

swift
let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let destination = documents.appendingPathComponent("report.pdf")

This choice affects cleanup, backups, and user expectations.

Use a delegate when you need progress

If you need a progress bar, use URLSessionDownloadDelegate instead of the one-shot async call.

swift
1import Foundation
2
3final class Downloader: NSObject, URLSessionDownloadDelegate {
4    lazy var session: URLSession = {
5        URLSession(configuration: .default, delegate: self, delegateQueue: nil)
6    }()
7
8    func start() {
9        let url = URL(string: "https://example.com/report.pdf")!
10        session.downloadTask(with: url).resume()
11    }
12
13    func urlSession(
14        _ session: URLSession,
15        downloadTask: URLSessionDownloadTask,
16        didWriteData bytesWritten: Int64,
17        totalBytesWritten: Int64,
18        totalBytesExpectedToWrite: Int64
19    ) {
20        guard totalBytesExpectedToWrite > 0 else { return }
21        let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
22        print("Progress: \(Int(progress * 100))%")
23    }
24}

That is the right direction for UI-driven downloads.

Validate the response, not just the network success

A download can succeed at the transport level and still be logically wrong. A 404 page or HTML error response can still arrive as bytes. That is why checking the HTTPURLResponse matters.

In real apps, you may also check:

  • MIME type
  • expected file size
  • authentication state
  • redirect behavior

The more important the file is, the more explicit the validation should be.

Common Pitfalls

The biggest pitfall is forgetting to move the temporary file to a permanent location. The download appears to work, but the file is not where you expect later.

Another issue is ignoring the status code and treating any returned bytes as a successful file download.

It is also easy to overwrite an existing file accidentally or have moveItem fail because the destination already exists. Handle that case intentionally.

Finally, when using delegate callbacks in an app, send UI updates back to the main actor if necessary. Networking can finish off the main thread.

Summary

  • Use URLSession to download files in Swift.
  • Move the temporary file immediately to a destination you control.
  • Validate the HTTP response before treating the download as successful.
  • Use a download delegate when you need progress updates.
  • Choose the destination directory based on how long the file should live and whether the user should see it.

Course illustration
Course illustration

All Rights Reserved.