How to get iPad / iPhone app size programmatically in Swift
So, I am trying to get the size of my application programmatically. The app is downloading a large number of audio files and I am trying to verify that when the files are deleted, the size of the app shrinks as expected. So far, the only way I know how to do this is by looking at the iPad settings, but the numbers there don't seem to always be correct.
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{
}
}
I am currently just using File-System Attirbute Key for the file system size which I expected to be the size of my current application, but since it returns 249GB its obvious reading my entire MacBook file system, not the simulator document path ...
I looked through other attribute key options and found:
let NSFileSystemSize: NSString!
let NSFileSystemFreeSize: NSString!
let NSFileSystemNodes: NSString!
let NSFileSystemFreeNodes: NSString!
let NSFileSystemNumber: NSString!
However, I have not found a key that determines the size of the actual space my application is occupying. I think maybe there is a way to get all the nodes and then add their sizes, but I'm not really sure how.
Thank you in advance
EDIT: So the package seems to give me the size of my applications, but without any mp3 files that I save in the document path. I'm guessing I could save them in the wrong place or something, here is a simplified version of how I save them in the application.
//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)")
}
}
source to share
According to the documents , "The file systems in OS X and iOS handle persistent storage of data files, applications and their associated files with the operating system itself." Also according to the docs , "An NSBundle object represents a location in the filesystem that groups code and resources that can be used in a program," so your main application bundle is where its files live. And according to this link , every iOS app consists of a package, document directory, library and tmp.
To get the overall size of your application, list through bundle, docs, library and tmp subtypes, for example:
func appSize() {
// Go through the app 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 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 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 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")
}
source to share
You calculate the size of the Library folder. Not the size of your app directory folder.
According to NSSearchPathDirectory :
NSLibraryDirectory
Various user-visible documents, support files, and configuration files (/ Library).
Available in iOS 2.0 and later.
You have to modify your file to save and calculate the code:
Preservation:
//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)")
}
}
Calculation:
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")
}
source to share
Updated and refactored @ lyndsey-scott's answer for Swift 3 . I modified the code to return the megabyte size as Float64 instead of printing it to the console as that was fine for my purposes.
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)
}
source to share
What do you mean by application size?
If you are concerned about the amount of data you download from the web, it is best practice to download the catalog Cache
and be ready to check out when the system is required. Or you can store them in a directory Documents
and pay attention to adding a "no backup" attribute to the downloaded files, or your application is likely to be rejected.
source to share