Alamofire is a powerful HTTP networking library for Swift that simplifies downloading images and files from websites. It provides dedicated download APIs that stream content directly to the filesystem, making it ideal for handling large files efficiently.
Installation
Using Swift Package Manager (Recommended)
Add Alamofire to your project through Xcode:
1. Go to File → Add Package Dependencies
2. Enter: https://github.com/Alamofire/Alamofire.git
3. Select version ~> 5.8
(latest stable)
Using CocoaPods
Add to your Podfile
:
pod 'Alamofire', '~> 5.8'
Then run:
pod install
Basic File Download
Here's how to download an image or file using Alamofire's download method:
import Alamofire
func downloadFile(from urlString: String, to fileName: String) {
// Get Documents directory
guard let documentsURL = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first else {
print("Could not access Documents directory")
return
}
let destinationURL = documentsURL.appendingPathComponent(fileName)
// Create destination closure
let destination: DownloadRequest.Destination = { _, _ in
return (destinationURL, [.removePreviousFile, .createIntermediateDirectories])
}
// Download file
AF.download(urlString, to: destination)
.response { response in
switch response.result {
case .success:
print("File downloaded successfully to: \(destinationURL)")
case .failure(let error):
print("Download failed: \(error)")
}
}
}
// Usage
downloadFile(from: "https://example.com/image.jpg", to: "downloaded_image.jpg")
Download with Progress Tracking
Track download progress for better user experience:
func downloadWithProgress(from urlString: String, to fileName: String) {
guard let documentsURL = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first else { return }
let destinationURL = documentsURL.appendingPathComponent(fileName)
let destination: DownloadRequest.Destination = { _, _ in
return (destinationURL, [.removePreviousFile, .createIntermediateDirectories])
}
AF.download(urlString, to: destination)
.downloadProgress { progress in
let percentComplete = progress.fractionCompleted * 100
print("Download Progress: \(String(format: "%.1f", percentComplete))%")
// Update UI on main thread
DispatchQueue.main.async {
// Update progress bar or label
// progressBar.progress = Float(progress.fractionCompleted)
}
}
.response { response in
switch response.result {
case .success:
print("Download completed!")
case .failure(let error):
print("Download failed: \(error)")
}
}
}
Resumable Downloads
Implement resumable downloads for interrupted connections:
class FileDownloader {
private var downloadRequest: DownloadRequest?
private let resumeDataKey = "ResumeData"
func startDownload(from urlString: String, to fileName: String) {
guard let documentsURL = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first else { return }
let destinationURL = documentsURL.appendingPathComponent(fileName)
let destination: DownloadRequest.Destination = { _, _ in
return (destinationURL, [.removePreviousFile, .createIntermediateDirectories])
}
// Check for existing resume data
if let resumeData = UserDefaults.standard.data(forKey: resumeDataKey) {
downloadRequest = AF.download(resumingWith: resumeData, to: destination)
print("Resuming download...")
} else {
downloadRequest = AF.download(urlString, to: destination)
print("Starting new download...")
}
downloadRequest?
.downloadProgress { progress in
let percentComplete = progress.fractionCompleted * 100
print("Progress: \(String(format: "%.1f", percentComplete))%")
}
.response { [weak self] response in
switch response.result {
case .success:
print("Download completed successfully!")
// Clear resume data on successful completion
UserDefaults.standard.removeObject(forKey: self?.resumeDataKey ?? "")
case .failure(let error):
print("Download failed: \(error)")
// Save resume data for later use
if let resumeData = response.resumeData {
UserDefaults.standard.set(resumeData, forKey: self?.resumeDataKey ?? "")
}
}
}
}
func pauseDownload() {
downloadRequest?.cancel { resumeDataOrNil in
if let resumeData = resumeDataOrNil {
UserDefaults.standard.set(resumeData, forKey: self.resumeDataKey)
print("Download paused, resume data saved")
}
}
}
func cancelDownload() {
downloadRequest?.cancel()
UserDefaults.standard.removeObject(forKey: resumeDataKey)
print("Download cancelled")
}
}
// Usage
let downloader = FileDownloader()
downloader.startDownload(from: "https://example.com/largefile.zip", to: "largefile.zip")
Download Multiple Files
Download multiple files concurrently:
func downloadMultipleFiles(urls: [String], fileNames: [String]) {
guard urls.count == fileNames.count else {
print("URLs and file names count mismatch")
return
}
let dispatchGroup = DispatchGroup()
for (index, urlString) in urls.enumerated() {
dispatchGroup.enter()
let fileName = fileNames[index]
guard let documentsURL = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first else {
dispatchGroup.leave()
continue
}
let destinationURL = documentsURL.appendingPathComponent(fileName)
let destination: DownloadRequest.Destination = { _, _ in
return (destinationURL, [.removePreviousFile, .createIntermediateDirectories])
}
AF.download(urlString, to: destination)
.response { response in
defer { dispatchGroup.leave() }
switch response.result {
case .success:
print("✓ Downloaded: \(fileName)")
case .failure(let error):
print("✗ Failed to download \(fileName): \(error)")
}
}
}
dispatchGroup.notify(queue: .main) {
print("All downloads completed!")
}
}
// Usage
let imageUrls = [
"https://example.com/image1.jpg",
"https://example.com/image2.png",
"https://example.com/document.pdf"
]
let fileNames = ["image1.jpg", "image2.png", "document.pdf"]
downloadMultipleFiles(urls: imageUrls, fileNames: fileNames)
Best Practices
- Always use destination closures for better file management
- Handle network errors gracefully with proper error messages
- Update UI on the main thread when showing progress
- Use resume data for large file downloads
- Implement timeout handling for slow connections:
AF.download(urlString, to: destination)
.validate()
.response { response in
// Handle response
}
- Validate file integrity after download:
.response { response in
switch response.result {
case .success(let url):
if let url = url {
let fileSize = try? FileManager.default.attributesOfItem(atPath: url.path)[.size] as? Int64
print("Downloaded file size: \(fileSize ?? 0) bytes")
}
case .failure(let error):
print("Download failed: \(error)")
}
}
This comprehensive approach ensures reliable file downloads with proper error handling, progress tracking, and resume capabilities in your iOS or macOS applications.