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

问题描述 投票:0回答:4

我正在尝试使用

Swift
获取可用的 iOS 设备存储空间。我找到了这个功能这里

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)。我从一般意义上理解这个问题,但是因为建议的解决方案涉及嵌套案例,所以我不太明白如何修复我感兴趣的函数(这并没有帮助我对
Swift很陌生
)。有人可以建议如何使该功能发挥作用吗?注意:我不确定原始函数是否经过作者测试,或者它是否在 xcode 6 beta 下工作,但据我所知,它在 GM 下不起作用。

ios swift storage
4个回答
75
投票

iOS 11 更新

下面给出的答案在 iOS 11 下不再提供准确的结果。可以将新的音量容量键传递给

URL.resourceValues(forKeys:)
,提供与设备设置中可用的值相匹配的值。

  • static let volumeAvailableCapacityKey: URLResourceKey
    卷的可用容量(以字节为单位)的键(只读)。

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

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

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

来自Apple的文档

概述

在尝试在本地存储大量数据之前,请首先验证您是否有足够的存储容量。要获取卷的存储容量,您可以构造一个 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")
}

62
投票

那么,根据上面的代码:

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扩展

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)")


    


21
投票

我已经编写了一个类来使用 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
        }
    }

}

演示:

get disk space status with Swift


2
投票

这与 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
© www.soinside.com 2019 - 2024. All rights reserved.