- Published on
> Getting Current Disk Usage in Swift
- Authors

- Name
- Mick MacCallum
- @0x7fs
Knowing how much disk space is available can help your app make smart decisions—whether to download that large file, warn users before a sync, or clean up cached data proactively. iOS provides this information through URL resource values, which give you access to volume capacity data for any file system location.
Reading Volume Capacity
The URL type provides a resourceValues(forKeys:) method that returns information about the file system. For disk usage, the relevant keys are in the URLResourceKey enum:
import Foundation
func getDiskUsage() -> (total: Int64, available: Int64)? {
let fileManager = FileManager.default
guard let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
}
do {
let values = try documentsURL.resourceValues(forKeys: [
.volumeTotalCapacityKey,
.volumeAvailableCapacityKey
])
guard let total = values.volumeTotalCapacity,
let available = values.volumeAvailableCapacity else {
return nil
}
return (Int64(total), Int64(available))
} catch {
return nil
}
}
The volumeTotalCapacityKey returns the total size of the volume in bytes, while volumeAvailableCapacityKey returns the free space. These values come from the same volume where your documents directory lives—which on iOS is the device's main storage.
Important vs Opportunistic Capacity
iOS distinguishes between space available for "important" operations and space for "opportunistic" ones. The system reserves some buffer space that it won't report as available for opportunistic use, but will allow for critical operations:
func getDetailedDiskUsage() -> DiskUsage? {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
do {
let values = try documentsURL.resourceValues(forKeys: [
.volumeTotalCapacityKey,
.volumeAvailableCapacityForImportantUsageKey,
.volumeAvailableCapacityForOpportunisticUsageKey
])
return DiskUsage(
total: values.volumeTotalCapacity.map(Int64.init) ?? 0,
availableForImportant: values.volumeAvailableCapacityForImportantUsage ?? 0,
availableForOpportunistic: values.volumeAvailableCapacityForOpportunisticUsage ?? 0
)
} catch {
return nil
}
}
struct DiskUsage {
let total: Int64
let availableForImportant: Int64
let availableForOpportunistic: Int64
var usedSpace: Int64 {
total - availableForImportant
}
var percentUsed: Double {
guard total > 0 else { return 0 }
return Double(usedSpace) / Double(total) * 100
}
}
Use volumeAvailableCapacityForImportantUsageKey when checking if there's room for user-initiated downloads or saves—things the user explicitly requested. Use volumeAvailableCapacityForOpportunisticUsageKey for background caching or prefetching where you want to be more conservative about space usage.
Formatting for Display
When showing disk space to users, format the bytes into human-readable units:
extension Int64 {
var formattedBytes: String {
let formatter = ByteCountFormatter()
formatter.countStyle = .file
return formatter.string(fromByteCount: self)
}
}
// Usage
if let usage = getDetailedDiskUsage() {
print("Total: \(usage.total.formattedBytes)")
print("Available: \(usage.availableForImportant.formattedBytes)")
print("Used: \(String(format: "%.1f%%", usage.percentUsed))")
}
ByteCountFormatter automatically picks appropriate units (KB, MB, GB) and localizes the output for the user's locale.
Practical Applications
Check available space before starting large downloads:
func canDownloadFile(ofSize bytes: Int64) -> Bool {
guard let usage = getDetailedDiskUsage() else { return false }
// Leave some buffer space
let requiredSpace = bytes + (50 * 1024 * 1024) // 50MB buffer
return usage.availableForImportant >= requiredSpace
}
Monitor disk usage and clean caches when space is low:
func cleanupIfNeeded() {
guard let usage = getDetailedDiskUsage() else { return }
let fiveGB: Int64 = 5 * 1024 * 1024 * 1024
if usage.availableForOpportunistic < fiveGB {
clearImageCache()
clearTemporaryFiles()
}
}
The resource values API is synchronous and fast enough for occasional checks, but avoid calling it in tight loops or on the main thread during performance-critical operations.
// Continue_Learning
Scheduling Alarms with AlarmKit
Learn how to use AlarmKit to schedule alarms that appear in the system Clock app, giving your app native alarm integration on iOS 26.
Reading Your App's Age Rating with StoreKit's ageRatingCode
Learn how to use AppStore.ageRatingCode to read your app's current age rating and react to rating changes for parental consent compliance.
Finding the Top View Controller in Swift
How to traverse the view controller hierarchy to find the currently visible view controller.
// Stay Updated
Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.