如何在Swift中以编程方式获取iPad/iPhone应用程序的大小

4

我正在尝试以编程方式获取我的应用程序的大小。该应用程序下载大量音频文件,我正在尝试检查当我删除文件时,应用程序的大小是否按预期减小。到目前为止,我唯一知道如何做到这一点的方法是查看iPad的设置,但那里的数字似乎并不总是正确的。

func getAppSize(){
    let paths : NSArray = NSSearchPathForDirectoriesInDomains(.LibraryDirectory, .UserDomainMask, true)
    var errorP = NSErrorPointer()
    let pathDictionary: NSDictionary = NSFileManager.defaultManager().attributesOfFileSystemForPath(paths.firstObject as String, error: errorP)!
    if(pathDictionary.count != 0){
        var fileSystemSizeInBytes: Double = pathDictionary.objectForKey("NSFileSystemSize") as Double
        var fileSystemSizeInMegaBytes : Double = fileSystemSizeInBytes/1000000
        println("Total Space: \(fileSystemSizeInMegaBytes) MB")
    }else{

    }
}

目前我只使用文件系统属性密钥来获取文件系统大小,本来以为这是我的当前应用程序的大小,但由于它返回了249 GB,显然它正在读取整个MacBook文件系统大小,而不是模拟器的文档路径。

我查看了其他属性密钥选项,发现:

let NSFileSystemSize: NSString!
let NSFileSystemFreeSize: NSString!
let NSFileSystemNodes: NSString!
let NSFileSystemFreeNodes: NSString!
let NSFileSystemNumber: NSString!

然而,我还没有找到一个指定我的应用程序实际占用空间大小的关键字。我在考虑也许有一种方法可以获取所有节点,然后将它们的大小相加,但我不太确定如何做到这一点。
编辑:所以bundle似乎给了我我的应用程序大小,但没有我保存到文档路径中的任何mp3文件。我猜我可能保存它们到了错误的位置或其他什么地方,这里是我如何将它们保存到应用程序的简化版本。
//Make HTTP Request Ect... before here...
if(data.length > 1000){
    let documentPath = NSSearchPathForDirectoriesInDomains(.LibraryDirectory, .UserDomainMask, true)
    let uuid = NSUUID().UUIDString
    let localUrl = uuid + ".mp3"
    let destPath = (documentPath[0] as String) + "/" + localUrl
    data.writeToFile(destPath, atomically: true)
    var error: NSErrorPointer = nil
    data.writeToFile(destPath, options: NSDataWritingOptions.DataWritingAtomic, error: error)
    if(error != nil){
        println("data write error: \(error.memory?.localizedDescription)")
    }
}
5个回答

5

根据文档,“OS X和iOS中的文件系统处理数据文件,应用程序以及与操作系统本身相关的文件的持久存储。” 另外根据文档,“NSBundle对象表示文件系统中的位置,该位置组合了可在程序中使用的代码和资源”,因此您的应用程序的主捆绑包是其文件所在的位置。而根据这个链接,每个iOS应用程序由一个捆绑包、文档目录、库和tmp组成。

要获取应用程序的总大小,请枚举捆绑包、文档、库和tmp子路径,例如:

func appSize() {

    // Go through the app's bundle
    let bundlePath = NSBundle.mainBundle().bundlePath
    let bundleArray:NSArray = NSFileManager.defaultManager().subpathsOfDirectoryAtPath(bundlePath, error: nil)!
    let bundleEnumerator = bundleArray.objectEnumerator()
    var fileSize: UInt64 = 0

    while let fileName:String = bundleEnumerator.nextObject() as? String {
        let fileDictionary:NSDictionary = NSFileManager.defaultManager().attributesOfItemAtPath(bundlePath.stringByAppendingPathComponent(fileName), error: nil)!
        fileSize += fileDictionary.fileSize();
    }

    // Go through the app's document directory
    let documentDirectory:NSArray = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, .UserDomainMask, true)
    let documentDirectoryPath:NSString = documentDirectory[0] as NSString
    let documentDirectoryArray:NSArray = NSFileManager.defaultManager().subpathsOfDirectoryAtPath(documentDirectoryPath, error: nil)!
    let documentDirectoryEnumerator = documentDirectoryArray.objectEnumerator()

    while let file:String = documentDirectoryEnumerator.nextObject() as? String {
        let attributes:NSDictionary = NSFileManager.defaultManager().attributesOfItemAtPath(documentDirectoryPath.stringByAppendingPathComponent(file), error: nil)!
        fileSize += attributes.fileSize();
    }

    // Go through the app's library directory
    let libraryDirectory:NSArray = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.LibraryDirectory, NSSearchPathDomainMask.UserDomainMask, true)
    let libraryDirectoryPath:NSString = libraryDirectory[0] as NSString
    let libraryDirectoryArray:NSArray = NSFileManager.defaultManager().subpathsOfDirectoryAtPath(libraryDirectoryPath, error: nil)!
    let libraryDirectoryEnumerator = libraryDirectoryArray.objectEnumerator()

    while let file:String = libraryDirectoryEnumerator.nextObject() as? String {
        let attributes:NSDictionary = NSFileManager.defaultManager().attributesOfItemAtPath(libraryDirectoryPath.stringByAppendingPathComponent(file), error: nil)!
        fileSize += attributes.fileSize();
    }

    // Go through the app's tmp directory
    let tmpDirectoryPath:NSString = NSTemporaryDirectory()
    let tmpDirectoryArray:NSArray = NSFileManager.defaultManager().subpathsOfDirectoryAtPath(tmpDirectoryPath, error: nil)!
    let tmpDirectoryEnumerator = tmpDirectoryArray.objectEnumerator()

    while let file:String = tmpDirectoryEnumerator.nextObject() as? String {
        let attributes:NSDictionary = NSFileManager.defaultManager().attributesOfItemAtPath(tmpDirectoryPath.stringByAppendingPathComponent(file), error: nil)!
        fileSize += attributes.fileSize();
    }

    var fileSystemSizeInMegaBytes : Double = Double(fileSize)/1000000
    println("Total App Space: \(fileSystemSizeInMegaBytes) MB")
}

1
@Unome 如果文件正在下载,则不会在应用程序包中找到它们。我们还需要搜索这些文件夹以获取完整的大小。 - Lyndsey Scott
1
@Unome 更新了计算您的 bundle 和 .LibraryDirectory。 - Lyndsey Scott
1
@Unome 你知道吗...也许有其他预设的目录文件包含着各种应用数据...我会去研究一下的... - Lyndsey Scott
1
@Unome 刚刚看到 Midhun 的回答,他是对的——你应该将文件添加到文档目录中...虽然这并不能解释那 6 MB 的问题...我还在继续研究... - Lyndsey Scott
1
@Unome,这个链接提供了有关应用目录的一些信息:http://developer.xamarin.com/guides/ios/application_fundamentals/working_with_the_file_system/。似乎应用程序由bundle + documents + library + tmp组成,因此我们可以添加library和tmp来获取总大小。 - Lyndsey Scott
显示剩余6条评论

3

更新并重构了@lyndsey-scott的答案,以适应Swift 3。我修改了代码,将大小以Float64的形式返回而不是打印到控制台,因为这对我的目的更合适。

private class func appSizeInMegaBytes() -> Float64 { // approximate value

    // create list of directories
    var paths = [Bundle.main.bundlePath] // main bundle
    let docDirDomain = FileManager.SearchPathDirectory.documentDirectory
    let docDirs = NSSearchPathForDirectoriesInDomains(docDirDomain, .userDomainMask, true)
    if let docDir = docDirs.first {
        paths.append(docDir) // documents directory
    }
    let libDirDomain = FileManager.SearchPathDirectory.libraryDirectory
    let libDirs = NSSearchPathForDirectoriesInDomains(libDirDomain, .userDomainMask, true)
    if let libDir = libDirs.first {
        paths.append(libDir) // library directory
    }
    paths.append(NSTemporaryDirectory() as String) // temp directory

    // combine sizes
    var totalSize: Float64 = 0
    for path in paths {
        if let size = bytesIn(directory: path) {
            totalSize += size
        }
    }
    return totalSize / 1000000 // megabytes
}

private class func bytesIn(directory: String) -> Float64? {
    let fm = FileManager.default
    guard let subdirectories = try? fm.subpathsOfDirectory(atPath: directory) as NSArray else {
        return nil
    }
    let enumerator = subdirectories.objectEnumerator()
    var size: UInt64 = 0
    while let fileName = enumerator.nextObject() as? String {
        do {
            let fileDictionary = try fm.attributesOfItem(atPath: directory.appending("/" + fileName)) as NSDictionary
            size += fileDictionary.fileSize()
        } catch let err {
            print("err getting attributes of file \(fileName): \(err.localizedDescription)")
        }
    }
    return Float64(size)
}

嘿,我一直得到一个比“iPhone存储”设置中稍微不同的应用程序大小。有没有办法获得完全相同的大小? - user10447655
没有我知道的。如果你找到答案,请发布! - Steve U

2
你正在计算“Library”文件夹的大小,而不是你的应用程序目录文件夹的大小。
根据 NSSearchPathDirectory
NSLibraryDirectory: 各种用户可见的文档、支持和配置文件(/Library)。 在iOS 2.0及更高版本中可用。
你应该修改你的文件保存和计算代码:
保存:
//Make HTTP Request Ect... before here...
if(data.length > 1000)
{
    let documentPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
    let uuid = NSUUID().UUIDString
    let localUrl = uuid + ".mp3"
    let destPath = (documentPath[0] as String) + "/" + localUrl
    data.writeToFile(destPath, atomically: true)
    var error: NSErrorPointer = nil
    data.writeToFile(destPath, options: NSDataWritingOptions.DataWritingAtomic, error: error)
    if(error != nil){
        println("data write error: \(error.memory?.localizedDescription)")
    }
}

计算中:
func getAppSize()
{
    let paths : NSArray   = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
    let path : NSString   = paths.firstObject? as NSString
    let files : NSArray   = NSFileManager.defaultManager().subpathsOfDirectoryAtPath(path, error: nil)!
    let dirEnumerator     = files.objectEnumerator()
    var totalSize: UInt64 = 0
    let fileManager       = NSFileManager.defaultManager();
    while let file:String = dirEnumerator.nextObject() as? String
    {
        let attributes:NSDictionary = fileManager.attributesOfItemAtPath(path.stringByAppendingPathComponent(file), error: nil)!
        totalSize += attributes.fileSize();
    }

    var fileSystemSizeInMegaBytes : Double = Double(totalSize)/1000000
    println("Total Space: \(fileSystemSizeInMegaBytes) MB")
}

那么我应该计算哪个文件夹? - Unome

2

在 Lyndsay 的回答基础上,这里提供了 Swift 5 的更新版本。它使用 ByteCountFormatter 来创建一个可读性强的大小表示。

private var appSize: String {
    let bcf = ByteCountFormatter()
    bcf.allowedUnits = [.useMB, .useGB]
    bcf.countStyle = .file
    return bcf.string(fromByteCount: appSizeInBytes())
}

func appSizeInBytes() -> Int64 {
    // create list of directories
    var paths = [Bundle.main.bundlePath] // main bundle
    let docDirs = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    if let docDir = docDirs.first {
        paths.append(docDir) // documents directory
    }
    let libDirs = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)
    if let libDir = libDirs.first {
        paths.append(libDir) // library directory
    }
    paths.append(NSTemporaryDirectory() as String) // temp directory

    // combine sizes
    var totalSize: Int64 = 0
    for path in paths {
        totalSize += byteSize(of: path)
    }
    
    return totalSize
}

func byteSize(of directory: String) -> Int64 {
    var size: Int64 = 0
    let fm = FileManager.default
    if let subdirectories = try? fm.subpathsOfDirectory(atPath: directory) {
        subdirectories.forEach { fileName in
            let fileDictionary = try? fm.attributesOfItem(atPath: directory.appending("/" + fileName)) as NSDictionary
            size += Int64(fileDictionary?.fileSize() ?? 0)
        }
    }
    
    return size
}

享受吧!


0

什么是应用程序大小?
如果您担心从网络下载的数据量,根据最佳实践,您应该在Cache目录中下载,并准备好在系统需要时进行清除。或者您可以将它们保存在Documents目录中,并注意为您下载的文件添加“无备份”属性,否则您的应用程序可能会被拒绝。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接