使用 Swift 查询可用的 iOS 磁盘空间

Posted

技术标签:

【中文标题】使用 Swift 查询可用的 iOS 磁盘空间【英文标题】:Query Available iOS Disk Space with Swift 【发布时间】:2014-11-29 15:23:00 【问题描述】:

我正在尝试使用 Swift 获取可用的 ios 设备存储空间。我找到了这个函数here

        func deviceRemainingFreeSpaceInBytes() -> NSNumber 
          let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
          let systemAttributes = NSFileManager.defaultManager().attributesOfFileSystemForPath(documentDirectoryPath.last as String, error: nil)
          return systemAttributes[NSFileSystemFreeSize] as NSNumber
        

但是在编译时会给出这个错误:[NSObject : AnyObject]? does not have a member named 'subscript' 我相信这个错误是由提到的here 引起的,即attributesOfFileSystemForPath 返回一个可选字典(documentation)。我从一般意义上理解这个问题,但由于建议的解决方案涉及嵌套案例,我不太明白如何修复我感兴趣的功能(这对我很陌生 @987654328 并没有帮助@)。有人可以建议如何使该功能起作用吗?注意:我不确定原始功能是否经过作者测试,或者它是否在 xcode 6 beta 下工作,但据我所知,它在 GM 下不起作用。

【问题讨论】:

【参考方案1】:

嗯,根据上面的代码:

let usedSpace = totalDiskSpaceInBytes - freeDiskSpaceInBytes

您可能会发现 usedSpace 不等于 iPhone 设置页面的值。这是因为在 iOS11 中,Apple 引入了“重要”资源的总可用容量(以字节为单位)

“重要”资源的总可用容量(以字节为单位),包括 预计将通过清除非必要和缓存来清除的空间 资源。 “重要”是指用户或应用程序 显然希望出现在本地系统上,但最终 可更换。这将包括用户明确拥有的项目 通过 UI 请求,以及应用程序需要的资源 为了提供功能。

示例:用户发布的视频 已明确要求观看但尚未看完或 用户请求下载的音频文件。

这个值 不应该用于确定是否有空间 不可替代的资源。在不可替代的资源的情况下,总是 无论可用容量如何,都尝试保存资源,并且 尽可能优雅地处理失败。

为了获得与我们在 iPhone 设置页面中看到的完全相同的值,我们可以通过 volumeAvailableCapacityForImportantUsage

获得空闲空间
if let space = try? URL(fileURLWithPath: NSHomeDirectory() as String).resourceValues(forKeys: [URLResourceKey.volumeAvailableCapacityForImportantUsageKey]).volumeAvailableCapacityForImportantUsage 
    return space ?? 0

您可以使用以下UIDevice extension:

Swift4

extension UIDevice 
    func MBFormatter(_ bytes: Int64) -> String 
        let formatter = ByteCountFormatter()
        formatter.allowedUnits = ByteCountFormatter.Units.useMB
        formatter.countStyle = ByteCountFormatter.CountStyle.decimal
        formatter.includesUnit = false
        return formatter.string(fromByteCount: bytes) as String
    
    
    //MARK: Get String Value
    var totalDiskSpaceInGB:String 
       return ByteCountFormatter.string(fromByteCount: totalDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.decimal)
    
    
    var freeDiskSpaceInGB:String 
        return ByteCountFormatter.string(fromByteCount: freeDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.decimal)
    
    
    var usedDiskSpaceInGB:String 
        return ByteCountFormatter.string(fromByteCount: usedDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.decimal)
    
    
    var totalDiskSpaceInMB:String 
        return MBFormatter(totalDiskSpaceInBytes)
    
    
    var freeDiskSpaceInMB:String 
        return MBFormatter(freeDiskSpaceInBytes)
    
    
    var usedDiskSpaceInMB:String 
        return MBFormatter(usedDiskSpaceInBytes)
    
    
    //MARK: Get raw value
    var totalDiskSpaceInBytes:Int64 
        guard let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String),
            let space = (systemAttributes[FileAttributeKey.systemSize] as? NSNumber)?.int64Value else  return 0 
        return space
    
    
    /*
     Total available capacity in bytes for "Important" resources, including space expected to be cleared by purging non-essential and cached resources. "Important" means something that the user or application clearly expects to be present on the local system, but is ultimately replaceable. This would include items that the user has explicitly requested via the UI, and resources that an application requires in order to provide functionality.
     Examples: A video that the user has explicitly requested to watch but has not yet finished watching or an audio file that the user has requested to download.
     This value should not be used in determining if there is room for an irreplaceable resource. In the case of irreplaceable resources, always attempt to save the resource regardless of available capacity and handle failure as gracefully as possible.
     */
    var freeDiskSpaceInBytes:Int64 
        if #available(iOS 11.0, *) 
            if let space = try? URL(fileURLWithPath: NSHomeDirectory() as String).resourceValues(forKeys: [URLResourceKey.volumeAvailableCapacityForImportantUsageKey]).volumeAvailableCapacityForImportantUsage 
                return space ?? 0
             else 
                return 0
            
         else 
            if let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String),
            let freeSpace = (systemAttributes[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value 
                return freeSpace
             else 
                return 0
            
        
    
    
    var usedDiskSpaceInBytes:Int64 
       return totalDiskSpaceInBytes - freeDiskSpaceInBytes
    


用法:

print("totalDiskSpaceInBytes: \(UIDevice.current.totalDiskSpaceInBytes)")
print("freeDiskSpace: \(UIDevice.current.freeDiskSpaceInBytes)")
print("usedDiskSpace: \(UIDevice.current.usedDiskSpaceInBytes)")


    

【讨论】:

这应该是2018年的最佳答案。【参考方案2】:

iOS 11 更新

下面给出的答案在 iOS 11 下不再提供准确的结果。新的音量容量键可以传递给URL.resourceValues(forKeys:),它们提供的值与设备设置中可用的值相匹配。

static let volumeAvailableCapacityKey: URLResourceKey 卷可用容量的密钥(以字节为单位)。

static let volumeAvailableCapacityForImportantUsageKey: URLResourceKey 用于存储重要资源的卷可用容量(以字节为单位)的密钥(只读)。

static let volumeAvailableCapacityForOpportunisticUsageKey: URLResourceKey 用于存储非必要资源的卷可用容量的密钥(以字节为单位)(只读)。

static let volumeTotalCapacityKey: URLResourceKey 卷总容量的密钥(以字节为单位)(只读)。

来自Apple's documentation:

概述

在您尝试在本地存储大量数据之前,请先确认您有足够的存储容量。要获取卷的存储容量,请构造一个 URL(使用 URL 的实例),该 URL 引用要查询的卷上的对象,然后查询该卷。

决定使用哪种查询类型

要使用的查询类型取决于存储的内容。如果您根据用户请求或应用程序正常运行所需的资源(例如,用户将要观看的视频或游戏中下一关所需的资源)存储数据,请查询 volumeAvailableCapacityForImportantUsageKey .但是,如果您以更具预测性的方式下载数据(例如,下载用户最近观看的电视剧的新剧集),请查询 volumeAvailableCapacityForOpportunisticUsageKey

构造查询

使用此示例作为构建您自己的查询的指南:

let fileURL = URL(fileURLWithPath: NSHomeDirectory() as String)
do 
    let values = try fileURL.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey])
    if let capacity = values.volumeAvailableCapacityForImportantUsage 
        print("Available capacity for important usage: \(capacity)")
     else 
        print("Capacity is unavailable")
    
 catch 
    print("Error retrieving capacity: \(error.localizedDescription)")


原答案

可选绑定if let 在这里也可以工作。

我建议该函数返回一个可选的Int64,以便它可以返回 nil 表示失败:

func deviceRemainingFreeSpaceInBytes() -> Int64? 
    let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
    if let systemAttributes = NSFileManager.defaultManager().attributesOfFileSystemForPath(documentDirectoryPath.last as String, error: nil) 
        if let freeSize = systemAttributes[NSFileSystemFreeSize] as? NSNumber 
            return freeSize.longLongValue
        
    
    // something failed
    return nil

Swift 2.1 更新:

func deviceRemainingFreeSpaceInBytes() -> Int64? 
    let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).last!
    guard
        let systemAttributes = try? NSFileManager.defaultManager().attributesOfFileSystemForPath(documentDirectory),
        let freeSize = systemAttributes[NSFileSystemFreeSize] as? NSNumber
    else 
        // something failed
        return nil
    
    return freeSize.longLongValue

Swift 3.0 更新:

func deviceRemainingFreeSpaceInBytes() -> Int64? 
    let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last!
    guard
        let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: documentDirectory),
        let freeSize = systemAttributes[.systemFreeSize] as? NSNumber
    else 
        // something failed
        return nil
    
    return freeSize.int64Value

用法:

if let bytes = deviceRemainingFreeSpaceInBytes() 
    print("free space: \(bytes)")
 else 
    print("failed")

【讨论】:

太棒了,谢谢!我将不得不研究这个以确保我知道它是如何工作的/为什么工作,并且也感谢第二个版本。 为什么这些返回大约。 200mb (200495104) 的可用空间。虽然 iOS 不允许我拍照,而且我无法将 7mb 的视频保存到磁盘? 正如@LucèBrùlè 提到的,当我运行上面的代码时,同样的事情也会发生。显示大约 215089152 字节。但是当我签入显示几个字节的应用程序存储时。有什么方法可以找出准确的,就像在应用程序存储设置中显示的一样?提前致谢 嘿@SubinKKuriakose 我进行了大量研究,但实际上并没有真正准确的方法来获取剩余的可用空间。我想这与 iOS 在电量不足时开始清理东西有关,这实际上总是在变化。 我编辑了这个答案,以便在 iOS 11 上提供更准确的结果。【参考方案3】:

这类似于 Martin 对 Swift 3.1 的回答,但已转换为 UIDevice 的扩展名,以便更轻松地访问它。

extension UIDevice 
    var systemSize: Int64? 
        guard let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String),
            let totalSize = (systemAttributes[.systemSize] as? NSNumber)?.int64Value else 
                return nil
        

        return totalSize
    

    var systemFreeSize: Int64? 
        guard let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String),
            let freeSize = (systemAttributes[.systemFreeSize] as? NSNumber)?.int64Value else 
                return nil
        

        return freeSize
    

获取空闲空间:

UIDevice.current.systemFreeSize

并获得总空间:

UIDevice.current.systemSize

【讨论】:

【参考方案4】:

我编写了一个类来使用 Swift 获取可用/已用内存。 演示地址:https://github.com/thanhcuong1990/swift-disk-status

升级以支持 Swift 3。

import UIKit

class DiskStatus 

    //MARK: Formatter MB only
    class func MBFormatter(_ bytes: Int64) -> String 
        let formatter = ByteCountFormatter()
        formatter.allowedUnits = ByteCountFormatter.Units.useMB
        formatter.countStyle = ByteCountFormatter.CountStyle.decimal
        formatter.includesUnit = false
        return formatter.string(fromByteCount: bytes) as String
    


    //MARK: Get String Value
    class var totalDiskSpace:String 
        get 
            return ByteCountFormatter.string(fromByteCount: totalDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.binary)
        
    

    class var freeDiskSpace:String 
        get 
            return ByteCountFormatter.string(fromByteCount: freeDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.binary)
        
    

    class var usedDiskSpace:String 
        get 
            return ByteCountFormatter.string(fromByteCount: usedDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.binary)
        
    


    //MARK: Get raw value
    class var totalDiskSpaceInBytes:Int64 
        get 
            do 
                let systemAttributes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String)
                let space = (systemAttributes[FileAttributeKey.systemSize] as? NSNumber)?.int64Value
                return space!
             catch 
                return 0
            
        
    

    class var freeDiskSpaceInBytes:Int64 
        get 
            do 
                let systemAttributes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String)
                let freeSpace = (systemAttributes[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value
                return freeSpace!
             catch 
                return 0
            
        
    

    class var usedDiskSpaceInBytes:Int64 
        get 
            let usedSpace = totalDiskSpaceInBytes - freeDiskSpaceInBytes
            return usedSpace
        
    


演示:

【讨论】:

不错!我喜欢显示方面。 这很好并且帮助了我。谢谢! Xcode 7.3:Use of unresolved identifier 'ByteCountFormatter' 不幸的是,iPhone“关于”页面上的值不同

以上是关于使用 Swift 查询可用的 iOS 磁盘空间的主要内容,如果未能解决你的问题,请参考以下文章

Python查询磁盘信息,磁盘剩余可用空间

iOS 是不是会清除文档目录中的磁盘使用量?

如何在 iOS 设备上模拟磁盘空间不足的情况?

使用 Prometheus 获取总磁盘空间和可用磁盘空间

linux中怎么查看硬盘剩余空间

正在保存备份的磁盘没有足够的可用空间