swiftmacosusbstoragefile-manager

How to get Total/Available/Used space of a USB drive in MacOS - Swift?


How do you determine the Total, Available, and Used space of a USB drive in Swift (MacOS)?

There are several good posts about this (example: How to get the Total Disk Space and Free Disk space using AttributesOfFileSystemForpaths in swift 2.0 and https://developer.apple.com/documentation/foundation/urlresourcekey/checking_volume_storage_capacity), but they all just get the space of the operating system drive and not of a USB drive.

For example, I have a 64GB USB drive with volume name "myusb", so the MacOS mounts the drive at /Volumes/myusb. Finder shows the total space of the USB drive as 62.91GB, available as 62.29GB, and used as 625,999,872 bytes.

The trouble seems to be that when I give the path of the USB drive, since it obviously is part of the main / path, it is returning the information for / which is my OS drive, not the USB drive.

Here is what I am doing when trying to determine free space of the USB drive and which returns a value of 292298430687 bytes (which is the available space of my OS drive, not USB drive):

/**
   Returns URL of root of USB drive - i.e. /Volumes/myusb
   Uses bundleURL as .app file being executed is located on the USB drive
*/
static func getRootURL() -> URL {
    let bundlePath = Bundle.main.bundleURL
    let bundlePathComponents = bundlePath.pathComponents
        
    let destinationRootPathURL = URL(fileURLWithPath: bundlePathComponents[0])
        .appendingPathComponent(bundlePathComponents[1])
        .appendingPathComponent(bundlePathComponents[2])
        
    return destinationRootPathURL
}

/**
   returns free space of USB drive
*/
func getAvailableSpaceInBytes() -> Int64 {
    if #available(OSX 10.13, *) {
        if let freeSpace = try? getRootURL().resourceValues(forKeys: [URLResourceKey.volumeAvailableCapacityForImportantUsageKey])
                .volumeAvailableCapacityForImportantUsage {
                return freeSpace
        }
    } else {
        // Fallback on earlier versions
        guard let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: getRootURL().path),
              let freeSize = systemAttributes[FileAttributeKey.systemFreeSize] as? NSNumber
        else {
            // something failed so return nil
            return 0
        }
            
        return freeSize.int64Value
    }
        
        return 0
    }

Solution

  • You can use FileManager's mountedVolumeURLs method to get all mounted volumes and get the volumeAvailableCapacityForImportantUsage resource key/value from it:


    extension FileManager {
        static var mountedVolumes: [URL] {
            (FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: nil) ?? []).filter({$0.path.hasPrefix("/Volumes/")})
        }
    }
    

    extension URL {
        var volumeTotalCapacity: Int? {
            (try? resourceValues(forKeys: [.volumeTotalCapacityKey]))?.volumeTotalCapacity
        }
        var volumeAvailableCapacityForImportantUsage: Int64? {
            (try? resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey]))?.volumeAvailableCapacityForImportantUsage
        }
        var name: String? {
            (try? resourceValues(forKeys: [.nameKey]))?.name
        }
        
    }
    

    Usage:

    for url in FileManager.mountedVolumes {
        print(url.name ?? "Untitled")
        print("Capacity:", url.volumeTotalCapacity ?? "nil")
        print("Available:", url.volumeAvailableCapacityForImportantUsage ?? "nil")
        print("Used:", (try? url.sizeOnDisk()) ?? "nil") // check the other link below
    }
    

    For a 16GB USB drive with BigSur installer the code above will print

    Install macOS Big Sur
    Capacity: 15180193792
    Available: 2232998976
    Used: 12.93 GB on disk


    To get the used space of a volume "sizeOnDisk" you can check this post